import 'scan_conditions.dart';
import 'vital_sign.dart';

/// The information of a video frame.
class VideoFrameInfo {
  final double width;
  final double height;

  const VideoFrameInfo(this.width, this.height);
  factory VideoFrameInfo.fromMap(Map map) {
    double width = map['width'];
    double height = map['height'];
    return VideoFrameInfo(width, height);
  }
}

/// The normalized coordinates of a bounding box of a face.
class NormalizedFaceBox {
  final double xCenter;
  final double yCenter;
  final double height;
  final double width;

  const NormalizedFaceBox(this.xCenter, this.yCenter, this.height, this.width);

  factory NormalizedFaceBox.fromMap(Map map) {
    return NormalizedFaceBox(
        map['xCenter'], map['yCenter'], map['height'], map['width']);
  }
}

/// The stages of the scan.
enum GetHealthStage {
  /// The first stage of the scan, waiting until the it is ready to collect data.
  waitingData,

  /// The second stage of the scan, collecting face data.
  collectingData,

  /// The third stage of the scan, sending data to the backend for analyzing.
  analyzingData,

  /// Idle stage.
  idle
}

/// Returns [GetHealthStage] from an integer value.
GetHealthStage getHealthStageFromInt(int value) {
  if (value == 0) {
    return GetHealthStage.waitingData;
  } else if (value == 1) {
    return GetHealthStage.collectingData;
  } else if (value == 2) {
    return GetHealthStage.analyzingData;
  } else if (value == 3) {
    return GetHealthStage.idle;
  } else {
    return GetHealthStage.idle;
  }
}

/// Describing the properties of the scanning signals sent to the plugin.
class ScanParameters {
  final List conditions;
  final double invalidPixelPercentage;
  final double signalDuration;
  final double signalQuality;
  final double snr;

  const ScanParameters(this.conditions, this.invalidPixelPercentage,
      this.signalDuration, this.signalQuality, this.snr);

  factory ScanParameters.fromMap(Map map) {
    return ScanParameters(map['conditions'], map['invalidPixelPercentage'],
        map['signalDuration'], map['signalQuality'], map['snr']);
  }
}

/// The Health result of the scanning.
class Health {
  final DateTime? datetime;
  final ScanParameters? scanParameters;
  final VitalSign vitalSigns;
  final HealthRisks? risks;
  final HolisticAnalysis? holisticHealth;

  const Health(this.datetime, this.scanParameters, this.vitalSigns, this.risks,
      this.holisticHealth);

  factory Health.fromMap(Map map) {
    return Health(
        null,
        map['scanParameters'] != null
            ? ScanParameters.fromMap(map['scanParameters'])
            : null,
        VitalSign.fromMap(map['vitalSigns']),
        map['risks'] != null ? HealthRisks.fromMap(map['risks']) : null,
        map['holisticHealth'] != null
            ? HolisticAnalysis.fromMap(map['holisticHealth'])
            : null);
  }
}

/// The information of the ongoing scan.
class HealthResult {
  /// The stage of the scanning.
  final GetHealthStage stage;

  /// The remaining time of the current scanning stage.
  final double remainingTime;

  /// The information or warning message of the scan.
  final String? message;

  /// The error of the scan.
  final dynamic error;

  /// The error code of the scan.
  final int? errorCode;

  /// The health result of the scan.
  final Health? health;

  const HealthResult(this.stage, this.remainingTime, this.message, this.error,
      this.errorCode, this.health);

  factory HealthResult.fromMap(Map map) {
    return HealthResult(
        getHealthStageFromInt(map['stage']),
        map['remainingTime'],
        map['message'],
        map['error'],
        map['errorCode'],
        map['health'] != null ? Health.fromMap(map['health']) : null);
  }
}

/// The information of the ongoing scan.
class VideoFrameProcessedEvent {
  final VideoFrameInfo videoFrameInfo;
  final ScanConditions scanConditions;
  final NormalizedFaceBox? faceBox;
  final HealthResult? healthResult;

  const VideoFrameProcessedEvent(this.videoFrameInfo, this.scanConditions,
      this.faceBox, this.healthResult);

  factory VideoFrameProcessedEvent.fromMap(Map map) {
    return VideoFrameProcessedEvent(
        VideoFrameInfo.fromMap(map['videoFrameInfo']),
        ScanConditions.fromMap(map['scanConditions']),
        map['faceBox'] != null
            ? NormalizedFaceBox.fromMap(map['faceBox'])
            : null,
        map['healthResult'] != null
            ? HealthResult.fromMap(map['healthResult'])
            : null);
  }
}
