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

///|
/// Optical bounds record.
pub struct OpticalBounds {
  left : Int
  top : Int
  right : Int
  bottom : Int
} derive(Eq, Show, ToJson)

///|
/// Parsed opbd table.
pub struct OpbdTable {
  version : Int
  format : Int
  lookup : LookupTable
  data : BytesView
} derive(Show, ToJson)

///|
pub fn OpbdTable::has_data(self : OpbdTable) -> Bool {
  self.version != 0
}

fn read_bounds(
  data : BytesView,
  offset : Int,
) -> Result[OpticalBounds, AatError] {
  if offset < 0 || offset + 8 > data.length() {
    return Err(UnexpectedEof)
  }
  let left = read_i16(data, offset)
  let top = read_i16(data, offset + 2)
  let right = read_i16(data, offset + 4)
  let bottom = read_i16(data, offset + 6)
  match (left, top, right, bottom) {
    (Err(err), _, _, _) => Err(err)
    (_, Err(err), _, _) => Err(err)
    (_, _, Err(err), _) => Err(err)
    (_, _, _, Err(err)) => Err(err)
    (Ok(left), Ok(top), Ok(right), Ok(bottom)) =>
      Ok(OpticalBounds::{ left, top, right, bottom })
  }
}

///|
/// Parse an opbd table.
pub fn OpbdTable::parse(
  data : BytesView,
  num_glyphs : Int,
) -> Result[OpbdTable, AatError] {
  if num_glyphs < 0 {
    return Err(InvalidFormat)
  }
  let version = read_i32(data, 0)
  let format = read_u16_int(data, 4)
  match (version, format) {
    (Err(err), _) => Err(err)
    (_, Err(err)) => Err(err)
    (Ok(version), Ok(format)) => {
      if format != 0 && format != 1 {
        return Err(InvalidFormat)
      }
      let lookup_offset = 6
      let lookup = match parse_lookup(data, lookup_offset, num_glyphs) {
        Err(err) => return Err(err)
        Ok(value) => value
      }
      Ok(OpbdTable::{ version, format, lookup, data })
    }
  }
}

///|
/// Resolve optical bounds for a glyph.
pub fn OpbdTable::bounds(
  self : OpbdTable,
  glyph : UInt,
  contour_point? : (UInt, Int) -> (Int, Int)? = (_, _) => None,
) -> Result[OpticalBounds?, AatError] {
  let offset = match self.lookup.value_for(glyph) {
    None => return Ok(None)
    Some(value) => value.reinterpret_as_int()
  }
  if offset <= 0 {
    return Ok(None)
  }
  let bounds = match read_bounds(self.data, offset) {
    Err(err) => return Err(err)
    Ok(value) => value
  }
  if self.format == 0 {
    return Ok(Some(bounds))
  }
  if self.format == 1 {
    let contour_point = contour_point
    if bounds.left < 0 || bounds.top < 0 || bounds.right < 0 || bounds.bottom < 0 {
      return Ok(None)
    }
    let left = contour_point(glyph, bounds.left)
    let top = contour_point(glyph, bounds.top)
    let right = contour_point(glyph, bounds.right)
    let bottom = contour_point(glyph, bounds.bottom)
    match (left, top, right, bottom) {
      (Some((left, _)), Some((_, top)), Some((right, _)), Some((_, bottom))) =>
        Ok(Some(OpticalBounds::{ left, top, right, bottom }))
      _ => Ok(None)
    }
  } else {
    Ok(None)
  }
}