// Copyright 2026 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.

///|
pub(all) enum BufferSerializeFormat {
  Invalid
  Text
  Json
} derive(Eq, Show)

///|
pub(all) enum BufferSerializeMode {
  Glyphs
  Unicode
} derive(Eq, Show)

///|
pub fn BufferSerializeMode::from_string(
  s : String,
  len? : Int = -1,
) -> BufferSerializeMode {
  if s is "" || len == 0 {
    return BufferSerializeMode::Glyphs
  }
  let max_len = if len < 0 || len > s.length() { s.length() } else { len }
  let sb = StringBuilder::new(size_hint=max_len)
  let mut count = 0
  for c in s {
    if count >= max_len {
      break
    }
    sb.write_char(c.to_ascii_lowercase())
    count = count + 1
  }
  let normalized = sb.to_string()
  if normalized == "unicode" {
    BufferSerializeMode::Unicode
  } else {
    BufferSerializeMode::Glyphs
  }
}

///|
pub fn BufferSerializeFormat::from_string(
  s : String,
  len? : Int = -1,
) -> BufferSerializeFormat {
  if s is "" || len == 0 {
    return BufferSerializeFormat::Invalid
  }
  let max_len = if len < 0 || len > s.length() { s.length() } else { len }
  let sb = StringBuilder::new(size_hint=max_len)
  let mut count = 0
  for c in s {
    if count >= max_len {
      break
    }
    sb.write_char(c.to_ascii_lowercase())
    count = count + 1
  }
  let normalized = sb.to_string()
  if normalized == "text" {
    BufferSerializeFormat::Text
  } else if normalized == "json" {
    BufferSerializeFormat::Json
  } else {
    BufferSerializeFormat::Invalid
  }
}

///|
pub fn BufferSerializeFormat::to_string(self : BufferSerializeFormat) -> String? {
  match self {
    BufferSerializeFormat::Text => Some("text")
    BufferSerializeFormat::Json => Some("json")
    BufferSerializeFormat::Invalid => None
  }
}

fn hex_digit(value : Int) -> Char {
  let code = if value < 10 { 48 + value } else { 55 + value }
  code.unsafe_to_char()
}

fn format_hex(value : UInt, min_width : Int) -> String {
  let digits : Array[Char] = []
  let mut v = value
  if v == 0U {
    digits.push('0')
  } else {
    while v > 0U {
      let d = (v & 0xFU).reinterpret_as_int()
      digits.push(hex_digit(d))
      v = v >> 4
    }
  }
  while digits.length() < min_width {
    digits.push('0')
  }
  let len = digits.length()
  let sb = StringBuilder::new(size_hint=len)
  for i in 0.. Unit {
  sb.write_string("\{value}")
}

fn append_uint(sb : StringBuilder, value : UInt) -> Unit {
  sb.write_string("\{value}")
}

fn serialize_text(
  buffer : Buffer,
  mode : BufferSerializeMode,
  start : Int,
  end : Int,
  include_clusters : Bool,
  include_positions : Bool,
  include_advances : Bool,
) -> String {
  let sb = StringBuilder::new()
  let mut x = 0
  let mut y = 0
  let open = if mode == BufferSerializeMode::Glyphs { '[' } else { '<' }
  let close = if mode == BufferSerializeMode::Glyphs { ']' } else { '>' }
  sb.write_char(open)
  for i in start.. start {
      sb.write_char('|')
    }
    let info = buffer.infos[i]
    if mode == BufferSerializeMode::Glyphs {
      append_uint(sb, info.codepoint)
    } else {
      sb.write_string("U+")
      sb.write_string(format_hex(info.unicode, 4))
    }
    if include_clusters {
      sb.write_char('=')
      append_int(sb, info.cluster)
    }
    if include_positions {
      let pos = buffer.positions[i]
      let dx = x + pos.x_offset
      let dy = y + pos.y_offset
      if dx != 0 || dy != 0 {
        sb.write_char('@')
        append_int(sb, dx)
        sb.write_char(',')
        append_int(sb, dy)
      }
      if include_advances {
        sb.write_char('+')
        append_int(sb, pos.x_advance)
        if pos.y_advance != 0 {
          sb.write_char(',')
          append_int(sb, pos.y_advance)
        }
      } else {
        x = x + pos.x_advance
        y = y + pos.y_advance
      }
    }
  }
  sb.write_char(close)
  sb.to_string()
}

fn serialize_json(
  buffer : Buffer,
  mode : BufferSerializeMode,
  start : Int,
  end : Int,
  include_clusters : Bool,
  include_positions : Bool,
  include_advances : Bool,
) -> String {
  let sb = StringBuilder::new()
  let mut x = 0
  let mut y = 0
  sb.write_char('[')
  for i in start.. start {
      sb.write_char(',')
    }
    sb.write_char('{')
    let info = buffer.infos[i]
    if mode == BufferSerializeMode::Glyphs {
      sb.write_string("\"g\":")
      append_uint(sb, info.codepoint)
    } else {
      sb.write_string("\"u\":")
      append_uint(sb, info.unicode)
    }
    if include_clusters {
      sb.write_string(",\"cl\":")
      append_int(sb, info.cluster)
    }
    if include_positions {
      let pos = buffer.positions[i]
      let dx = x + pos.x_offset
      let dy = y + pos.y_offset
      sb.write_string(",\"dx\":")
      append_int(sb, dx)
      sb.write_string(",\"dy\":")
      append_int(sb, dy)
      if include_advances {
        sb.write_string(",\"ax\":")
        append_int(sb, pos.x_advance)
        sb.write_string(",\"ay\":")
        append_int(sb, pos.y_advance)
      } else {
        x = x + pos.x_advance
        y = y + pos.y_advance
      }
    }
    sb.write_char('}')
  }
  sb.write_char(']')
  sb.to_string()
}

///|
pub fn Buffer::serialize(
  self : Buffer,
  format? : BufferSerializeFormat = BufferSerializeFormat::Text,
  mode? : BufferSerializeMode = BufferSerializeMode::Glyphs,
  start? : Int = 0,
  end? : Int = -1,
  no_positions? : Bool = false,
  no_clusters? : Bool = false,
  no_advances? : Bool = false,
) -> String {
  let len = self.infos.length()
  let mut start = start
  let end = if end < 0 || end > len { len } else { end }
  if start < 0 {
    start = 0
  }
  if start > end {
    start = end
  }
  if start >= end {
    return ""
  }
  let include_positions = !no_positions
  let include_advances = include_positions && !no_advances
  let include_clusters = !no_clusters
  match format {
    BufferSerializeFormat::Text =>
      serialize_text(
        self,
        mode,
        start,
        end,
        include_clusters,
        include_positions,
        include_advances,
      )
    BufferSerializeFormat::Json =>
      serialize_json(
        self,
        mode,
        start,
        end,
        include_clusters,
        include_positions,
        include_advances,
      )
    BufferSerializeFormat::Invalid => ""
  }
}