import 'package:fis_common/logger/logger.dart';
import 'package:fis_measure/interfaces/date_types/rect_region.dart';
import 'package:fis_measure/interfaces/enums/annotation.dart';
import 'package:fis_measure/interfaces/enums/operate.dart';
import 'package:fis_measure/interfaces/process/annotations/annotation.dart';
import 'package:fis_measure/interfaces/process/items/item.dart';
import 'package:fis_measure/interfaces/process/items/item_feature.dart';
import 'package:fis_measure/interfaces/process/items/item_metas.dart';
import 'package:fis_measure/interfaces/process/visuals/visual_area.dart';
import 'package:fis_measure/interfaces/process/visuals/visual.dart';
import 'package:fis_measure/interfaces/process/viewports/viewport.dart';
import 'package:fis_measure/interfaces/process/modes/mode.dart';
import 'package:fis_common/event/event_type.dart';
import 'package:fis_measure/interfaces/process/workspace/application.dart';
import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
import 'package:fis_measure/interfaces/process/workspace/recorder.dart';
import 'package:fis_measure/process/annotations/arrow_annotation.dart';
import 'package:fis_measure/process/annotations/input_annotation.dart';
import 'package:fis_measure/process/annotations/label_annotation.dart';
import 'package:fis_measure/process/items/factory.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/painting.dart';
import 'package:vid/us/vid_us_image.dart';
import 'package:vid/us/vid_us_probe.dart';
import 'dart:math';
import 'recorder.dart';
import 'visual_loader.dart';

class Application implements IApplication {
  // ignore: constant_identifier_names
  static const C_VID_THIRDPART_NAME = "ThirdPart";

  late VidUsProbe _probe;
  VidUsImage? _frame;
  List<IVisual>? _visuals;
  IMeasureItem? _activeMeasureItem;
  IAnnotationItem? _activeAnnotationItem;
  bool _canOperate = false;
  Size _displaySize = Size.zero;
  bool _isAdaptiveCarotid2D = false;
  bool _isSingleFrame = true;
  Size _carotid2DSize = Size.zero;
  MeasureOperateType _currOpType = MeasureOperateType.measure;
  final List<IMeasureItem> _measureItems = [];
  final Set<IAnnotationItem> _annotationItems = {};
  late final _recorder = MeasureRecorder(this);

  Application(VidUsProbe probe) {
    _probe = probe;
    currentModeChanged = FEventHandler<IMode>();
    visualAreaChanged = FEventHandler<IVisualArea>();
    canMeasureChanged = FEventHandler<bool>();
    activeMeasureItemChanged = FEventHandler<IMeasureItem?>();
    activeAnnotationItemChanged = FEventHandler<IAnnotationItem?>();
    updateRenderReady = FEventHandler<void>();
    operateTypeChanged = FEventHandler<MeasureOperateType>();
    visualsLoaded = FEventHandler<void>();
    displaySizeChanged = FEventHandler<Size>();
  }

  @override
  bool get canMeasure => _canOperate;
  @override
  set canMeasure(bool value) {
    if (value != _canOperate) {
      _canOperate = value;
      _doCanMeasureChanged();
    }
  }

  /// 是否扇形探头
  bool get isProbeConvex => probe.type == VidUsProbeType.Convex;

  @override
  VidUsProbe get probe => _probe;

  @override
  String get applicationName => _probe.application.applicationName;

  @override
  String get categoryName => _probe.application.applicationCategoryName;

  @override
  bool get isThirdPart => probe.name == C_VID_THIRDPART_NAME;

  @override
  IMode get currentMode => currentVisualArea.mode;

  @override
  IViewPort get currentViewPort => currentVisualArea.viewport!;

  @override
  IVisual get currentVisual => visuals.firstWhere((e) => e.activeArea != null);

  @override
  IVisualArea get currentVisualArea => currentVisual.activeArea!;

  @override
  VidUsImage? get frameData => _frame;

  @override
  List<IVisual> get visuals => _visuals!;

