/// Created by @Haoyi on 2021/7/7
///

part of "design.dart";

class _DeviceMetrics {
  final double wScale;
  final double hScale;
  final Size size;
  final TextDirection? direction;
  final Offset offset;
  final SystemAdaptation? adaptation;

  const _DeviceMetrics(this.wScale, this.hScale, this.size, this.direction,
      {this.offset = Offset.zero, this.adaptation});
}

enum DesignFit { fitWidth, fitHeight, fill }

enum _DesignMethod {
  width,
  height,
  average,
  fontSize,
  absoluteFontSize,
  widthScale,
  heightScale,
  combine,
  origin,
  statusBarHeight,
  wrappedInSafeAreaStatusBarHeight,
  navigationBarHeight,
  wrappedInSafeAreaNavigationBarHeight
}

class SystemAdaptation {
  final double? pixelRatio;

  final double? textScaleFactor;

  const SystemAdaptation({this.pixelRatio, this.textScaleFactor});

  static SystemAdaptation? defaultSystemAdaptation;

  static double? get globalPixelRation => defaultSystemAdaptation?.pixelRatio;

  static double? get globalTextScaleFactor => defaultSystemAdaptation?.textScaleFactor;
}

class DesignMetrics {
  Size designResolution = Size.zero;
  final List<DesignObject> objects = [];
  final SystemAdaptation? adaptation;
  late Size measuredSize;

  static Map<num, DesignField> originFields = {
    0: DesignField._origin(0),
    1: DesignField._origin(1),
    2: DesignField._origin(2),
    3: DesignField._origin(3),
    4: DesignField._origin(4),
    5: DesignField._origin(5),
    6: DesignField._origin(6),
    7: DesignField._origin(7),
    8: DesignField._origin(8),
    9: DesignField._origin(9),
  };

  DesignMetrics.create(this.designResolution, {this.adaptation});

  DesignMetrics.dynamic({this.adaptation}) : designResolution = Size.zero;

  DesignField origin(num value) {
    final result = originFields[value];
    if (result != null) {
      return result;
    }
    final field = DesignField._origin(value);
    originFields[value] = field;
    return field;
  }

  DesignField designWidth(double size) {
    final field = DesignField._width(size);
    _add(field);
    return field;
  }

  DesignField designHorizontal(double size) {
    final field = DesignField._width(size);
    _add(field);
    return field;
  }

  DesignField designHeight(double size) {
    final field = DesignField._height(size);
    _add(field);
    return field;
  }

  DesignField designVertical(double size) {
    final field = DesignField._height(size);
    _add(field);
    return field;
  }

  DesignField designFontSize(double size) {
    final field = DesignField._average(size);
    _add(field);
    return field;
  }

  DesignField designAverageSize(double size) {
    final field = DesignField._average(size);
    _add(field);
    return field;
  }

  DesignField design(DesignField field) {
    _add(field);
    return field;
  }

  DesignField widthScale(double scale) {
    final field = DesignField._widthScale(scale);
    _add(field);
    return field;
  }

  DesignField heightScale(double scale) {
    final field = DesignField._heightScale(scale);
    _add(field);
    return field;
  }

  DesignField relativeRatio(DesignField relative, double ratio) {
    final field = DesignField._relativeRatio(relative, ratio);
    _add(field);
    return field;
  }

  DesignSize designSize(double width, double height) {
    final size = _DesignExplicitSize(width, height);
    _add(size);
    return size;
  }

  DesignSize designFieldSize(DesignField width, DesignField height) {
    final size = _DesignFieldSize(width, height);
    _add(size);
    return size;
  }

  DesignSize widthRatioSize({required double width, required double ratio}) {
    final size = _DesignWidthRatioSize(width, ratio);
    _add(size);
    return size;
  }

  DesignSize heightRatioSize({required double height, required double ratio}) {
    final size = _DesignHeightRatioSize(height, ratio);
    _add(size);
    return size;
  }

  DesignRect fromLTRB(double left, double top, double right, double bottom) {
    final rect = _DesignExplicitRect.fromLTRB(left, top, right, bottom);
    _add(rect);
    return rect;
  }

  DesignRect fromLTWH(double left, double top, double width, double height) {
    return fromLTRB(left, top, left + width, top + height);
  }

  DesignRect fromCenter(Offset center, double width, double height) {
    return fromLTRB(center.dx - width * 0.5, center.dy - height * 0.5, center.dx + width * 0.5,
        center.dy + height * 0.5);
  }

  DesignRect fromFieldLTRB(
      DesignField left, DesignField top, DesignField right, DesignField bottom) {
    final rect = _DesignFieldRect.fromLTRB(left, top, right, bottom);
    _add(rect);
    return rect;
  }

  DesignRect fromFieldLTWH(
      DesignField left, DesignField top, DesignField width, DesignField height) {
    return fromFieldLTRB(left, top, left + width, top + height);
  }

  DesignRect fromFieldCenter(
      DesignField centerX, DesignField centerY, DesignField width, DesignField height) {
    return fromFieldLTRB(
        centerX - DesignField._relativeRatio(width, 0.5),
        centerY - DesignField._relativeRatio(height, 0.5),
        centerX + DesignField._relativeRatio(width, 0.5),
        centerY + DesignField._relativeRatio(height, 0.5));
  }

  DesignOffset fieldOffset(DesignField dx, DesignField dy) {
    final offset = DesignOffset(dx, dy);
    _add(offset);
    return offset;
  }

  DesignOffset offset(double dx, double dy) {
    return fieldOffset(designHorizontal(dx), designVertical(dy));
  }

