///|
pub struct Issue {
  code : String
  curve : String
  row : Int
  depth : Double
  message : String
}

///|
pub(all) struct QcRule {
  curve : String
  minimum : Double?
  maximum : Double?
  max_gradient : Double?
  constant_run : Int
}

///|
/// Rules run in caller order, rows in acquisition order. No interpretations inferred.
pub fn quality_check(
  log : Log,
  rules : Array[QcRule],
  max_depth_gap : Double,
) -> Array[Issue] raise WellError {
  validate(log)
  if !finite(max_depth_gap) || max_depth_gap <= 0.0 {
    fail("QC depth gap must be positive and finite")
  }
  if rules.length() > 512 {
    fail("QC rule budget is 512")
  }
  let issues : Array[Issue] = []
  for i in 1.. max_depth_gap {
      issues.push({
        code: "DEPTH_GAP",
        curve: log.curves[0].name,
        row: i,
        depth: log.rows[i][0].unwrap(),
        message: "depth gap " + delta.to_string(),
      })
    }
  }
  for rule in rules {
    let c = curve_index(log, rule.curve)
    for v in [rule.minimum, rule.maximum, rule.max_gradient] {
      if v is Some(x) && !finite(x) {
        fail("QC thresholds must be finite")
      }
    }
    if rule.minimum is Some(lo) && rule.maximum is Some(hi) && lo > hi {
      fail("QC bounds reversed")
    }
    if rule.max_gradient is Some(g) && g < 0.0 {
      fail("QC gradient must be nonnegative")
    }
    if rule.constant_run < 0 || rule.constant_run == 1 {
      fail("constant run is disabled (0) or at least 2")
    }
    let mut run = 0
    let mut previous : Double? = None
    for i in 0.. {
          codes.push("MISSING")
          run = 0
        }
        Some(v) => {
          if rule.minimum is Some(lo) && v < lo {
            codes.push("BELOW_MIN")
          }
          if rule.maximum is Some(hi) && v > hi {
            codes.push("ABOVE_MAX")
          }
          if previous is Some(p) {
            run = if p == v { run + 1 } else { 1 }
            if rule.max_gradient is Some(g) && i > 0 {
              let gradient = adjacent_gradient(
                p,
                v,
                log.rows[i - 1][0].unwrap(),
                depth,
              )
              if gradient.abs() > g {
                codes.push("GRADIENT")
              }
            }
          } else {
            run = 1
          }
          if rule.constant_run > 0 && run == rule.constant_run {
            codes.push("CONSTANT_RUN")
          }
        }
      }
      for code in codes {
        issues.push({
          code,
          curve: log.curves[c].name,
          row: i,
          depth,
          message: "rule violation",
        })
        if issues.length() > 1000000 {
          fail("QC issue budget exceeded")
        }
      }
      previous = value
    }
  }
  issues
}