// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

///|
/// 13.2.5.53 DOCTYPE state
fn Tokenizer::run_doctype_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => {
      self.state = BeforeDOCTYPEName
      None
    }
    Some('>') => {
      self.reconsume()
      self.state = BeforeDOCTYPEName
      None
    }
    Some(_) => {
      self.emit_error(MissingWhitespaceBeforeDoctypeName)
      self.reconsume()
      self.state = BeforeDOCTYPEName
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.reset_doctype()
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.54 Before DOCTYPE name state
fn Tokenizer::run_before_doctype_name_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => None // Ignore whitespace
    Some(c) if is_ascii_upper_alpha(c) => {
      self.reset_doctype()
      self.current_doctype_name.write_char(to_ascii_lower(c))
      self.state = DOCTYPEName
      None
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.reset_doctype()
      self.current_doctype_name.write_char('\u{FFFD}')
      self.state = DOCTYPEName
      None
    }
    Some('>') => {
      self.emit_error(MissingDoctypeName)
      self.reset_doctype()
      self.current_doctype_force_quirks = true
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(c) => {
      self.reset_doctype()
      self.current_doctype_name.write_char(c)
      self.state = DOCTYPEName
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.reset_doctype()
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.55 DOCTYPE name state
fn Tokenizer::run_doctype_name_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => {
      self.state = AfterDOCTYPEName
      None
    }
    Some('>') => {
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(c) if is_ascii_upper_alpha(c) => {
      self.current_doctype_name.write_char(to_ascii_lower(c))
      None
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.current_doctype_name.write_char('\u{FFFD}')
      None
    }
    Some(c) => {
      self.current_doctype_name.write_char(c)
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.56 After DOCTYPE name state
fn Tokenizer::run_after_doctype_name_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => None // Ignore whitespace
    Some('>') => {
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(_) => {
      // Check for PUBLIC or SYSTEM
      self.reconsume()
      if self.check_ascii_insensitive("PUBLIC") {
        for _ in 0..<6 {
          let _ = self.consume()
        }
        self.state = AfterDOCTYPEPublicKeyword
        None
      } else if self.check_ascii_insensitive("SYSTEM") {
        for _ in 0..<6 {
          let _ = self.consume()
        }
        self.state = AfterDOCTYPESystemKeyword
        None
      } else {
        self.emit_error(InvalidCharacterSequenceAfterDoctypeName)
        self.current_doctype_force_quirks = true
        self.state = BogusDOCTYPE
        None
      }
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.57 After DOCTYPE public keyword state
fn Tokenizer::run_after_doctype_public_keyword_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => {
      self.state = BeforeDOCTYPEPublicIdentifier
      None
    }
    Some('"') => {
      self.emit_error(MissingWhitespaceAfterDoctypePublicKeyword)
      self.current_doctype_public_id.reset()
      self.current_doctype_public_id_set = true
      self.state = DOCTYPEPublicIdentifierDoubleQuoted
      None
    }
    Some('\'') => {
      self.emit_error(MissingWhitespaceAfterDoctypePublicKeyword)
      self.current_doctype_public_id.reset()
      self.current_doctype_public_id_set = true
      self.state = DOCTYPEPublicIdentifierSingleQuoted
      None
    }
    Some('>') => {
      self.emit_error(MissingDoctypePublicIdentifier)
      self.current_doctype_force_quirks = true
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(_) => {
      self.emit_error(MissingQuoteBeforeDoctypePublicIdentifier)
      self.current_doctype_force_quirks = true
      self.reconsume()
      self.state = BogusDOCTYPE
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.58 Before DOCTYPE public identifier state
fn Tokenizer::run_before_doctype_public_identifier_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => None // Ignore whitespace
    Some('"') => {
      self.current_doctype_public_id.reset()
      self.current_doctype_public_id_set = true
      self.state = DOCTYPEPublicIdentifierDoubleQuoted
      None
    }
    Some('\'') => {
      self.current_doctype_public_id.reset()
      self.current_doctype_public_id_set = true
      self.state = DOCTYPEPublicIdentifierSingleQuoted
      None
    }
    Some('>') => {
      self.emit_error(MissingDoctypePublicIdentifier)
      self.current_doctype_force_quirks = true
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(_) => {
      self.emit_error(MissingQuoteBeforeDoctypePublicIdentifier)
      self.current_doctype_force_quirks = true
      self.reconsume()
      self.state = BogusDOCTYPE
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.59 DOCTYPE public identifier (double-quoted) state
fn Tokenizer::run_doctype_public_identifier_double_quoted_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some('"') => {
      self.state = AfterDOCTYPEPublicIdentifier
      None
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.current_doctype_public_id.write_char('\u{FFFD}')
      None
    }
    Some('>') => {
      self.emit_error(AbruptDoctypePublicIdentifier)
      self.current_doctype_force_quirks = true
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(c) => {
      self.current_doctype_public_id.write_char(c)
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.60 DOCTYPE public identifier (single-quoted) state
fn Tokenizer::run_doctype_public_identifier_single_quoted_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some('\'') => {
      self.state = AfterDOCTYPEPublicIdentifier
      None
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.current_doctype_public_id.write_char('\u{FFFD}')
      None
    }
    Some('>') => {
      self.emit_error(AbruptDoctypePublicIdentifier)
      self.current_doctype_force_quirks = true
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(c) => {
      self.current_doctype_public_id.write_char(c)
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.61 After DOCTYPE public identifier state
fn Tokenizer::run_after_doctype_public_identifier_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => {
      self.state = BetweenDOCTYPEPublicAndSystemIdentifiers
      None
    }
    Some('>') => {
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some('"') => {
      self.emit_error(MissingWhitespaceBetweenDoctypePublicAndSystemIdentifiers)
      self.current_doctype_system_id.reset()
      self.current_doctype_system_id_set = true
      self.state = DOCTYPESystemIdentifierDoubleQuoted
      None
    }
    Some('\'') => {
      self.emit_error(MissingWhitespaceBetweenDoctypePublicAndSystemIdentifiers)
      self.current_doctype_system_id.reset()
      self.current_doctype_system_id_set = true
      self.state = DOCTYPESystemIdentifierSingleQuoted
      None
    }
    Some(_) => {
      self.emit_error(MissingQuoteBeforeDoctypeSystemIdentifier)
      self.current_doctype_force_quirks = true
      self.reconsume()
      self.state = BogusDOCTYPE
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.62 Between DOCTYPE public and system identifiers state
fn Tokenizer::run_between_doctype_public_and_system_identifiers_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => None // Ignore whitespace
    Some('>') => {
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some('"') => {
      self.current_doctype_system_id.reset()
      self.current_doctype_system_id_set = true
      self.state = DOCTYPESystemIdentifierDoubleQuoted
      None
    }
    Some('\'') => {
      self.current_doctype_system_id.reset()
      self.current_doctype_system_id_set = true
      self.state = DOCTYPESystemIdentifierSingleQuoted
      None
    }
    Some(_) => {
      self.emit_error(MissingQuoteBeforeDoctypeSystemIdentifier)
      self.current_doctype_force_quirks = true
      self.reconsume()
      self.state = BogusDOCTYPE
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.63 After DOCTYPE system keyword state
fn Tokenizer::run_after_doctype_system_keyword_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => {
      self.state = BeforeDOCTYPESystemIdentifier
      None
    }
    Some('"') => {
      self.emit_error(MissingWhitespaceAfterDoctypeSystemKeyword)
      self.current_doctype_system_id.reset()
      self.current_doctype_system_id_set = true
      self.state = DOCTYPESystemIdentifierDoubleQuoted
      None
    }
    Some('\'') => {
      self.emit_error(MissingWhitespaceAfterDoctypeSystemKeyword)
      self.current_doctype_system_id.reset()
      self.current_doctype_system_id_set = true
      self.state = DOCTYPESystemIdentifierSingleQuoted
      None
    }
    Some('>') => {
      self.emit_error(MissingDoctypeSystemIdentifier)
      self.current_doctype_force_quirks = true
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(_) => {
      self.emit_error(MissingQuoteBeforeDoctypeSystemIdentifier)
      self.current_doctype_force_quirks = true
      self.reconsume()
      self.state = BogusDOCTYPE
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.64 Before DOCTYPE system identifier state
fn Tokenizer::run_before_doctype_system_identifier_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => None // Ignore whitespace
    Some('"') => {
      self.current_doctype_system_id.reset()
      self.current_doctype_system_id_set = true
      self.state = DOCTYPESystemIdentifierDoubleQuoted
      None
    }
    Some('\'') => {
      self.current_doctype_system_id.reset()
      self.current_doctype_system_id_set = true
      self.state = DOCTYPESystemIdentifierSingleQuoted
      None
    }
    Some('>') => {
      self.emit_error(MissingDoctypeSystemIdentifier)
      self.current_doctype_force_quirks = true
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(_) => {
      self.emit_error(MissingQuoteBeforeDoctypeSystemIdentifier)
      self.current_doctype_force_quirks = true
      self.reconsume()
      self.state = BogusDOCTYPE
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.65 DOCTYPE system identifier (double-quoted) state
fn Tokenizer::run_doctype_system_identifier_double_quoted_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some('"') => {
      self.state = AfterDOCTYPESystemIdentifier
      None
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.current_doctype_system_id.write_char('\u{FFFD}')
      None
    }
    Some('>') => {
      self.emit_error(AbruptDoctypeSystemIdentifier)
      self.current_doctype_force_quirks = true
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(c) => {
      self.current_doctype_system_id.write_char(c)
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.66 DOCTYPE system identifier (single-quoted) state
fn Tokenizer::run_doctype_system_identifier_single_quoted_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some('\'') => {
      self.state = AfterDOCTYPESystemIdentifier
      None
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.current_doctype_system_id.write_char('\u{FFFD}')
      None
    }
    Some('>') => {
      self.emit_error(AbruptDoctypeSystemIdentifier)
      self.current_doctype_force_quirks = true
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(c) => {
      self.current_doctype_system_id.write_char(c)
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.67 After DOCTYPE system identifier state
fn Tokenizer::run_after_doctype_system_identifier_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) => None // Ignore whitespace
    Some('>') => {
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some(_) => {
      self.emit_error(UnexpectedCharacterAfterDoctypeSystemIdentifier)
      self.reconsume()
      self.state = BogusDOCTYPE
      None
    }
    None => {
      self.emit_error(EofInDoctype)
      self.current_doctype_force_quirks = true
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.68 Bogus DOCTYPE state
fn Tokenizer::run_bogus_doctype_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('>') => {
      self.state = Data
      Some(self.emit_current_doctype())
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      None
    }
    Some(_) => None
    None => {
      let token = self.emit_current_doctype()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}