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

///|
/// CPAL color palette table.
pub struct CpalTable {
  version : Int
  num_colors : Int
  num_palettes : Int
  num_color_records : Int
  color_records : Array[@paint.Color]
  color_record_indices : Array[Int]
  palette_flags : Array[UInt]?
  palette_labels : Array[Int]?
  color_labels : Array[Int]?
} derive(Show, ToJson)

///|
/// Parse CPAL table data.
pub fn CpalTable::parse(data : BytesView) -> Result[CpalTable, ColorError] {
  let version = read_u16_int(data, 0)
  let num_colors = read_u16_int(data, 2)
  let num_palettes = read_u16_int(data, 4)
  let num_color_records = read_u16_int(data, 6)
  let color_records_offset = read_u32_int(data, 8)
  match (version, num_colors, num_palettes, num_color_records, color_records_offset) {
    (Err(err), _, _, _, _) => Err(err)
    (_, Err(err), _, _, _) => Err(err)
    (_, _, Err(err), _, _) => Err(err)
    (_, _, _, Err(err), _) => Err(err)
    (_, _, _, _, Err(err)) => Err(err)
    (
      Ok(version),
      Ok(num_colors),
      Ok(num_palettes),
      Ok(num_color_records),
      Ok(color_records_offset),
    ) => {
      if version < 0 || num_colors < 0 || num_palettes < 0 || num_color_records < 0 {
        return Err(InvalidFormat)
      }
      if version != 0 && version != 1 {
        return Err(InvalidFormat)
      }
      if color_records_offset < 0 {
        return Err(InvalidFormat)
      }
      let indices_offset = 12
      let indices_len = num_palettes * 2
      if indices_offset + indices_len > data.length() {
        return Err(UnexpectedEof)
      }
      let color_record_indices : Array[Int] = []
      for i in 0.. return Err(err)
          Ok(value) => color_record_indices.push(value)
        }
      }
      let record_size = 4
      let records_len = num_color_records * record_size
      if color_records_offset + records_len > data.length() {
        return Err(UnexpectedEof)
      }
      let color_records : Array[@paint.Color] = []
      for i in 0.. data.length() {
          return Err(UnexpectedEof)
        }
        let palette_flags_offset = read_u32_int(data, tail_offset)
        let palette_labels_offset = read_u32_int(data, tail_offset + 4)
        let color_labels_offset = read_u32_int(data, tail_offset + 8)
        match (palette_flags_offset, palette_labels_offset, color_labels_offset) {
          (Err(err), _, _) => return Err(err)
          (_, Err(err), _) => return Err(err)
          (_, _, Err(err)) => return Err(err)
          (Ok(flags_offset), Ok(labels_offset), Ok(colors_offset)) => {
            if flags_offset != 0 {
              if flags_offset < 0 || flags_offset + num_palettes * 4 > data.length() {
                return Err(UnexpectedEof)
              }
              let flags : Array[UInt] = []
              for i in 0.. return Err(err)
                  Ok(v) => flags.push(v)
                }
              }
              palette_flags = Some(flags)
            }
            if labels_offset != 0 {
              if labels_offset < 0 || labels_offset + num_palettes * 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let labels : Array[Int] = []
              for i in 0.. return Err(err)
                  Ok(v) => labels.push(v)
                }
              }
              palette_labels = Some(labels)
            }
            if colors_offset != 0 {
              if colors_offset < 0 || colors_offset + num_colors * 2 > data.length() {
                return Err(UnexpectedEof)
              }
              let labels : Array[Int] = []
              for i in 0.. return Err(err)
                  Ok(v) => labels.push(v)
                }
              }
              color_labels = Some(labels)
            }
          }
        }
      }
      Ok(CpalTable::{
        version,
        num_colors,
        num_palettes,
        num_color_records,
        color_records,
        color_record_indices,
        palette_flags,
        palette_labels,
        color_labels,
      })
    }
  }
}

///|
/// Get palette colors for the given palette index.
pub fn CpalTable::palette_colors(self : CpalTable, palette_index : Int) -> Array[@paint.Color] {
  if palette_index < 0 || palette_index >= self.num_palettes {
    return []
  }
  let start = self.color_record_indices[palette_index]
  if start < 0 {
    return []
  }
  let end = start + self.num_colors
  if end > self.color_records.length() {
    return []
  }
  let colors : Array[@paint.Color] = []
  for i in start..