///|
/// ICC tag 数据体的解析。
///
/// 每个 tag 数据以 4 字节类型签名开头,紧接着 4 字节保留位,之后是类型相关内容。
/// 本模块只实现 matrix/TRC 变换所需的类型:
///
/// - `XYZ `:三刺激值(matrix 列、白点、黑点)
/// - `curv`:一维曲线(用查找表或单一 gamma 值表示)
/// - `para`:参数化曲线(sRGB 等常用曲线,用 1..4 个参数表示)
///
/// 其余类型(`mft1`/`mft2`/`mAB`/`mBA`/`text` 等)不在本模块范围,
/// 由调用方按需处理。

///|
/// tag 类型签名与数据体之间固定有 4 字节保留位。
let tag_type_header_size : Int32 = 8

///|
/// 解析 tag 数据体的公共前置:返回 (类型签名, 数据体起始偏移)。
pub fn tag_type_and_data(
  data : Bytes,
  entry : IccTagEntry,
) -> (String, Int32) raise IccError {
  if entry.size < tag_type_header_size {
    raise BadTagData(
      tag=entry.signature,
      reason="tag 数据不足 \{tag_type_header_size} 字节,无法容纳类型签名",
    )
  }
  let r = BigEndianReader::new(data)
  r.seek(entry.offset)
  let signature = r.read_signature()
  // 跳过 4 字节保留位
  r.skip(4)
  (signature, r.position())
}

///|
/// 解析 `XYZ ` 类型 tag,返回三刺激值。
///
/// 数据体为 3 个 `s15Fixed16Number`。用于 `rXYZ`/`gXYZ`/`bXYZ`/`wtpt`/`bkpt`。
pub fn parse_xyz_tag(
  data : Bytes,
  entry : IccTagEntry,
) -> XyzNumber raise IccError {
  let (type_sig, body_at) = tag_type_and_data(data, entry)
  if type_sig != "XYZ " {
    raise BadTagData(
      tag=entry.signature,
      reason="期望类型 \"XYZ \",实际 \"\{type_sig}\"",
    )
  }
  let r = BigEndianReader::new(data)
  r.seek(body_at)
  let x = r.read_s15f16()
  let y = r.read_s15f16()
  let z = r.read_s15f16()
  { x, y, z, }
}

///|
/// 一维曲线(TRC)。
///
/// ICC 用两种方式表示:`curv` 类型(gamma 值或查找表)与 `para` 类型(参数方程)。
/// 本库统一求值为 `evaluate`。
pub enum ToneCurve {
  /// 单一 gamma 值:`y = x^gamma`。
  Gamma(Double)
  /// 查找表:在表项间线性插值,输入域 [0,1]。
  Table(Array[Double])
  /// 参数化曲线(`para` 类型),参数个数 1..4。
  Parametric(Array[Double])
}

///|
/// 解析 `curv` 类型 tag。
///
/// 数据体:u32 表项数 N,随后 N 个 u16(0..65535 映射到 [0,1])。
/// N == 0 表示恒等曲线;N == 1 表示 `y = x^(值/256)`。
pub fn parse_curv_tag(
  data : Bytes,
  entry : IccTagEntry,
) -> ToneCurve raise IccError {
  let (type_sig, body_at) = tag_type_and_data(data, entry)
  if type_sig != "curv" {
    raise BadTagData(
      tag=entry.signature,
      reason="期望类型 \"curv\",实际 \"\{type_sig}\"",
    )
  }
  let r = BigEndianReader::new(data)
  r.seek(body_at)
  let count = r.read_u32()
  match count {
    0 => ToneCurve::Table([])
    1 => {
      let raw = r.read_u16()
      ToneCurve::Gamma(raw.to_double() / 256.0)
    }
    _ => {
      // 表项是 u16,需要 2*count 字节
      if r.remaining() < count * 2 {
        raise BadTagData(
          tag=entry.signature,
          reason="曲线声明 \{count} 项,但剩余字节不足",
        )
      }
      let table : Array[Double] = []
      for i = 0; i < count; i = i + 1 {
        table.push(r.read_u16().to_double() / 65535.0)
      }
      ToneCurve::Table(table)
    }
  }
}

