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

part of "design.dart";

abstract class CombinedValue {
  double combine(_DeviceMetrics metrics);
}

class SimpleCombinedValue extends CombinedValue {
  final DesignField field1;
  final DesignField field2;
  final double Function(double a, double b) combiner;

  SimpleCombinedValue(this.field1, this.field2, this.combiner);

  @override
  double combine(_DeviceMetrics metrics) {
    final a = field1.measure(metrics);
    final b = field2.measure(metrics);
    return combiner.call(a, b);
  }
}

class RatioCombinedValue extends CombinedValue {
  final DesignField relative;
  final double scale;

  RatioCombinedValue(this.relative, this.scale);

  @override
  double combine(_DeviceMetrics metrics) {
    final value = relative.measure(metrics);
    return value * scale;
  }
}

abstract class DesignObject<T> {
  T measure(_DeviceMetrics metrics);
}

class DesignField extends DesignObject<double> {
  final _DesignMethod designMethod;
  final dynamic designValue;

  double? _size;

  static double _calculate(_DesignMethod method, _DeviceMetrics metrics, dynamic value) {
    switch (method) {
      case _DesignMethod.origin:
        return value;
      case _DesignMethod.width:
        return value * metrics.wScale;
      case _DesignMethod.height:
        return value * metrics.hScale;
      case _DesignMethod.widthScale:
        return value * metrics.size.width;
      case _DesignMethod.heightScale:
        return value * metrics.size.height;
      case _DesignMethod.combine:
        final combinedValue = value as CombinedValue;
        return combinedValue.combine(metrics);
      case _DesignMethod.statusBarHeight:
        final statusBarHeight = Get.statusBarHeight /
            (metrics.adaptation?.pixelRatio ??
                SystemAdaptation.globalPixelRation ??
                Get.pixelRatio);
        if (statusBarHeight != 0) {
          return statusBarHeight;
        }
        return value * 1.0 * metrics.hScale;
      case _DesignMethod.wrappedInSafeAreaStatusBarHeight:
        final statusBarHeight = Get.statusBarHeight /
            (metrics.adaptation?.pixelRatio ??
                SystemAdaptation.globalPixelRation ??
                Get.pixelRatio);
        if (statusBarHeight != 0) {
          return 0;
        }
        return value * 1.0 * metrics.hScale;
      case _DesignMethod.navigationBarHeight:
        final navigationBarHeight = Get.bottomBarHeight /
            (metrics.adaptation?.pixelRatio ??
                SystemAdaptation.globalPixelRation ??
                Get.pixelRatio);
        if (navigationBarHeight != 0) {
          return navigationBarHeight;
        }
        return value * 1.0 * metrics.hScale;
      case _DesignMethod.wrappedInSafeAreaNavigationBarHeight:
        final navigationBarHeight = Get.bottomBarHeight /
            (metrics.adaptation?.pixelRatio ??
                SystemAdaptation.globalPixelRation ??
                Get.pixelRatio);
        if (navigationBarHeight != 0) {
          return 0;
        }
        return value * 1.0 * metrics.hScale;
      case _DesignMethod.absoluteFontSize:
        return ((((value * metrics.hScale) + (value * metrics.wScale)) / 2) /
            (metrics.adaptation?.textScaleFactor ??
                SystemAdaptation.globalTextScaleFactor ??
                Get.textScaleFactor));
      //   return ((((value * metrics.hScale) + (value * metrics.wScale)) / 2) * Get.textScaleFactor);
      case _DesignMethod.average:
      default:
        return ((value * metrics.hScale) + (value * metrics.wScale)) / 2;
    }
  }

  @override
  double measure(_DeviceMetrics metrics) {
    _size ??= _calculate(
      designMethod,
      metrics,
      designValue,
    );
    return _size!;
  }

  DesignField._origin(this.designValue)
      : designMethod = _DesignMethod.origin,
        _size = designValue.toDouble();

  DesignField._width(this.designValue) : designMethod = _DesignMethod.width;

  DesignField._height(this.designValue) : designMethod = _DesignMethod.height;

  DesignField._widthScale(this.designValue) : designMethod = _DesignMethod.widthScale;

  DesignField._heightScale(this.designValue) : designMethod = _DesignMethod.heightScale;

  DesignField._average(this.designValue) : designMethod = _DesignMethod.average;

  DesignField._relativeRatio(DesignField relative, double ratio)
      : designValue = RatioCombinedValue(relative, ratio),
        designMethod = _DesignMethod.combine;

  DesignField.statusBarHeight({double defaultHeight = 0})
      : designValue = defaultHeight,
        designMethod = _DesignMethod.statusBarHeight;

  DesignField.wrappedInSafeAreaStatusBarHeight(double fakeHeight)
      : designValue = fakeHeight,
        designMethod = _DesignMethod.wrappedInSafeAreaStatusBarHeight;

  DesignField.navigationBarHeight({double defaultHeight = 0})
      : designValue = defaultHeight,
        designMethod = _DesignMethod.navigationBarHeight;

  DesignField.wrappedInSafeAreaNavigationBarHeight(double fakeHeight)
      : designValue = fakeHeight,
        designMethod = _DesignMethod.wrappedInSafeAreaNavigationBarHeight;

  DesignField._combine(this.designValue) : designMethod = _DesignMethod.combine;

  DesignField operator +(DesignField other) {
    return DesignField._combine(SimpleCombinedValue(this, other, (a, b) => a + b));
  }

  DesignField operator -(DesignField other) {
    return DesignField._combine(SimpleCombinedValue(this, other, (a, b) => a - b));
  }

  DesignField operator *(DesignField other) {
    return DesignField._combine(SimpleCombinedValue(this, other, (a, b) => a * b));
  }

  DesignField operator /(DesignField other) {
    return DesignField._combine(SimpleCombinedValue(this, other, (a, b) => a / b));
  }

  double get size => _size!;
}
