///|
/// Which reference-strand orientation recognized the motif.
pub(all) enum Orientation {
  Forward
  Reverse
} derive(Eq, Debug, ToJson, @json.FromJson)

///|
/// Certain means every possible input base satisfies the motif.
pub(all) enum Certainty {
  Certain
  Possible
} derive(Eq, Debug, ToJson, @json.FromJson)

///|
/// A recognition event can exist even when cleavage falls outside linear DNA.
pub(all) struct Site {
  enzyme : String
  start : Int
  orientation : Orientation
  top : Int
  bottom : Int
  stagger : Int
  certainty : Certainty
  cuttable : Bool
} derive(Eq, Debug, ToJson, @json.FromJson)

///|
fn base_mask(c : Char) -> Int {
  let mut result = 0
  for n in @base.IupacBase::from_char(c).expand() {
    let bit = match n.to_char() {
      'A' => 1
      'C' => 2
      'G' => 4
      _ => 8
    }
    result = result | bit
  }
  result
}

///|
fn match_window(
  input : Array[Int],
  motif : Array[Int],
  start : Int,
) -> Certainty? {
  let mut certain = true
  for j in 0.. Array[Site] raise RestrictError {
  let n = dna.length()
  let m = enzyme.motif.length()
  if n * m > 10000000 {
    raise LimitExceeded("SCAN_WORK")
  }
  let result : Array[Site] = []
  if m > n {
    return result
  }
  let input = dna.bases.to_array().map(base_mask)
  let forward = enzyme.motif.to_array().map(base_mask)
  let reverse = reverse_complement(Dna::new(enzyme.motif)).bases
    .to_array()
    .map(base_mask)
  let starts = if dna.topology == Circular { n } else { n - m + 1 }
  for start in 0.. ()
        Some(certainty) => {
          if result.length() >= 10000 {
            raise LimitExceeded("SITE_MAX_10000")
          }
          result.push({
            enzyme: enzyme.name,
            start,
            orientation,
            top: if dna.topology == Circular {
              wrap(top, n)
            } else {
              top
            },
            bottom: if dna.topology == Circular {
              wrap(bottom, n)
            } else {
              bottom
            },
            stagger: bottom - top,
            certainty,
            cuttable: if dna.topology == Circular {
              (bottom - top).abs() < n
            } else {
              top >= 0 && top <= n && bottom >= 0 && bottom <= n
            },
          })
        }
      }
    }
  }
  result
}

///|
fn wrap(position : Int, length : Int) -> Int {
  (position % length + length) % length
}