  @override
  List<IMeasureItem> get measureItems => _measureItems;

  @override
  Set<IAnnotationItem> get annotationItems => _annotationItems;

  @override
  MeasureOperateType get currentOperateType => _currOpType;

  @override
  Size get displaySize => _displaySize;
  @override
  set displaySize(Size value) {
    if (value != _displaySize) {
      _displaySize = value;
      displaySizeChanged.emit(this, value);
    }
  }

  @override
  bool get isSingleFrame => _isSingleFrame;
  @override
  set isSingleFrame(bool value) {
    if (value != _isSingleFrame) {
      _isSingleFrame = value;
    }
  }

  @override
  double get displayScaleRatio {
    if (isAdaptiveCarotid2D) {
      final firstScale = min(displaySize.width / frameData!.width,
          displaySize.height / frameData!.height);
      final secondScale = min(frameData!.width / carotid2DSize.width,
          frameData!.height / carotid2DSize.height);
      return firstScale * secondScale;
    }
    if (frameData != null) {
      return min(displaySize.width / frameData!.width,
          displaySize.height / frameData!.height);
    }
    return 1.0;
  }

  @override
  List<IMode> get avaliableModes {
    final modes = <IMode>[];
    for (var visual in visuals) {
      modes.addAll(visual.modes);
    }
    return modes;
  }

  @override
  bool get isAdaptiveCarotid2D => _isAdaptiveCarotid2D;
  @override
  set isAdaptiveCarotid2D(bool value) {
    if (value != _isAdaptiveCarotid2D) {
      _isAdaptiveCarotid2D = value;
      //[Carotid] ✅如果颈动脉2D图像超出范围需要缩放,利用 layoutRegion 参数改变缩放比
      if (value) {
        final scale = min(frameData!.width / carotid2DSize.width,
            frameData!.height / carotid2DSize.height);
        currentVisualArea.layoutRegion = RectRegion.fill(0, 0, scale, scale);
      } else {
        currentVisualArea.layoutRegion = RectRegion.fill(0, 0, 1, 1);
      }
    }
  }

  @override
  Size get carotid2DSize => _carotid2DSize;
  @override
  set carotid2DSize(Size value) {
    if (value != _carotid2DSize) {
      _carotid2DSize = value;
    }
  }

  @override
  IMeasureItem? get activeMeasureItem => _activeMeasureItem;
  set activeMeasureItem(IMeasureItem? value) {
    if (value != _activeMeasureItem) {
      // 解绑失活测量项事件监听
      _activeMeasureItem?.featureUpdated
          .removeListener(_onActiveMeasureItemFeatureUpdated);
      _activeMeasureItem = value;
      if (_activeMeasureItem != null) {
        _measureItems.add(_activeMeasureItem!);
        // 添加活动测量项事件监听
        _activeMeasureItem!.featureUpdated
            .addListener(_onActiveMeasureItemFeatureUpdated);
      }
      // 通知更新事件
      activeMeasureItemChanged.emit(this, value);
      _updateRender();
    }
  }

  @override
  IAnnotationItem? get activeAnnotationItem => _activeAnnotationItem;
  set activeAnnotationItem(IAnnotationItem? value) {
    if (value != _activeAnnotationItem) {
      // 解绑失活注释项事件监听
      _activeAnnotationItem?.featureUpdated
          .removeListener(_onActiveAnnotationItemFeatureUpdated);
      _activeAnnotationItem = value;
      if (_activeAnnotationItem != null) {
        _annotationItems.add(_activeAnnotationItem!);
        // 添加活动注释项事件监听
        _activeAnnotationItem!.featureUpdated
            .addListener(_onActiveAnnotationItemFeatureUpdated);
      }
      // 通知更新事件
      activeAnnotationItemChanged.emit(this, value);
      _updateRender();
    }
  }

  /// 是否注释模式工作中
  bool get isAnnotationWorking =>
      currentOperateType == MeasureOperateType.annotation;

