// 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.41 Bogus comment state
fn Tokenizer::run_bogus_comment_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('>') => {
      self.state = Data
      Some(self.emit_current_comment())
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.current_comment.write_char('\u{FFFD}')
      None
    }
    Some(c) => {
      self.current_comment.write_char(c)
      None
    }
    None => {
      let token = self.emit_current_comment()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.42 Markup declaration open state
fn Tokenizer::run_markup_declaration_open_state(self : Tokenizer) -> Token? {
  // Check for "--" (comment)
  if self.peek() == Some('-') && self.peek_at(1) == Some('-') {
    let _ = self.consume()
    let _ = self.consume()
    self.reset_comment()
    self.state = CommentStart
    return None
  }
  // Check for "DOCTYPE" (case-insensitive)
  if self.check_ascii_insensitive("DOCTYPE") {
    for _ in 0..<7 {
      let _ = self.consume()
    }
    self.state = DOCTYPE
    return None
  }
  // Check for "[CDATA["
  if self.check_string("[CDATA[") {
    for _ in 0..<7 {
      let _ = self.consume()
    }
    // CDATA is only allowed in foreign content (SVG/MathML)
    if self.in_foreign_content {
      // In foreign content, switch to CDATA section state
      self.state = CDATASection
      return None
    }
    // In HTML content, emit error and treat as bogus comment
    self.emit_error(CdataInHtmlContent)
    self.reset_comment()
    self.current_comment.write_string("[CDATA[")
    self.state = BogusComment
    return None
  }
  // Anything else is incorrectly opened comment
  self.emit_error_at_next_position(IncorrectlyOpenedComment)
  self.reset_comment()
  self.state = BogusComment
  None
}

///|
/// Check if next chars match string (case-insensitive ASCII)
fn Tokenizer::check_ascii_insensitive(self : Tokenizer, s : String) -> Bool {
  let chars = s.to_array()
  for i = 0; i < chars.length(); i = i + 1 {
    match self.peek_at(i) {
      Some(c) => {
        let expected = chars[i]
        let c_lower = to_ascii_lower(c)
        let expected_lower = to_ascii_lower(expected)
        if c_lower != expected_lower {
          return false
        }
      }
      None => return false
    }
  }
  true
}

///|
/// Check if next chars match string exactly
fn Tokenizer::check_string(self : Tokenizer, s : String) -> Bool {
  let chars = s.to_array()
  for i = 0; i < chars.length(); i = i + 1 {
    match self.peek_at(i) {
      Some(c) if c == chars[i] => continue
      _ => return false
    }
  }
  true
}

///|
/// 13.2.5.43 Comment start state
fn Tokenizer::run_comment_start_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = CommentStartDash
      None
    }
    Some('>') => {
      self.emit_error(AbruptClosingOfEmptyComment)
      self.state = Data
      Some(self.emit_current_comment())
    }
    Some(_) => {
      self.reconsume()
      self.state = Comment
      None
    }
    None => {
      self.reconsume()
      self.state = Comment
      None
    }
  }
}

///|
/// 13.2.5.44 Comment start dash state
fn Tokenizer::run_comment_start_dash_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = CommentEnd
      None
    }
    Some('>') => {
      self.emit_error(AbruptClosingOfEmptyComment)
      self.state = Data
      Some(self.emit_current_comment())
    }
    Some(_) => {
      self.current_comment.write_char('-')
      self.reconsume()
      self.state = Comment
      None
    }
    None => {
      self.emit_error(EofInComment)
      let token = self.emit_current_comment()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.45 Comment state
fn Tokenizer::run_comment_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('<') => {
      self.current_comment.write_char('<')
      self.state = CommentLessThanSign
      None
    }
    Some('-') => {
      self.state = CommentEndDash
      None
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.current_comment.write_char('\u{FFFD}')
      None
    }
    Some(c) => {
      self.current_comment.write_char(c)
      None
    }
    None => {
      self.emit_error(EofInComment)
      let token = self.emit_current_comment()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.46 Comment less-than sign state
fn Tokenizer::run_comment_less_than_sign_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('!') => {
      self.current_comment.write_char('!')
      self.state = CommentLessThanSignBang
      None
    }
    Some('<') => {
      self.current_comment.write_char('<')
      None
    }
    Some(_) => {
      self.reconsume()
      self.state = Comment
      None
    }
    None => {
      self.reconsume()
      self.state = Comment
      None
    }
  }
}

///|
/// 13.2.5.47 Comment less-than sign bang state
fn Tokenizer::run_comment_less_than_sign_bang_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = CommentLessThanSignBangDash
      None
    }
    Some(_) => {
      self.reconsume()
      self.state = Comment
      None
    }
    None => {
      self.reconsume()
      self.state = Comment
      None
    }
  }
}

///|
/// 13.2.5.48 Comment less-than sign bang dash state
fn Tokenizer::run_comment_less_than_sign_bang_dash_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = CommentLessThanSignBangDashDash
      None
    }
    Some(_) => {
      self.reconsume()
      self.state = CommentEndDash
      None
    }
    None => {
      self.reconsume()
      self.state = CommentEndDash
      None
    }
  }
}

///|
/// 13.2.5.49 Comment less-than sign bang dash dash state
fn Tokenizer::run_comment_less_than_sign_bang_dash_dash_state(
  self : Tokenizer,
) -> Token? {
  match self.peek() {
    Some('>') | None => {
      self.state = CommentEnd
      None
    }
    Some(_) => {
      self.emit_error_at_next_position(NestedComment)
      self.state = CommentEnd
      None
    }
  }
}

///|
/// 13.2.5.50 Comment end dash state
fn Tokenizer::run_comment_end_dash_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = CommentEnd
      None
    }
    Some(_) => {
      self.current_comment.write_char('-')
      self.reconsume()
      self.state = Comment
      None
    }
    None => {
      self.emit_error(EofInComment)
      let token = self.emit_current_comment()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.51 Comment end state
fn Tokenizer::run_comment_end_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('>') => {
      self.state = Data
      Some(self.emit_current_comment())
    }
    Some('!') => {
      self.state = CommentEndBang
      None
    }
    Some('-') => {
      self.current_comment.write_char('-')
      None
    }
    Some(_) => {
      self.current_comment.write_string("--")
      self.reconsume()
      self.state = Comment
      None
    }
    None => {
      self.emit_error(EofInComment)
      let token = self.emit_current_comment()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}

///|
/// 13.2.5.52 Comment end bang state
fn Tokenizer::run_comment_end_bang_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('-') => {
      self.current_comment.write_string("--!")
      self.state = CommentEndDash
      None
    }
    Some('>') => {
      self.emit_error(IncorrectlyClosedComment)
      self.state = Data
      Some(self.emit_current_comment())
    }
    Some(_) => {
      self.current_comment.write_string("--!")
      self.reconsume()
      self.state = Comment
      None
    }
    None => {
      self.emit_error(EofInComment)
      let token = self.emit_current_comment()
      self.pending_tokens.push(EOF)
      Some(token)
    }
  }
}