import 'dart:math';
import 'dart:ui';

import 'package:fis_measure/interfaces/date_types/point.dart';
import 'package:fis_measure/interfaces/enums/items.dart';
import 'package:fis_measure/interfaces/process/items/item_metas.dart';
import 'package:fis_measure/interfaces/process/items/item.dart';
import 'package:fis_measure/interfaces/process/items/types.dart';
import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
import 'package:fis_measure/process/calcuators/calculator.dart';
import 'package:fis_measure/process/items/item.dart';
import 'package:fis_measure/process/items/item_feature.dart';
import 'package:fis_measure/utils/canvas.dart';

/// 连续两个线段确定一个角度
class PolylineAngle extends MeasureItem<PolylineAngleFeature> {
  PolylineAngle(ItemMeta meta, IMeasureItem? parent) : super(meta, parent);

  static PolylineAngle createPolyAngle(ItemMeta meta, [IMeasureItem? parent]) {
    PolylineAngle polygon = PolylineAngle(meta, parent);
    polygon.calculator = _AngleCalc(polygon);
    return polygon;
  }

  @override
  bool onExecuteMouse(PointInfo args) {
    if (state == ItemStates.finished) {
      if (args.pointType == PointInfoType.mouseDown) {
        state = ItemStates.waiting;
      }
    }

    if (state == ItemStates.waiting) {
      if (args.pointType == PointInfoType.mouseDown) {
        handleMouseDownWhileWaiting(args);
      }
    } else if (state == ItemStates.running) {
      if (feature == null) return false;
      if (args.pointType == PointInfoType.mouseUp) return false;

      final f = feature!;

      if (args.pointType == PointInfoType.mouseDown) {
        f.innerPoints.add(args);
        if (f.innerPoints.length == 4) {
          f.innerPoints.removeLast();
          doFeatureFinish();
        }
      } else {
        f.innerPoints.last = args;
      }
      doCalculate();
    }
    return true;
  }

  DPoint lastStartPoint = DPoint(0, 0); // 上一个起点
  bool isFirstPointNeedOffset = false; // 第一个点是否需要施加偏移
  DPoint splineTouchStartPoint = DPoint(0, 0); // 轨迹触摸起始点
  bool isOtherPointNeedOffset = false; // 其他点是否需要施加偏移
  @override
  bool onExecuteTouch(PointInfo args) {
    if (state == ItemStates.finished) {
      if (args.pointType == PointInfoType.touchDown) {
        state = ItemStates.waiting;
      }
    }

    if (state == ItemStates.waiting) {
      if (isFirstPointNeedOffset) args.addOffset(0, -0.2);
      switch (args.pointType) {
        case PointInfoType.touchDown:
          handleTouchDownWhileWaiting(args);
          isFirstPointNeedOffset = false;
          break;
        case PointInfoType.touchUp:
          lastStartPoint = args; // 设置线段起点
          state = ItemStates.running;
          feature?.innerPoints.first = args;
          break; // 按下立即抬起无事发生
        case PointInfoType.touchMove:
          if (isMoveTargetOutOfRange(args)) return true;
          isFirstPointNeedOffset = true;
          feature?.innerPoints.first = args;
          break;
        default:
          break;
      }
    } else if (state == ItemStates.running) {
      if (feature == null) return false;
      DPoint newPoint =
          lastStartPoint.clone().addVector(args - splineTouchStartPoint);
      final f = feature!;
      if (args.pointType == PointInfoType.touchUp) {
        if (!isOtherPointNeedOffset) {
          f.innerPoints.last = args;
          lastStartPoint = args;
          doCalculate();
        } else {
          lastStartPoint = newPoint;
        }
        if (f.innerPoints.length == 3) {
          // f.innerPoints.removeLast();
          doFeatureFinish();
        }
      }

      if (args.pointType == PointInfoType.touchDown) {
        isOtherPointNeedOffset = false;
        splineTouchStartPoint = args;
        f.innerPoints.add(lastStartPoint);
      }
      if (args.pointType == PointInfoType.touchMove) {
        if (isMoveTargetOutOfRange(args)) return true;
        isOtherPointNeedOffset = true;
        f.innerPoints.last = newPoint;
      }
      doCalculate();
    }
    return true;
  }

  void handleMouseDownWhileWaiting(PointInfo args) {
    // TODO: 判断是否当前area
    // 转换为Area逻辑位置
    final point = args.toAreaLogicPoint();
    feature = PolylineAngleFeature(this, point);
    if (args.hostVisualArea != null) {
      feature!.hostVisualArea = args.hostVisualArea;
    }
    state = ItemStates.running;
  }

  void handleTouchDownWhileWaiting(PointInfo args) {
    // TODO: 判断是否当前area
    final point = args.toAreaLogicPoint();
    feature = PolylineAngleFeature.withOneStartPoint(this, point);
    if (args.hostVisualArea != null) {
      feature!.hostVisualArea = args.hostVisualArea;
    }
  }
}

class PolylineAngleFeature extends MeasureItemFeature {
  PolylineAngleFeature(IMeasureItem refItem, DPoint point) : super(refItem) {
    innerPoints.add(point.clone());
    innerPoints.add(point.clone());
  }
  PolylineAngleFeature.withOneStartPoint(IMeasureItem refItem, DPoint point)
      : super(refItem) {
    innerPoints.add(point.clone());
  }

  @override
  void paint(Canvas canvas, Size size) {
    if (innerPoints.isEmpty) return;

    drawId(canvas, size);

    final innerOffsets =
        innerPoints.map((e) => convert2ViewPoint(size, e).toOffset()).toList();
    for (var e in innerOffsets) {
      drawVertex(canvas, e);
    }

    final len = innerOffsets.length;
    for (var i = 1; i < len; i++) {
      final a = innerOffsets[i - 1];
      final b = innerOffsets[i];
      canvas.drawDashLine(a, b, 1, 10, paintLinePan);
    }
  }
}

class _AngleCalc extends Calculator<PolylineAngle, double> {
  _AngleCalc(PolylineAngle ref) : super(ref);

  @override
  void calculate() {
    if (ref.feature == null) return;

    final feature = ref.feature!;
    final viewport = feature.hostVisualArea!.viewport!;
    final points = feature.innerPoints.map((e) => viewport.convert(e)).toList();

    feature.values.clear();

    double angle;
    if (points.length != 3) {
      angle = 0;
    } else {
      angle = calcAngle(points[1], points[0], points[2]);
    }
    for (var output in ref.meta.outputs) {
      if (output.name == MeasureTypes.Angle) {
        var value = roundDouble(angle, output.fractionalDigits);
        feature.updateFloatValue(output, value, output.unit);
      }
    }
  }

  /// 三个点计算角度,pointsA 为顶点
  static double calcAngle(DPoint pointsA, DPoint pointsB, DPoint pointsC) {
    double angle = 0;
    double a =
        sqrt(pow(pointsB.x - pointsC.x, 2) + pow(pointsB.y - pointsC.y, 2));
    double b =
        sqrt(pow(pointsA.x - pointsC.x, 2) + pow(pointsA.y - pointsC.y, 2));
    double c =
        sqrt(pow(pointsA.x - pointsB.x, 2) + pow(pointsA.y - pointsB.y, 2));
    if (a + b > c && a + c > b && b + c > a) {
      double cosA = (pow(b, 2) + pow(c, 2) - pow(a, 2)) / (2 * b * c);
      angle = acos(cosA) * 180 / pi;
    }
    return angle;
  }
}