  @override
  IMeasureRecorder get recorder => _recorder;

  @override
  late final FEventHandler<IMode> currentModeChanged;

  @override
  late final FEventHandler<IVisualArea> visualAreaChanged;

  @override
  late final FEventHandler<bool> canMeasureChanged;

  @override
  late final FEventHandler<IMeasureItem?> activeMeasureItemChanged;

  @override
  late final FEventHandler<IAnnotationItem?> activeAnnotationItemChanged;

  @override
  late final FEventHandler<void> updateRenderReady;
  @override
  late final FEventHandler<MeasureOperateType> operateTypeChanged;

  @override
  late final FEventHandler<void> visualsLoaded;

  @override
  late final FEventHandler<Size> displaySizeChanged;

  @override
  void loadFrame(VidUsImage frame, [bool clearable = true]) {
    bool isSameIndex = _frame?.index == frame.index;
    _frame = frame;
    if (isSameIndex == false) {
      if (clearable) {
        _clearFrameCache();
        if (canMeasure) {
          loadVisuals();
        }
      }
      //如果clearable为false,需要手动调用loadCarotidVisuals加载visuals
    }
  }

  final FEventHandler<Offset> mobileTouchEvent = FEventHandler();

  @override
  PointInfo createPointInfo(Offset offset, PointInfoType type) {
    if (frameData == null) {
      throw NullThrownError();
    }
    final width = displaySize.width;
    final height = displaySize.height;
    final x = offset.dx / width;
    final y = offset.dy / height;
    final percentOffset = Offset(x, y);
    final info = PointInfo.fromOffset(percentOffset, type);
    final matchArea = _attchVisualArea(info);
    if (matchArea != null) {
      if (matchArea != currentVisualArea) {
        bool focusAreaChanged = _handleAreaSwitch(matchArea, info);
        // if (focusAreaChanged) {
        //   // 焦点区域发生变更,不继续执行操作
        //   return info;
        // }
        return info;
      }
    }
    info.hostVisualArea ??= currentVisualArea; // 未切换区域则沿用当前区域

    if (isAnnotationWorking) {
      activeAnnotationItem?.execute(info);
    } else {
      activeMeasureItem?.execute(info);
      if (type == PointInfoType.touchMove) {
        mobileTouchEvent.emit(this, offset); // 传出移动事件
      }
    }
    return info;
  }

  @override
  void switchItem(ItemMeta meta) {
    _updateOperateType(MeasureOperateType.measure);
    _handleBeforeSwitchItem();
    activeMeasureItem = MeasureItemFactory.createItem(meta);
  }

  @override
  void autoStartAgain(ItemMeta meta) {
    if (activeMeasureItem == null) return;
    final item = activeMeasureItem!;
    if (item.feature != null) {
      item.finishOnce();
    }
    activeMeasureItem = MeasureItemFactory.createItem(meta);
  }

  @override
  void switchAnnotation([AnnotationType? type, String? text]) {
    _updateOperateType(MeasureOperateType.annotation);
    final targetType = type ?? AnnotationType.input;
    if (activeAnnotationItem != null &&
        activeAnnotationItem!.type == targetType &&
        activeAnnotationItem!.text == text) {
      return;
    }
    // activeAnnotationItem?.finishLast();

    final cachedItems = annotationItems.toList();
    final cachedItemIdx = cachedItems.indexWhere((e) => e.type == targetType);
    if (cachedItemIdx > -1) {
      activeAnnotationItem = cachedItems[cachedItemIdx];
    } else {
      switch (targetType) {
        case AnnotationType.label:
          activeAnnotationItem = LabelAnnotation();
          break;
        case AnnotationType.input:
          activeAnnotationItem = InputAnnotation();
          break;
        case AnnotationType.arrow:
          activeAnnotationItem = ArrowAnnotation();
          break;
      }
      cachedItems.add(activeAnnotationItem!);
    }
    activeAnnotationItem?.text = text;
    activeAnnotationItemChanged.emit(this, activeAnnotationItem);
  }

