// 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.15 Script data less-than sign state
fn Tokenizer::run_script_data_less_than_sign_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('/') => {
      self.temp_buffer.reset()
      self.state = ScriptDataEndTagOpen
      None
    }
    Some('!') => {
      self.state = ScriptDataEscapeStart
      self.pending_tokens.push(Character('!'))
      Some(Character('<'))
    }
    Some(_) => {
      self.reconsume()
      self.state = ScriptData
      Some(Character('<'))
    }
    None => {
      self.state = ScriptData
      Some(Character('<'))
    }
  }
}

///|
/// 13.2.5.16 Script data end tag open state
fn Tokenizer::run_script_data_end_tag_open_state(self : Tokenizer) -> Token? {
  match self.peek() {
    Some(c) if is_ascii_alpha(c) => {
      self.reset_tag(true)
      self.state = ScriptDataEndTagName
      None
    }
    _ => {
      self.state = ScriptData
      self.pending_tokens.push(Character('/'))
      Some(Character('<'))
    }
  }
}

///|
/// 13.2.5.17 Script data end tag name state
fn Tokenizer::run_script_data_end_tag_name_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) =>
      if self.is_appropriate_end_tag() {
        self.state = BeforeAttributeName
        None
      } else {
        self.emit_temp_buffer_as_chars('<', '/')
        self.pending_tokens.push(Character(c))
        self.state = ScriptData
        None
      }
    Some('/') =>
      if self.is_appropriate_end_tag() {
        self.state = SelfClosingStartTag
        None
      } else {
        self.emit_temp_buffer_as_chars('<', '/')
        self.pending_tokens.push(Character('/'))
        self.state = ScriptData
        None
      }
    Some('>') =>
      if self.is_appropriate_end_tag() {
        self.state = Data
        Some(self.emit_current_tag())
      } else {
        self.emit_temp_buffer_as_chars('<', '/')
        self.pending_tokens.push(Character('>'))
        self.state = ScriptData
        None
      }
    Some(c) if is_ascii_upper_alpha(c) => {
      self.current_tag_name.write_char(to_ascii_lower(c))
      self.temp_buffer.write_char(c)
      None
    }
    Some(c) if is_ascii_lower_alpha(c) => {
      self.current_tag_name.write_char(c)
      self.temp_buffer.write_char(c)
      None
    }
    None => {
      // At EOF, emit the buffered chars and reconsume in script data state
      // (per WHATWG spec, we don't complete the end tag at EOF)
      self.emit_temp_buffer_as_chars('<', '/')
      self.reconsume()
      self.state = ScriptData
      None
    }
    _ => {
      self.reconsume()
      self.emit_temp_buffer_as_chars('<', '/')
      self.state = ScriptData
      None
    }
  }
}

///|
/// 13.2.5.18 Script data escape start state
fn Tokenizer::run_script_data_escape_start_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = ScriptDataEscapeStartDash
      Some(Character('-'))
    }
    Some(_) => {
      self.reconsume()
      self.state = ScriptData
      None
    }
    None => {
      self.state = ScriptData
      None
    }
  }
}

///|
/// 13.2.5.19 Script data escape start dash state
fn Tokenizer::run_script_data_escape_start_dash_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = ScriptDataEscapedDashDash
      Some(Character('-'))
    }
    Some(_) => {
      self.reconsume()
      self.state = ScriptData
      None
    }
    None => {
      self.state = ScriptData
      None
    }
  }
}

///|
/// 13.2.5.20 Script data escaped state
fn Tokenizer::run_script_data_escaped_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = ScriptDataEscapedDash
      Some(Character('-'))
    }
    Some('<') => {
      self.state = ScriptDataEscapedLessThanSign
      None
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      Some(Character('\u{FFFD}'))
    }
    Some(c) => Some(Character(c))
    None => {
      self.emit_error(EofInScriptHtmlCommentLikeText)
      Some(EOF)
    }
  }
}

