import 'package:fis_common/event/event_type.dart';
import 'package:fis_measure/interfaces/enums/items.dart';
import 'package:fis_measure/interfaces/process/calculators/calculator.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/workspace/application.dart';
import 'package:fis_measure/interfaces/process/workspace/point_info.dart';
import 'package:flutter/foundation.dart';
import 'package:get/get.dart';

import 'item_feature.dart';
import 'top_item.dart';

abstract class MeasureItem<T extends MeasureItemFeature> extends IMeasureItem {
  late final ItemMeta _meta;
  late final IMeasureItem? _parent;
  ItemStates _state = ItemStates.waiting;
  ICalculator? _calculator;
  T? _feature;
  final List<T> _measuredFeatures = [];
  final application = Get.find<IApplication>();

  MeasureItem(ItemMeta meta, [IMeasureItem? parent]) {
    _parent = parent;
    _meta = meta;
  }

  @override
  String get displayName {
    if (briefAnnotation.isNotEmpty) {
      return briefAnnotation;
    }
    if (description.isNotEmpty) {
      return description;
    }
    return meta.name;
  }

  @override
  String get briefAnnotation => meta.briefAnnotation;

  @override
  String get description => meta.description;

  @override
  T? get feature => _feature;
  @protected
  set feature(T? value) {
    if (value != _feature) {
      _feature = value;
    }
  }

  @override
  ICalculator? get calculator => _calculator;
  @protected
  set calculator(ICalculator? value) {
    if (value != _calculator) {
      _calculator = value;
    }
  }

  @override
  IMeasureItem? get parent => _parent;

  @override
  ItemMeta get meta => _meta;

  @override
  ItemStates get state => _state;
  @protected
  set state(ItemStates value) {
    if (value != _state) {
      _state = value;
    }
  }

  @override
  double get scaleRatio => application.displayScaleRatio;

  @override
  late final featureUpdated = FEventHandler<IMeasureItemFeature?>();

  @override
  bool execute(PointInfo args) {
    bool hasFeatureBefore = feature != null;
    bool result = false;
    if (state == ItemStates.waiting ||
        state == ItemStates.running ||
        state == ItemStates.finished) {
      switch (args.pointType) {
        case PointInfoType.mouseUp:
        case PointInfoType.mouseDown:
        case PointInfoType.mouseMove:
          result = onExecuteMouse(args);
          break;
        case PointInfoType.touchUp:
        case PointInfoType.touchDown:
        case PointInfoType.touchMove:
          result = onExecuteTouch(args);
          break;
      }
    }

    if (result) {
      /// 未创建测量,不通知刷新
      if (feature == null && !hasFeatureBefore) return result;

      /// 通知刷新
      doFeatureUpdate();
    }
    return result;
  }

  @override
  void clear() {
    feature = null;
    if (calculator != null) {
      calculator!.finishOnce();
    }

    measuredFeatures.clear();
  }

  @override
  void finishOnce() {
    doFeatureFinish();
  }

  @override
  void cancelOnce() {
    if (this is TopMeasureItem) {
      final that = this as TopMeasureItem;
      for (var item in that.childItems) {
        item.cancelOnce();
        item.measuredFeatures.clear();
      }
    }

    feature = null;
    state = ItemStates.waiting;
    onCancelingOnce();
    doFeatureUpdate();
  }

  @protected
  void onCancelingOnce() {}

  @protected
  void doFeatureUpdate() {
    featureUpdated.emit(this, feature);
  }

  @protected
  void doFeatureFinish() {
    if (feature != null) {
      feature!.isActive = false;
      measuredFeatures.add(feature!);
      calculator?.finishOnce();
    }
    feature = null;
    state = ItemStates.finished;
  }

  @protected
  void doCalculate() {
    calculator?.calculate();
  }

  @protected
  bool onExecuteMouse(PointInfo args);
  @protected
  bool onExecuteTouch(PointInfo args);

  @override
  List<T> get measuredFeatures => _measuredFeatures;

  @override
  int assignId() {
    return application.recorder.newRecordId();
  }
}