// SPDX-License-Identifier: MIT

///|
pub impl Automaton for Matcher with fn start(self) {
  match self.kind {
    Empty => -1
    Either(a, b) | Both(a, b) => pair_state(a.start(), b.start(), b.states + 1)
    Not(inner) => inner.start() + 1
    Beginning(inner) => {
      let state = inner.start()
      if end_matches(inner, state) {
        inner.states + 1
      } else {
        state + 1
      }
    }
    Explicit(machine) => machine.initial
    _ => 0
  }
}

///|
pub impl Automaton for Matcher with fn accept(self, state, byte) {
  if state < 0 || state >= self.states {
    return -1
  }
  match self.kind {
    All => 0
    Empty => -1
    Literal(pattern, prefix) =>
      if state == pattern.length() {
        if prefix {
          state
        } else {
          -1
        }
      } else if pattern[state] == byte {
        state + 1
      } else {
        -1
      }
    Subsequent(pattern) =>
      if state == pattern.length() {
        state
      } else if pattern[state] == byte {
        state + 1
      } else {
        state
      }
    OneByte(lo, hi) =>
      if state == 0 && byte.to_int() >= lo && byte.to_int() <= hi {
        1
      } else {
        -1
      }
    Length(minimum, maximum) =>
      match maximum {
        Some(n) => if state < n { state + 1 } else { -1 }
        None => if state < minimum { state + 1 } else { minimum }
      }
    Either(a, b) | Both(a, b) => {
      let (sa, sb) = split_state(state, b.states + 1)
      pair_state(a.accept(sa, byte), b.accept(sb, byte), b.states + 1)
    }
    Not(inner) => inner.accept(state - 1, byte) + 1
    Beginning(inner) =>
      if state == inner.states + 1 {
        state
      } else {
        let next = inner.accept(state - 1, byte)
        if end_matches(inner, next) {
          inner.states + 1
        } else {
          next + 1
        }
      }
    Explicit(machine) => machine.transitions[state][byte.to_int()]
    Levenshtein(machine) => machine.step(state, byte)
  }
}

///|
pub impl Automaton for Matcher with fn is_match(self, state) {
  if state < 0 || state >= self.states {
    return false
  }
  match self.kind {
    All => true
    Empty => false
    Literal(pattern, _) | Subsequent(pattern) => state == pattern.length()
    OneByte(_, _) => state == 1
    Length(minimum, _) => state >= minimum
    Either(a, b) => {
      let (sa, sb) = split_state(state, b.states + 1)
      a.is_match(sa) || b.is_match(sb)
    }
    Both(a, b) => {
      let (sa, sb) = split_state(state, b.states + 1)
      a.is_match(sa) && b.is_match(sb)
    }
    Not(inner) => !inner.is_match(state - 1)
    Beginning(inner) => state == inner.states + 1
    Explicit(machine) => machine.accepting[state]
    Levenshtein(machine) => machine.matches(state)
  }
}

///|
pub impl Automaton for Matcher with fn can_match(self, state) {
  if state < 0 || state >= self.states {
    return false
  }
  match self.kind {
    Empty => false
    Either(a, b) => {
      let (sa, sb) = split_state(state, b.states + 1)
      a.can_match(sa) || b.can_match(sb)
    }
    Both(a, b) => {
      let (sa, sb) = split_state(state, b.states + 1)
      a.can_match(sa) && b.can_match(sb)
    }
    Not(inner) => !inner.will_always_match(state - 1)
    Beginning(inner) => state == inner.states + 1 || inner.can_match(state - 1)
    Explicit(machine) => machine.live[state]
    // Construction interns only DP rows whose minimum is within the bound.
    // A pending valid UTF-8 prefix is conservatively kept live until completion.
    Levenshtein(_) => true
    _ => true
  }
}

///|
pub impl Automaton for Matcher with fn will_always_match(self, state) {
  if state < 0 || state >= self.states {
    return false
  }
  match self.kind {
    All => true
    Empty => false
    Literal(pattern, prefix) => prefix && state == pattern.length()
    Subsequent(pattern) => state == pattern.length()
    Length(minimum, maximum) => maximum is None && state >= minimum
    Either(a, b) => {
      let (sa, sb) = split_state(state, b.states + 1)
      a.will_always_match(sa) || b.will_always_match(sb)
    }
    Both(a, b) => {
      let (sa, sb) = split_state(state, b.states + 1)
      a.will_always_match(sa) && b.will_always_match(sb)
    }
    Not(inner) => !inner.can_match(state - 1)
    Beginning(inner) => state == inner.states + 1
    Explicit(machine) => machine.universal[state]
    _ => false
  }
}

///|
pub impl Automaton for Matcher with fn accept_eof(self, state) {
  if state < 0 || state >= self.states {
    return None
  }
  match self.kind {
    Either(a, b) | Both(a, b) => {
      let (sa, sb) = split_state(state, b.states + 1)
      let ea = a.accept_eof(sa).unwrap_or(sa)
      let eb = b.accept_eof(sb).unwrap_or(sb)
      Some(pair_state(ea, eb, b.states + 1))
    }
    Not(inner) => Some(inner.accept_eof(state - 1).unwrap_or(state - 1) + 1)
    Beginning(inner) =>
      if state == inner.states + 1 {
        None
      } else {
        let end = inner.accept_eof(state - 1).unwrap_or(state - 1)
        if inner.is_match(end) {
          Some(inner.states + 1)
        } else {
          Some(end + 1)
        }
      }
    Explicit(machine) => machine.eof[state]
    _ => None
  }
}