  DesignField statusBarHeight({double defaultHeight = 0, bool wrappedInSafeArea = false}) {
    final field = wrappedInSafeArea
        ? DesignField.wrappedInSafeAreaStatusBarHeight(defaultHeight)
        : DesignField.statusBarHeight(defaultHeight: defaultHeight);
    _add(field);
    return field;
  }

  DesignField fakeStatusBarHeight({double fakeHeight = 0}) {
    final field = DesignField.wrappedInSafeAreaStatusBarHeight(fakeHeight);
    _add(field);
    return field;
  }

  DesignField navigationBarHeight({double defaultHeight = 0, bool wrappedInSafeArea = false}) {
    final field = wrappedInSafeArea
        ? DesignField.wrappedInSafeAreaNavigationBarHeight(defaultHeight)
        : DesignField.navigationBarHeight(defaultHeight: defaultHeight);
    _add(field);
    return field;
  }

  DesignField combineAdd(DesignField field1, DesignField field2) {
    final field = field1 + field2;
    _add(field);
    return field;
  }

  void _add(DesignObject object) {
    objects.add(object);
  }

  MeasuredMetrics measure(Size size) {
    measuredSize = size;
    final deviceMetrics = _DeviceMetrics(
        size.width / designResolution.width, size.height / designResolution.height, size, null,
        adaptation: adaptation);
    for (var field in objects) {
      field.measure(deviceMetrics);
    }
    // print("size:$size designResolution:$designResolution ${deviceMetrics.wScale} ${deviceMetrics.hScale}");
    return MeasuredMetrics(deviceMetrics, designResolution);
  }

  MeasuredMetrics adaptiveMeasure(Size screenSize,
      {DesignFit fit = DesignFit.fill, double scale = 1.0, double? limitSize, Size? designSize}) {
    Size size;
    designResolution = designSize ?? designResolution;
    switch (fit) {
      case DesignFit.fitWidth:
        final newWidth = min(screenSize.width * scale, limitSize ?? screenSize.width);
        final newHeight = newWidth * (designResolution.height / designResolution.width);
        size = Size(newWidth, newHeight);
        break;
      case DesignFit.fitHeight:
        final newHeight = screenSize.height * scale;
        final newWidth = newHeight * (designResolution.width / designResolution.height);
        size = Size(newWidth, newHeight);
        break;
      default:
        size = screenSize;
        break;
    }
    return measure(size);
  }
}

class MeasuredMetrics {
  final _DeviceMetrics metrics;

  final Size designResolution;

  Size get size => metrics.size;

  double get measuredWidth => size.width;

  double get measuredHeight => size.height;

  // 宽高比
  double get aspectRatio => size.width / size.height;

  double get designAspectRatio => designResolution.width / designResolution.height;

  double get deviceInverseAspectRatio => 1 / aspectRatio;

  double get designInverseAspectRatio => 1 / designAspectRatio;

  double get layoutHeightScaleFactor => designInverseAspectRatio / deviceInverseAspectRatio;

  double get layoutWidthScaleFactor => deviceInverseAspectRatio / designInverseAspectRatio;

  MeasuredMetrics(this.metrics, this.designResolution);

  Size measureSize(double width, double height) {
    return Size(metrics.wScale * width, metrics.hScale * height);
  }

  Offset measureOffset(double x, double y) {
    return Offset(metrics.wScale * x, metrics.hScale * y);
  }

  double measureWidth(double width, {bool consistent = false}) {
    // 宽度暂不支持这个参数
    return metrics.wScale * width;
  }

  double measureHeight(double height, {bool consistent = false}) {
    return metrics.hScale * height * (consistent ? layoutHeightScaleFactor : 1.0);
  }

  double measureVertical(double vertical, {bool consistent = false}) {
    return metrics.hScale * vertical * (consistent ? layoutHeightScaleFactor : 1.0);
  }

  double measureHorizontal(double horizontal, {bool consistent = false}) {
    // 宽度暂不支持这个参数
    return metrics.wScale * horizontal;
  }

  double measureFontSize(double fontSize) {
    return DesignField._calculate(_DesignMethod.fontSize, metrics, fontSize);
  }

  double measureAbsoluteFontSize(double fontSize, {bool consistent = false}) {
    if (consistent) {
      return fontSize *
          metrics.hScale /
          (metrics.adaptation?.textScaleFactor ?? Get.textScaleFactor);
    }
    return DesignField._calculate(_DesignMethod.absoluteFontSize, metrics, fontSize);
  }

  double statusBarHeight(double ifAbsent) {
    return DesignField._calculate(_DesignMethod.statusBarHeight, metrics, ifAbsent);
  }

  double wrappedInSafeAreaStatusBarHeight(double ifAbsent) {
    return DesignField._calculate(
        _DesignMethod.wrappedInSafeAreaStatusBarHeight, metrics, ifAbsent);
  }

  double navigationBarHeight(double navigationBarHeight) {
    return DesignField._calculate(_DesignMethod.navigationBarHeight, metrics, navigationBarHeight);
  }

  double relativeWidthRatio(double width, double ratio) {
    return metrics.wScale * width * ratio;
  }

  double relativeHeightRatio(double height, double ratio) {
    return metrics.hScale * height * ratio;
  }

  Rect fromLTWH(double left, double top, double width, double height) {
    return Rect.fromLTWH(left * metrics.wScale, top * metrics.hScale, width * metrics.wScale,
        height * metrics.hScale);
  }

  Rect fromLTRB(double left, double top, double right, double bottom) {
    return Rect.fromLTRB(left * metrics.wScale, top * metrics.hScale, right * metrics.wScale,
        bottom * metrics.hScale);
  }
}
