import 'package:fis_measure/configs/patient.dart';
import 'package:fis_measure/interfaces/process/items/item_metas.dart';
import 'package:fis_measure/interfaces/process/items/terms.dart';
import 'package:fis_measure/process/calcuators/trace.dart';
import 'package:fis_measure/process/items/item_feature.dart';
import 'package:fis_measure/process/primitives/combos/flow_area_vti.dart';
import 'package:fis_measure/process/primitives/combos/sv.dart';
import 'package:fis_measure/process/primitives/multi_method/multiple_trace.dart';
import 'package:vid/us/vid_us_unit.dart';

import 'calculator.dart';
import 'formulas/cardiac.dart';
import 'formulas/general.dart';

class FlowVolumeCal extends Calculator<Sv, double> {
  FlowVolumeCal(super.ref);

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

    final feature = ref.feature!;

    final diam = pickChildFloatValue(ref.l);

    if (diam == null) {
      updateStringValue("");
      return;
    }

    final flowArea = GeneralFormulas.area(diam, diam);
    double flowVolTAMAX = double.nan;
    double flowVolTAMEAN = double.nan;

    TraceItemFeatureAbstract? traceFeature = ref.trace.feature;
    if (traceFeature == null) {
      if (ref.trace.measuredFeatures.isNotEmpty) {
        traceFeature = ref.trace.measuredFeatures.first;
      }
    }

    if (traceFeature != null) {
      final countVTIResult = TraceCal.getCountVTI(traceFeature);

      //FlowVol by TAMAX
      double coefficent = parseCoefficent(traceFeature);
      flowVolTAMAX = calculateFlowVol(countVTIResult, flowArea, coefficent);

      //FlowVol by TAMEAN
      coefficent = 1;
      flowVolTAMEAN = calculateFlowVol(countVTIResult, flowArea, coefficent);
    }

    for (var output in ref.meta.outputs) {
      if (output.name == MeasureTerms.FlowVol) {
        if (flowVolTAMEAN.isNaN) {
          updateStringValue("");
        } else {
          updateFloatValue(flowVolTAMEAN, unit: VidUsUnit.cm3s);
        }
      } else if (output.name == MeasureTerms.FlowVolTAMAX) {
        if (!flowVolTAMEAN.isNaN) {
          feature.updateFloatValue(output, flowVolTAMAX, VidUsUnit.cm3s);
        }
      } else if (output.name == MeasureTerms.FlowArea) {
        feature.updateFloatValue(output, flowArea, VidUsUnit.cm2);
      }
    }
  }

  static double parseCoefficent(MeasureItemFeature feature) {
    // TODO: read from meta args
    return 0.75;
  }

  static double calculateFlowVol(
      List<double> values, double flowArea, double coefficient) {
    if (values.isEmpty) {
      return double.nan;
    }
    if (values.length < 2) {
      return double.nan;
    }

    final taMax = values[2];
    double flowVol = GeneralFormulas.flowVolTAMAX(flowArea, taMax, coefficient);
    return flowVol;
  }
}

class FlowAreaByVTICal extends Calculator<FlowAreaByVti, double> {
  FlowAreaByVTICal(super.ref);

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

    final feature = ref.feature!;

    final diam = pickChildFloatValue(ref.l);

    feature.values.clear();
    updateStringValue("");
    if (diam == null) {
      return;
    }

    final diamOutputIndex =
        ref.l.meta.outputs.indexWhere((e) => e.name == MeasureTerms.Diam);
    if (diamOutputIndex > -1) {
      final output = ref.l.meta.outputs[diamOutputIndex];
      feature.updateFloatValue(output, diam, VidUsUnit.cm);
    }

    double va = double.nan;
    double dvi = double.nan;
    double avaIndex = double.nan;
    double otVti = double.nan;
    double vti = double.nan;
    double otSV = double.nan;
    double otCSA = double.nan;

    otCSA = GeneralFormulas.csa(diam); // ???

    final otFeature = _findTraceOT();
    final vtiFeature = _findTraceVTI();

    if (otFeature != null) {
      final countOtVTIResult = TraceCal.getCountVTI(otFeature);
      otVti = countOtVTIResult[0];
    }

    if (vtiFeature != null) {
      final countVTIResult = TraceCal.getCountVTI(vtiFeature);
      vti = countVTIResult[0];
    }

    if (!vti.isNaN && !otVti.isNaN) {
      va = CardiacFormulas.flowAreaByVTI(diam, otVti, vti);
      dvi = CardiacFormulas.dviByVTI(otVti, vti);

      final bsa = GlobalPatientConfig.bsa;
      if (bsa > 0) {
        avaIndex = CardiacFormulas.avaIndex(va, bsa);
      }

      otSV = GeneralFormulas.svDiam(diam, otVti);
      otCSA = GeneralFormulas.csa(diam);
    }

    if (otFeature != null) {
      for (var output in ref.trace1.meta.outputs) {
        if (output.name == MeasureTerms.SV_Trace) {
          feature.updateStringValue(
            ItemOutputMeta(output.description, output.description, output.unit),
            '',
          );
        } else if (output.name == MeasureTerms.VTI) {
          if (!otVti.isNaN) {
            feature.updateFloatValue(
              ItemOutputMeta(
                  output.description, output.description, output.unit),
              otVti,
              VidUsUnit.cm,
            );
          }
        }
      }
    }
    if (vtiFeature != null) {
      for (var output in ref.trace2.meta.outputs) {
        if (output.name == MeasureTerms.SV_Trace) {
          feature.updateStringValue(
            ItemOutputMeta(output.description, output.description, output.unit),
            '',
          );
        } else if (output.name == MeasureTerms.VTI) {
          if (!vti.isNaN) {
            feature.updateFloatValue(
              ItemOutputMeta(
                  output.description, output.description, output.unit),
              vti,
              VidUsUnit.cm,
            );
          }
        }
      }
    }

    for (var output in ref.meta.outputs) {
      switch (output.name) {
        case MeasureTerms.FlowArea:
          if (!va.isNaN) {
            feature.updateFloatValue(output, va, VidUsUnit.cm2);
          }
          break;
        case MeasureTerms.AVAI:
          if (!avaIndex.isNaN) {
            feature.updateFloatValue(output, avaIndex, VidUsUnit.cm2m2);
          }
          break;
        case MeasureTerms.DVI:
          if (!dvi.isNaN) {
            feature.updateFloatValue(output, dvi, VidUsUnit.None);
          }
          break;
        case MeasureTerms.SV:
          if (!otSV.isNaN) {
            feature.updateFloatValue(output, otSV, VidUsUnit.cm3);
          }
          break;
        case MeasureTerms.CSA:
          if (!otCSA.isNaN) {
            feature.updateFloatValue(output, otCSA, VidUsUnit.cm2);
          }
          break;
      }
    }
  }

  TraceItemFeatureAbstract? _findTraceOT() {
    TraceItemFeatureAbstract? traceFeature = ref.trace1.feature;
    if (traceFeature == null) {
      if (ref.trace1.measuredFeatures.isNotEmpty) {
        traceFeature = ref.trace1.measuredFeatures.first;
      }
    }
    return traceFeature;
  }

  TraceItemFeatureAbstract? _findTraceVTI() {
    TraceItemFeatureAbstract? traceFeature = ref.trace2.feature;
    if (traceFeature == null) {
      if (ref.trace2.measuredFeatures.isNotEmpty) {
        traceFeature = ref.trace2.measuredFeatures.first;
      }
    }
    return traceFeature;
  }
}