///|
/// 解析 `para` 类型 tag。
///
/// 数据体:u16 函数类型 (0..4),u16 保留位,随后 s15Fixed16 参数。
/// 参数个数由函数类型决定:0→1 个, 1→3 个, 2→4 个, 3→5 个, 4→7 个。
pub fn parse_para_tag(
  data : Bytes,
  entry : IccTagEntry,
) -> ToneCurve raise IccError {
  let (type_sig, body_at) = tag_type_and_data(data, entry)
  if type_sig != "para" {
    raise BadTagData(
      tag=entry.signature,
      reason="期望类型 \"para\",实际 \"\{type_sig}\"",
    )
  }
  let r = BigEndianReader::new(data)
  r.seek(body_at)
  let function_type = r.read_u16()
  r.skip(2)
  let param_count = match function_type {
    0 => 1
    1 => 3
    2 => 4
    3 => 5
    4 => 7
    _ =>
      raise BadTagData(
        tag=entry.signature,
        reason="不支持的 para 函数类型 \{function_type}",
      )
  }
  let params : Array[Double] = []
  for i = 0; i < param_count; i = i + 1 {
    params.push(r.read_s15f16())
  }
  ToneCurve::Parametric(params)
}

///|
/// 按 tag 类型自动分派解析曲线(`curv` 或 `para`)。
pub fn parse_tone_curve(
  data : Bytes,
  entry : IccTagEntry,
) -> ToneCurve raise IccError {
  let (type_sig, _) = tag_type_and_data(data, entry)
  match type_sig {
    "curv" => parse_curv_tag(data, entry)
    "para" => parse_para_tag(data, entry)
    _ =>
      raise BadTagData(
        tag=entry.signature,
        reason="不支持的曲线类型 \"\{type_sig}\"(仅支持 curv 与 para)",
      )
  }
}

///|
/// 把输入限制在 [0,1]。
fn clamp01(x : Double) -> Double {
  if x < 0.0 {
    0.0
  } else if x > 1.0 {
    1.0
  } else {
    x
  }
}

///|
/// 对曲线求值。
///
/// - `Gamma(g)`:`x^g`;
/// - `Table(t)`:在表项间线性插值,表长 1 时视为常量;
/// - `Parametric(p)`:按 ICC 定义的 5 种函数求值。
pub fn ToneCurve::evaluate(self : ToneCurve, x : Double) -> Double {
  let v = clamp01(x)
  match self {
    Gamma(g) => if g == 1.0 { v } else { @math.pow(v, g) }
    Table(t) => {
      let n = t.length()
      if n == 0 {
        // 空表表示恒等
        v
      } else if n == 1 {
        t[0]
      } else {
        // 均匀采样:索引 = v * (n-1),在相邻项间线性插值
        let pos = v * (n - 1).to_double()
        let i = pos.to_int()
        if i >= n - 1 {
          t[n - 1]
        } else {
          let frac = pos - i.to_double()
          t[i] * (1.0 - frac) + t[i + 1] * frac
        }
      }
    }
    Parametric(p) => evaluate_parametric(p, v)
  }
}

///|
/// 按 ICC.1 表 68 求值参数化曲线。
///
/// 参数含义:`g` gamma、`a`、`b`、`c`、`d`、`e`、`f`,断点为 `x = d` 或 `x = -b/a`。
fn evaluate_parametric(p : Array[Double], x : Double) -> Double {
  let n = p.length()
  if n == 0 {
    return x
  }
  let g = p[0]
  match n {
    1 => @math.pow(x, g)
    3 => {
      // Y = (aX + b)^g  for X >= -b/a
      // Y = 0           for X <  -b/a
      let a = p[1]
      let b = p[2]
      let breakpoint = if a == 0.0 { 0.0 } else { -b / a }
      if x >= breakpoint {
        @math.pow(a * x + b, g)
      } else {
        0.0
      }
    }
    4 => {
      // Y = (aX + b)^g  for X >= -b/a
      // Y = cX          for X <  -b/a
      let a = p[1]
      let b = p[2]
      let c = p[3]
      let breakpoint = if a == 0.0 { 0.0 } else { -b / a }
      if x >= breakpoint {
        @math.pow(a * x + b, g)
      } else {
        c * x
      }
    }
    5 => {
      // Y = (aX + b)^g + e  for X >= d
      // Y = cX + f          for X <  d
      // 该形式只给了 e 与 f 中的 e(参数为 g,a,b,c,d,e);f 取 0。
      let a = p[1]
      let b = p[2]
      let c = p[3]
      let d = p[4]
      if x >= d {
        @math.pow(a * x + b, g) + p[5]
      } else {
        c * x
      }
    }
    7 => {
      // Y = (aX + b)^g + e  for X >= d
      // Y = cX + f          for X <  d
      let a = p[1]
      let b = p[2]
      let c = p[3]
      let d = p[4]
      let e = p[5]
      let f = p[6]
      if x >= d {
        @math.pow(a * x + b, g) + e
      } else {
        c * x + f
      }
    }
    _ => x
  }
}