  @override
  void switchMode(String name) {
    for (var area in currentVisual.visualAreas) {
      if (area.mode.name == name) {
        _changeAcitveArea(area);
      }
    }
  }

  @override
  void switchVisual(int indicator) {
    if (_visuals == null) return;
    try {
      for (var i = 0; i < _visuals!.length; i++) {
        final v = _visuals![i];
        if (i == indicator) {
          v.visualAreas.first.isActive = true;
        } else {
          v.setUnAcitve();
        }
      }
    } catch (e) {
      logger.e('switch error: $e');
    }
  }

  @override
  void undoRecord() {
    if (_recorder.undoOnce()) {
      _updateRender();
    }
  }

  @override
  void clearRecords() {
    _recorder.clear();
    _updateRender();
  }

  @protected
  List<IVisual> convertVisuals() {
    return VisualsLoader(frameData!.visuals).load();
  }

  void _handleBeforeSwitchItem() {
    if (activeMeasureItem == null) return;
    final item = activeMeasureItem!;
    if (item.feature != null) {
      if (item.finishAfterUnactive) {
        item.finishOnce();
      } else {
        _recorder.undoOnce();
      }
    }
  }

  void _clearFrameCache() {
    _recorder.clear();
    _annotationItems.clear();
    _clearVisuals();
  }

  void _updateOperateType(MeasureOperateType type) {
    if (currentOperateType == MeasureOperateType.annotation) {
      activeAnnotationItem?.finishLast();
    }
    if (currentOperateType != type) {
      _currOpType = type;
      operateTypeChanged.emit(this, type);
    }
  }

  void _updateRender() {
    updateRenderReady.emit(this, null);
  }

  void _doCanMeasureChanged() {
    canMeasureChanged.emit(this, canMeasure);

    _clearFrameCache();
    if (canMeasure) {
      if (frameData != null) {
        loadVisuals();
      }
    }
  }

  void _onActiveMeasureItemFeatureUpdated(
    Object sender,
    IMeasureItemFeature? e,
  ) {
    _updateRender();
  }

  void _onActiveAnnotationItemFeatureUpdated(
    Object sender,
    IAnnotationItemFeature? e,
  ) {
    _updateRender();
  }

  @protected
  void loadVisuals() {
    _clearVisuals();
    _visuals = convertVisuals();
    // 默认第一个区域为活动域
    switchVisual(0);
    visualsLoaded.emit(this, null);
  }

  @override
  void loadCarotidVisuals(VidUsImage carotidVid) {
    _clearVisuals();
    _visuals = VisualsLoader(carotidVid.visuals).load();
    switchVisual(0);
    visualsLoaded.emit(this, null);
  }

  void _clearVisuals() {
    _visuals = [];
  }

  IVisualArea? _attchVisualArea(PointInfo point) {
    if (currentVisualArea.displayRegion.containsPoint(point)) {
      return currentVisualArea;
    }
    for (var visual in visuals) {
      for (var area in visual.visualAreas) {
        if (area.displayRegion.containsPoint(point)) {
          return area;
        }
      }
    }
    return null;
  }

  bool _handleAreaSwitch(IVisualArea area, PointInfo point) {
    if (point.pointType != PointInfoType.mouseDown &&
        point.pointType != PointInfoType.touchDown) {
      return false;
    }

    // 点击切换所在区域焦点
    // 不同幅或者不同模式类型的才切换
    bool needSwitch = false;
    if (area.visual.visualData.indicator !=
        currentVisual.visualData.indicator) {
      needSwitch = true;
    } else {
      if (!currentVisualArea.displayRegion.containsPoint(point)) {
        needSwitch = true;
      }
    }
    if (needSwitch) {
      _changeAcitveArea(area);
      return true;
    }

    return false;
  }

  void _changeAcitveArea(IVisualArea area) {
    for (var visual in visuals) {
      visual.setUnAcitve();
    }
    area.isActive = true;
    visualAreaChanged.emit(this, area);
  }
}