///|
/// 13.2.5.21 Script data escaped dash state
fn Tokenizer::run_script_data_escaped_dash_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = ScriptDataEscapedDashDash
      Some(Character('-'))
    }
    Some('<') => {
      self.state = ScriptDataEscapedLessThanSign
      None
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.state = ScriptDataEscaped
      Some(Character('\u{FFFD}'))
    }
    Some(c) => {
      self.state = ScriptDataEscaped
      Some(Character(c))
    }
    None => {
      self.emit_error(EofInScriptHtmlCommentLikeText)
      Some(EOF)
    }
  }
}

///|
/// 13.2.5.22 Script data escaped dash dash state
fn Tokenizer::run_script_data_escaped_dash_dash_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some('-') => Some(Character('-'))
    Some('<') => {
      self.state = ScriptDataEscapedLessThanSign
      None
    }
    Some('>') => {
      self.state = ScriptData
      Some(Character('>'))
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.state = ScriptDataEscaped
      Some(Character('\u{FFFD}'))
    }
    Some(c) => {
      self.state = ScriptDataEscaped
      Some(Character(c))
    }
    None => {
      self.emit_error(EofInScriptHtmlCommentLikeText)
      Some(EOF)
    }
  }
}

///|
/// 13.2.5.23 Script data escaped less-than sign state
fn Tokenizer::run_script_data_escaped_less_than_sign_state(
  self : Tokenizer,
) -> Token? {
  match self.peek() {
    Some('/') => {
      let _ = self.consume()
      self.temp_buffer.reset()
      self.state = ScriptDataEscapedEndTagOpen
      None
    }
    Some(c) if is_ascii_alpha(c) => {
      self.temp_buffer.reset()
      self.state = ScriptDataDoubleEscapeStart
      Some(Character('<'))
    }
    _ => {
      self.state = ScriptDataEscaped
      Some(Character('<'))
    }
  }
}

///|
/// 13.2.5.24 Script data escaped end tag open state
fn Tokenizer::run_script_data_escaped_end_tag_open_state(
  self : Tokenizer,
) -> Token? {
  match self.peek() {
    Some(c) if is_ascii_alpha(c) => {
      self.reset_tag(true)
      self.state = ScriptDataEscapedEndTagName
      None
    }
    _ => {
      self.state = ScriptDataEscaped
      self.pending_tokens.push(Character('/'))
      Some(Character('<'))
    }
  }
}

///|
/// 13.2.5.25 Script data escaped end tag name state
fn Tokenizer::run_script_data_escaped_end_tag_name_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) =>
      if self.is_appropriate_end_tag() {
        self.state = BeforeAttributeName
        None
      } else {
        self.emit_temp_buffer_as_chars('<', '/')
        self.pending_tokens.push(Character(c))
        self.state = ScriptDataEscaped
        None
      }
    Some('/') =>
      if self.is_appropriate_end_tag() {
        self.state = SelfClosingStartTag
        None
      } else {
        self.emit_temp_buffer_as_chars('<', '/')
        self.pending_tokens.push(Character('/'))
        self.state = ScriptDataEscaped
        None
      }
    Some('>') =>
      if self.is_appropriate_end_tag() {
        self.state = Data
        Some(self.emit_current_tag())
      } else {
        self.emit_temp_buffer_as_chars('<', '/')
        self.pending_tokens.push(Character('>'))
        self.state = ScriptDataEscaped
        None
      }
    Some(c) if is_ascii_upper_alpha(c) => {
      self.current_tag_name.write_char(to_ascii_lower(c))
      self.temp_buffer.write_char(c)
      None
    }
    Some(c) if is_ascii_lower_alpha(c) => {
      self.current_tag_name.write_char(c)
      self.temp_buffer.write_char(c)
      None
    }
    None => {
      // At EOF, emit the buffered chars and reconsume in script data escaped state
      self.emit_temp_buffer_as_chars('<', '/')
      self.reconsume()
      self.state = ScriptDataEscaped
      None
    }
    _ => {
      self.reconsume()
      self.emit_temp_buffer_as_chars('<', '/')
      self.state = ScriptDataEscaped
      None
    }
  }
}

