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.dart';
import 'package:fis_measure/interfaces/process/items/item_metas.dart';
import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
import 'package:fis_measure/process/calcuators/trace.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';
import 'package:fis_measure/view/gesture/positioned_touch_cursor.dart';
import 'package:get/get.dart';

/// 手势轨迹图形
class MultiTrace extends TraceItemAbstract {
  MultiTrace(ItemMeta meta, IMeasureItem? parent) : super(meta, parent);
  late final touchState = Get.find<ITouchPointState>();

  @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 (args.pointType == PointInfoType.mouseUp) return false;

      feature?.adopt(args);
      doCalculate();
      if (args.pointType == PointInfoType.mouseDown) {
        doFeatureFinish();
      }
    }
    return true;
  }

  @override
  void doFeatureFinish() {
    super.doFeatureFinish();
  }

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

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

  PointInfo? startPoint;
  DPoint touchStartPosition = DPoint(0, 0); // 相对位移起始触摸点
  bool isFirstPointMove = false;
  @override
  bool onExecuteTouch(PointInfo args) {
    if (state == ItemStates.finished) {
      if (args.pointType == PointInfoType.touchDown) {
        state = ItemStates.waiting;
      }
    }

    if (state == ItemStates.waiting) {
      if (isFirstPointMove) {
        args.addOffset(0, -0.2);
      }
      switch (args.pointType) {
        case PointInfoType.touchDown:
          isFirstPointMove = false;
          startPoint = args; // 设置线段起点
          handleTouchDownWhileWaiting(startPoint!); // 通过设置的起点开始一个绘制事件
          break;
        case PointInfoType.touchUp:
          startPoint = args; // 设置线段起点
          state = ItemStates.running;
          touchState.touchOffset = Offset.zero;
          break; // 按下立即抬起无事发生
        case PointInfoType.touchMove:
          if (isMoveTargetOutOfRange(args)) return true;
          isFirstPointMove = true;
          final pixelSize = application.displaySize;
          touchState.touchOffset =
              DPoint(0, -0.2).scale2Size(pixelSize).toOffset();
          feature?.innerPoints.first = args;
          break;
        default:
          break;
      }
    } else if (state == ItemStates.running) {
      if (args.pointType == PointInfoType.touchDown) {
        touchStartPosition = args;
        final pixelSize = application.displaySize;
        touchState.touchOffset = startPoint!.scale2Size(pixelSize).toOffset() -
            args.scale2Size(pixelSize).toOffset();
      }
      if (args.pointType == PointInfoType.touchUp) {
        touchState.touchOffset = Offset.zero;
        doFeatureFinish();
      }
      if (args.pointType == PointInfoType.touchMove) {
        PointInfo newPoint = PointInfo.fromOffset(
            startPoint!.clone().addVector(args - touchStartPosition).toOffset(),
            startPoint!.pointType);
        if (isMoveTargetOutOfRange(newPoint)) return true;
        feature?.adopt(newPoint);
        doCalculate();
      }
    }
    return true;
  }

  static MultiTrace createTrace(
    ItemMeta meta, [
    IMeasureItem? parent,
  ]) {
    MultiTrace trace = MultiTrace(meta, parent);
    trace.calculator = TraceCal(trace);
    return trace;
  }
}

class MultiTraceFeature extends TraceItemFeatureAbstract {
  MultiTraceFeature(TraceItemAbstract refItem) : super(refItem);
  final greenPen = Paint()
    ..color = const Color.fromARGB(255, 0, 255, 0)
    ..isAntiAlias = true
    ..strokeWidth = 1
    ..style = PaintingStyle.stroke;
  final dashLinePan = Paint()
    ..color = const Color.fromARGB(255, 255, 255, 0)
    ..isAntiAlias = false
    ..strokeWidth = 1
    ..style = PaintingStyle.stroke;
  DPoint furthestPoint = DPoint(0, 0);

  @override
  void paint(Canvas canvas, Size size) {
    final double areaTop = hostVisualArea!.displayRegion.top * size.height;
    final double areaBottom =
        hostVisualArea!.displayRegion.bottom * size.height;

    if (innerPoints.isEmpty) return;
    if (innerPoints.length == 1) {
      drawVertex(canvas, convert2ViewPoint(size, innerPoints[0]).toOffset());
      drawId(canvas, size);
      return;
    }
    double maxDistance = 0;
    drawId(canvas, size);

    final points = innerPoints.map((e) => convert2ViewPoint(size, e)).toList();
    final startOffset = convert2ViewPoint(size, startPoint);
    final endOffset = convert2ViewPoint(size, endPoint);
    canvas.drawDashLine(Offset(startOffset.x, areaTop),
        Offset(startOffset.x, areaBottom), 3, 3, dashLinePan);
    canvas.drawDashLine(Offset(endOffset.x, areaTop),
        Offset(endOffset.x, areaBottom), 3, 3, dashLinePan);

    if (points.length > 1) {
      final Path path = Path();
      path.moveTo(startOffset.x, startOffset.y);
      for (var i = 1; i < points.length; i++) {
        final point = points[i];
        path.lineTo(point.x, point.y);
        final distance = (point.y - startOffset.y).abs();
        if (distance > maxDistance) {
          maxDistance = distance;
          furthestPoint = point;
        }
      }
      drawCrossVertex(canvas, furthestPoint.toOffset());
      canvas.drawPath(
        path,
        greenPen,
      );
    }
  }
}

abstract class TraceItemFeatureAbstract extends MeasureItemFeature {
  TraceItemFeatureAbstract(TraceItemAbstract refItem) : super(refItem);

  @override
  TraceItemAbstract get refItem => super.refItem as TraceItemAbstract;

  DPoint get startPoint => innerPoints.first;
  DPoint get endPoint => innerPoints.last;

  bool ifRightSide = true;

  /// 接收新坐标
  void adopt(DPoint point) {
    if (innerPoints.isEmpty) {
      innerPoints.add(point);
    }
    if (point.x > startPoint.x) {
      handleChangeSide(point);
      if (point.x < innerPoints.last.x) {
        clearRight(point.x);
      } else if (point.x > innerPoints.last.x) {
        innerPoints.add(point);
      }
    } else {
      handleChangeSide(point);
      if (point.x > innerPoints.last.x) {
        clearLeft(point.x);
      } else if (point.x < innerPoints.last.x) {
        innerPoints.add(point);
      }
    }
  }

  void clearRight(double X) {
    if (innerPoints.isEmpty) return;
    for (var i = innerPoints.length - 1; i >= 0; i--) {
      if (innerPoints[i].x >= X) {
        innerPoints.removeAt(i);
      }
    }
  }

  void clearLeft(double X) {
    if (innerPoints.isEmpty) return;
    for (var i = innerPoints.length - 1; i >= 0; i--) {
      if (innerPoints[i].x <= X) {
        innerPoints.removeAt(i);
      }
    }
  }

  void handleChangeSide(point) {
    if (ifRightSide) {
      if (point.x < startPoint.x) {
        ifRightSide = false;
        innerPoints.clear();
        innerPoints.add(point);
      }
    } else {
      if (point.x > startPoint.x) {
        ifRightSide = true;
        innerPoints.clear();
        innerPoints.add(point);
      }
    }
  }
}

abstract class TraceItemAbstract extends MeasureItem<TraceItemFeatureAbstract> {
  TraceItemAbstract(ItemMeta meta, IMeasureItem? parent) : super(meta, parent);
}