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/terms.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/curve.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/process/primitives/utils/auto_snap.dart';
import 'package:fis_measure/utils/canvas.dart';

import 'area_abstract.dart';

/// 折线多边形
class Polyline extends AreaItemAbstract with AutoSnapMixin {
  Polyline(ItemMeta meta, IMeasureItem? parent) : super(meta, parent);

  static Polyline createAreaPerimeter(ItemMeta meta, [IMeasureItem? parent]) {
    Polyline polygon = Polyline(meta, parent);
    polygon.calculator = AreaPerimeterCal(polygon);
    return polygon;
  }

  static Polyline createCurveLength(ItemMeta meta, [IMeasureItem? parent]) {
    Polyline polygon = Polyline(meta, parent);
    polygon.calculator = CurveLengthCal(polygon);
    polygon.isClosed = false;
    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);
      } else {
        f.innerPoints.last = args;
      }
      doCalculate();

      checkAutoSnap(args);

      // if (f.startPoint.almostEquals(f.endPoint)) {
      //   doFeatureFinish();
      // }
    }
    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 (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();
      PointInfo newPointInfo = isOtherPointNeedOffset
          ? PointInfo.fromOffset(newPoint.toOffset(), args.pointType)
          : args;
      checkAutoSnap(newPointInfo);
    }
    return true;
  }

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

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

class PolylineFeature extends AreaItemFeatureAbstract {
  PolylineFeature(AreaItemAbstract refItem, DPoint point) : super(refItem) {
    innerPoints.add(point.clone());
    innerPoints.add(point.clone());
  }

  PolylineFeature.withOneStartPoint(AreaItemAbstract refItem, DPoint point)
      : super(refItem) {
    innerPoints.add(point.clone());
  }

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

    drawId(canvas, size);

    final startOffset = convert2ViewPoint(size, startPoint).toOffset();

    if (innerPoints.length == 1) {
      drawVertex(canvas, startOffset, true);
      return;
    } else {
      drawVertex(canvas, startOffset);
    }

    final len = innerPoints.length;
    for (var i = 1; i < len; i++) {
      final a = innerPoints[i - 1];
      final b = innerPoints[i];
      final offsetA = convert2ViewPoint(size, a).toOffset();
      final offsetB = convert2ViewPoint(size, b).toOffset();
      canvas.drawDashLine(offsetA, offsetB, 1, 10, paintPan);
      final isLast = len - i == 1;
      if (isLast) {
        drawVertex(canvas, offsetB, isActive);
        if (isClosed) {
          canvas.drawDashLine(offsetB, startOffset, 1, 10, paintLinePan);
        }
      } else {
        drawVertex(canvas, offsetB, false);
      }
    }
  }
}