///|
/// 13.2.5.26 Script data double escape start state
fn Tokenizer::run_script_data_double_escape_start_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) || c == '/' || c == '>' => {
      if self.temp_buffer.to_string() == "script" {
        self.state = ScriptDataDoubleEscaped
      } else {
        self.state = ScriptDataEscaped
      }
      Some(Character(c))
    }
    Some(c) if is_ascii_upper_alpha(c) => {
      self.temp_buffer.write_char(to_ascii_lower(c))
      Some(Character(c))
    }
    Some(c) if is_ascii_lower_alpha(c) => {
      self.temp_buffer.write_char(c)
      Some(Character(c))
    }
    Some(_) => {
      self.reconsume()
      self.state = ScriptDataEscaped
      None
    }
    None => {
      self.state = ScriptDataEscaped
      None
    }
  }
}

///|
/// 13.2.5.27 Script data double escaped state
fn Tokenizer::run_script_data_double_escaped_state(self : Tokenizer) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = ScriptDataDoubleEscapedDash
      Some(Character('-'))
    }
    Some('<') => {
      self.state = ScriptDataDoubleEscapedLessThanSign
      Some(Character('<'))
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      Some(Character('\u{FFFD}'))
    }
    Some(c) => Some(Character(c))
    None => {
      self.emit_error(EofInScriptHtmlCommentLikeText)
      Some(EOF)
    }
  }
}

///|
/// 13.2.5.28 Script data double escaped dash state
fn Tokenizer::run_script_data_double_escaped_dash_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some('-') => {
      self.state = ScriptDataDoubleEscapedDashDash
      Some(Character('-'))
    }
    Some('<') => {
      self.state = ScriptDataDoubleEscapedLessThanSign
      Some(Character('<'))
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.state = ScriptDataDoubleEscaped
      Some(Character('\u{FFFD}'))
    }
    Some(c) => {
      self.state = ScriptDataDoubleEscaped
      Some(Character(c))
    }
    None => {
      self.emit_error(EofInScriptHtmlCommentLikeText)
      Some(EOF)
    }
  }
}

///|
/// 13.2.5.29 Script data double escaped dash dash state
fn Tokenizer::run_script_data_double_escaped_dash_dash_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some('-') => Some(Character('-'))
    Some('<') => {
      self.state = ScriptDataDoubleEscapedLessThanSign
      Some(Character('<'))
    }
    Some('>') => {
      self.state = ScriptData
      Some(Character('>'))
    }
    Some('\u{0000}') => {
      self.emit_error(UnexpectedNullCharacter)
      self.state = ScriptDataDoubleEscaped
      Some(Character('\u{FFFD}'))
    }
    Some(c) => {
      self.state = ScriptDataDoubleEscaped
      Some(Character(c))
    }
    None => {
      self.emit_error(EofInScriptHtmlCommentLikeText)
      Some(EOF)
    }
  }
}

///|
/// 13.2.5.30 Script data double escaped less-than sign state
fn Tokenizer::run_script_data_double_escaped_less_than_sign_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some('/') => {
      self.temp_buffer.reset()
      self.state = ScriptDataDoubleEscapeEnd
      Some(Character('/'))
    }
    Some(_) => {
      self.reconsume()
      self.state = ScriptDataDoubleEscaped
      None
    }
    None => {
      self.state = ScriptDataDoubleEscaped
      None
    }
  }
}

///|
/// 13.2.5.31 Script data double escape end state
fn Tokenizer::run_script_data_double_escape_end_state(
  self : Tokenizer,
) -> Token? {
  match self.consume() {
    Some(c) if is_whitespace(c) || c == '/' || c == '>' => {
      if self.temp_buffer.to_string() == "script" {
        self.state = ScriptDataEscaped
      } else {
        self.state = ScriptDataDoubleEscaped
      }
      Some(Character(c))
    }
    Some(c) if is_ascii_upper_alpha(c) => {
      self.temp_buffer.write_char(to_ascii_lower(c))
      Some(Character(c))
    }
    Some(c) if is_ascii_lower_alpha(c) => {
      self.temp_buffer.write_char(c)
      Some(Character(c))
    }
    Some(_) => {
      self.reconsume()
      self.state = ScriptDataDoubleEscaped
      None
    }
    None => {
      self.state = ScriptDataDoubleEscaped
      None
    }
  }
}