///|
/// Validation policies for production sample tables.
pub struct SampleValidationPolicy2 {
  tolerance : Double
  endpoint_tolerance : Double
  maximum_gap : Double
  require_monotonic_time : Bool
} derive(Debug)

///|
pub fn sample_validation_policy2() -> SampleValidationPolicy2 {
  {
    tolerance: 0.000001,
    endpoint_tolerance: 0.000001,
    maximum_gap: 0.1,
    require_monotonic_time: true,
  }
}

///|
pub fn SampleValidationPolicy2::with_tolerance(
  self : SampleValidationPolicy2,
  value : Double,
) -> SampleValidationPolicy2 {
  {
    tolerance: value.max(0.000000000001),
    endpoint_tolerance: self.endpoint_tolerance,
    maximum_gap: self.maximum_gap,
    require_monotonic_time: self.require_monotonic_time,
  }
}

///|
pub fn SampleValidationPolicy2::with_gap(
  self : SampleValidationPolicy2,
  value : Double,
) -> SampleValidationPolicy2 {
  {
    tolerance: self.tolerance,
    endpoint_tolerance: self.endpoint_tolerance,
    maximum_gap: value.max(0.0),
    require_monotonic_time: self.require_monotonic_time,
  }
}

///|
pub fn SampleValidationPolicy2::with_monotonic_time(
  self : SampleValidationPolicy2,
  value : Bool,
) -> SampleValidationPolicy2 {
  {
    tolerance: self.tolerance,
    endpoint_tolerance: self.endpoint_tolerance,
    maximum_gap: self.maximum_gap,
    require_monotonic_time: value,
  }
}

///|
pub fn SampleValidationPolicy2::tolerance(
  self : SampleValidationPolicy2,
) -> Double {
  self.tolerance
}

///|
pub fn SampleValidationPolicy2::endpoint_tolerance(
  self : SampleValidationPolicy2,
) -> Double {
  self.endpoint_tolerance
}

///|
pub fn SampleValidationPolicy2::maximum_gap(
  self : SampleValidationPolicy2,
) -> Double {
  self.maximum_gap
}

///|
pub fn validate_sample_times2(
  name : String,
  times : Array[Double],
  policy : SampleValidationPolicy2,
) -> DiagnosticReport2 {
  let report = diagnostic_report2(name).with_measurements(
    times.length(),
    if times.length() == 0 {
      0.0
    } else {
      times[times.length() - 1] - times[0]
    },
  )
  for index in 0.. 0 {
      let gap = times[index] - times[index - 1]
      if policy.require_monotonic_time && gap <= 0.0 {
        report.add(
          diagnostic_issue(
            ErrorDiagnostic,
            EndpointMismatchDiagnostic,
            "sample times are not increasing",
            index,
            gap,
            0.0,
          ),
        )
      }
      if gap > policy.maximum_gap {
        report.add(
          diagnostic_issue(
            WarningDiagnostic,
            SamplingErrorDiagnostic,
            "sample time gap exceeds policy",
            index,
            gap,
            policy.maximum_gap,
          ),
        )
      }
    }
  }
  report
}

///|
pub fn validate_sample_values2(
  name : String,
  values : Array[Double],
  policy : SampleValidationPolicy2,
) -> DiagnosticReport2 {
  let report = inspect_scalar_values(name, values, policy.tolerance)
  if values.length() >= 2 &&
    (values[0] - values[values.length() - 1]).abs() <= policy.endpoint_tolerance {
    report.add(
      diagnostic_issue(
        InfoDiagnostic,
        EndpointMismatchDiagnostic,
        "sample table closes on the same endpoint",
        0,
        values[0],
        values[values.length() - 1],
      ),
    )
  }
  report
}

///|
pub fn validate_lut2(
  name : String,
  table : CurveLookupTable,
  curve : Curve,
  policy : SampleValidationPolicy2,
) -> DiagnosticReport2 {
  let report = diagnostic_report2(name).with_measurements(table.samples(), 1.0)
  if table.samples() < 2 {
    report.add(
      diagnostic_issue(
        ErrorDiagnostic,
        EmptyInputDiagnostic,
        "lookup table has fewer than two samples",
        0,
        table.samples().to_double(),
        2.0,
      ),
    )
    return report
  }
  if !table.monotonic() {
    report.add(
      diagnostic_issue(
        WarningDiagnostic,
        SamplingErrorDiagnostic,
        "lookup table is non-monotonic",
        0,
        0.0,
        0.0,
      ),
    )
  }
  let error = table.error(curve, table.samples() * 2)
  if error > policy.tolerance {
    report.add(
      diagnostic_issue(
        WarningDiagnostic,
        SamplingErrorDiagnostic,
        "lookup table error exceeds tolerance",
        0,
        error,
        policy.tolerance,
      ),
    )
  }
  report
}

///|
pub struct ValidationBundle2 {
  reports : Array[DiagnosticReport2]
  summary : DiagnosticSummary2
} derive(Debug)

///|
pub fn validation_bundle2(
  reports : Array[DiagnosticReport2],
) -> ValidationBundle2 {
  { reports: reports.copy(), summary: summarize_diagnostics(reports) }
}

///|
pub fn ValidationBundle2::reports(
  self : ValidationBundle2,
) -> Array[DiagnosticReport2] {
  self.reports.copy()
}

///|
pub fn ValidationBundle2::summary(
  self : ValidationBundle2,
) -> DiagnosticSummary2 {
  self.summary
}

///|
pub fn ValidationBundle2::passed(self : ValidationBundle2) -> Bool {
  self.summary.passed()
}