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

///|
/// Color palettes (OpenType `CPAL` table) MVP.
///
/// Ported from `fontations/skrifa/src/color/*` (Apache-2.0 OR MIT).
const COLOR_TAG_CPAL : UInt = 0x4350414C // "CPAL"

///|
fn color_read_u16_be(view : BytesView, offset : Int) -> Int? {
  if offset < 0 || offset + 2 > view.length() {
    None
  } else {
    let b0 = view.at(offset).to_int()
    let b1 = view.at(offset + 1).to_int()
    Some((b0 << 8) | b1)
  }
}

///|
fn color_read_u32_be(view : BytesView, offset : Int) -> UInt? {
  if offset < 0 || offset + 4 > view.length() {
    None
  } else {
    let b0 = view.at(offset).to_uint()
    let b1 = view.at(offset + 1).to_uint()
    let b2 = view.at(offset + 2).to_uint()
    let b3 = view.at(offset + 3).to_uint()
    Some((b0 << 24) | (b1 << 16) | (b2 << 8) | b3)
  }
}

///|
fn color_read_u32_be_int(view : BytesView, offset : Int) -> Int? {
  match color_read_u32_be(view, offset) {
    None => None
    Some(u) => Some(u.to_uint64().to_int())
  }
}

///|
pub struct Color {
  r : Byte
  g : Byte
  b : Byte
  a : Byte
}

///|
pub fn Color::r(self : Color) -> Byte {
  self.r
}

///|
pub fn Color::g(self : Color) -> Byte {
  self.g
}

///|
pub fn Color::b(self : Color) -> Byte {
  self.b
}

///|
pub fn Color::a(self : Color) -> Byte {
  self.a
}

///|
pub struct ColorPalettes {
  priv cpal : BytesView?
  priv num_entries : Int
  priv num_palettes : Int
  priv num_records : Int
  priv color_records_off : Int
  priv indices_off : Int
}

///|
pub fn ColorPalettes::new(font : FontRef) -> ColorPalettes {
  match font.table(COLOR_TAG_CPAL) {
    None =>
      ColorPalettes::{
        cpal: None,
        num_entries: 0,
        num_palettes: 0,
        num_records: 0,
        color_records_off: 0,
        indices_off: 0,
      }
    Some(cpal) => {
      if cpal.length() < 12 {
        return ColorPalettes::{
          cpal: None,
          num_entries: 0,
          num_palettes: 0,
          num_records: 0,
          color_records_off: 0,
          indices_off: 0,
        }
      }
      let num_entries = color_read_u16_be(cpal, 2).unwrap_or(-1)
      let num_palettes = color_read_u16_be(cpal, 4).unwrap_or(-1)
      let num_records = color_read_u16_be(cpal, 6).unwrap_or(-1)
      let records_off = color_read_u32_be_int(cpal, 8).unwrap_or(-1)
      if num_entries < 0 ||
        num_palettes < 0 ||
        num_records < 0 ||
        records_off < 0 {
        return ColorPalettes::{
          cpal: None,
          num_entries: 0,
          num_palettes: 0,
          num_records: 0,
          color_records_off: 0,
          indices_off: 0,
        }
      }
      let indices_off = 12
      let indices_end = indices_off + num_palettes * 2
      let records_end = records_off + num_records * 4
      if indices_end < indices_off || indices_end > cpal.length() {
        return ColorPalettes::{
          cpal: None,
          num_entries: 0,
          num_palettes: 0,
          num_records: 0,
          color_records_off: 0,
          indices_off: 0,
        }
      }
      if records_end < records_off || records_end > cpal.length() {
        return ColorPalettes::{
          cpal: None,
          num_entries: 0,
          num_palettes: 0,
          num_records: 0,
          color_records_off: 0,
          indices_off: 0,
        }
      }
      ColorPalettes::{
        cpal: Some(cpal),
        num_entries,
        num_palettes,
        num_records,
        color_records_off: records_off,
        indices_off,
      }
    }
  }
}

///|
pub fn ColorPalettes::len(self : ColorPalettes) -> Int {
  self.num_palettes
}

///|
pub fn ColorPalettes::entries_per_palette(self : ColorPalettes) -> Int {
  self.num_entries
}

///|
pub fn ColorPalettes::color(
  self : ColorPalettes,
  palette : Int,
  entry : Int,
) -> Color? {
  let cpal = match self.cpal {
    None => return None
    Some(v) => v
  }
  if palette < 0 || palette >= self.num_palettes {
    return None
  }
  if entry < 0 || entry >= self.num_entries {
    return None
  }
  let idx = color_read_u16_be(cpal, self.indices_off + palette * 2).unwrap_or(
    -1,
  )
  if idx < 0 {
    return None
  }
  let rec = idx + entry
  if rec < 0 || rec >= self.num_records {
    return None
  }
  let pos = self.color_records_off + rec * 4
  let b = cpal.at(pos)
  let g = cpal.at(pos + 1)
  let r = cpal.at(pos + 2)
  let a = cpal.at(pos + 3)
  Some(Color::{ r, g, b, a })
}

///|
pub fn FontRef::color_palettes(self : FontRef) -> ColorPalettes {
  ColorPalettes::new(self)
}