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

///|
/// Storage/CVT + delta instruction implementations for the TT engine.
///
/// Ported from `fontations/skrifa/src/outline/glyf/hint/engine/{storage.rs,cvt.rs,delta.rs}`
/// (Apache-2.0 OR MIT).
fn tt_scale_funits_to_26_6(value : Int, scale_bits : Int) -> Int {
  // value (font units) * scale_bits (16.16, includes *64) => 26.6
  let v = value.to_int64() * scale_bits.to_int64()
  let adj = 0x8000 - (if v < 0 { 1 } else { 0 })
  ((v + adj.to_int64()) >> 16).to_int()
}

///|
fn TtEngine::op_rs(self : TtEngine) -> Result[Unit, HintError] {
  let loc = match self.value_stack.pop_usize() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  if loc < 0 || loc >= self.storage.length() {
    if self.graphics.is_pedantic {
      Err(InvalidStorageIndex(loc))
    } else {
      self.value_stack.push(0)
    }
  } else {
    self.value_stack.push(self.storage.at(loc))
  }
}

///|
fn TtEngine::op_ws(self : TtEngine) -> Result[Unit, HintError] {
  let value = match self.value_stack.pop() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  let loc = match self.value_stack.pop_usize() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  if loc < 0 || loc >= self.storage.length() {
    if self.graphics.is_pedantic {
      Err(InvalidStorageIndex(loc))
    } else {
      Ok(())
    }
  } else {
    self.storage.set(loc, value)
    Ok(())
  }
}

///|
fn TtEngine::op_wcvtp(self : TtEngine) -> Result[Unit, HintError] {
  let value = match self.value_stack.pop() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  let loc = match self.value_stack.pop_usize() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  if loc < 0 || loc >= self.cvt.length() {
    if self.graphics.is_pedantic {
      Err(InvalidCvtIndex(loc))
    } else {
      Ok(())
    }
  } else {
    self.cvt.set(loc, value)
    Ok(())
  }
}

///|
fn TtEngine::op_wcvtf(self : TtEngine) -> Result[Unit, HintError] {
  let value_funits = match self.value_stack.pop() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  let loc = match self.value_stack.pop_usize() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  if loc < 0 || loc >= self.cvt.length() {
    if self.graphics.is_pedantic {
      Err(InvalidCvtIndex(loc))
    } else {
      Ok(())
    }
  } else {
    let value = tt_scale_funits_to_26_6(
      value_funits,
      self.graphics.retained.scale,
    )
    self.cvt.set(loc, value)
    Ok(())
  }
}

///|
fn TtEngine::op_rcvt(self : TtEngine) -> Result[Unit, HintError] {
  let loc = match self.value_stack.pop() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  if loc < 0 || loc >= self.cvt.length() {
    if self.graphics.is_pedantic {
      Err(InvalidCvtIndex(loc))
    } else {
      self.value_stack.push(0)
    }
  } else {
    self.value_stack.push(self.cvt.at(loc))
  }
}

///|
fn tt_delta_adjust(gs : TtGraphicsState, b0 : Int) -> Int {
  // See FreeType packing scheme.
  let mut b = (b0 & 0xF) - 8
  if b >= 0 {
    b = b + 1
  }
  b * (1 << (6 - gs.retained.delta_shift))
}

///|
fn TtEngine::op_deltap(
  self : TtEngine,
  opcode : Int,
) -> Result[Unit, HintError] {
  let ppem_u = self.graphics.retained.ppem.reinterpret_as_uint()
  let point_count = self.graphics.zp0_zone().points.length()
  let n0 = match self.value_stack.pop() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  if n0 < 0 {
    if self.graphics.is_pedantic {
      return Err(InvalidStackValue(n0))
    } else {
      return Ok(())
    }
  }
  // Each entry consumes 2 stack values.
  let n = n0.min(self.value_stack.length() / 2)
  let bias0 : UInt = (match opcode {
    0x71 => 16
    0x72 => 32
    _ => 0
  }).reinterpret_as_uint()
  let bias = bias0 + self.graphics.retained.delta_base.reinterpret_as_uint()
  let back_compat = self.graphics.backward_compatibility
  let did_iup = self.graphics.did_iup_x && self.graphics.did_iup_y
  for _ in 0.. return Err(e)
      Ok(v) => v
    }
    let b0 = match self.value_stack.pop() {
      Err(e) => return Err(e)
      Ok(v) => v
    }
    if point_ix < 0 || point_ix >= point_count {
      continue
    }
    let c : UInt = ((b0.reinterpret_as_uint() & 0xF0) >> 4) + bias
    if ppem_u == c {
      let adj = tt_delta_adjust(self.graphics, b0)
      if back_compat {
        let touched_y = match self.graphics.zp0_zone().is_touched(point_ix, Y) {
          Err(e) => return Err(e)
          Ok(v) => v
        }
        if !did_iup &&
          (
            (self.graphics.is_composite && self.graphics.freedom_vector.y != 0) ||
            touched_y
          ) {
          match self.graphics.move_point(self.graphics.zp0, point_ix, adj) {
            Err(e) => return Err(e)
            Ok(_) => ()
          }
        }
      } else {
        match self.graphics.move_point(self.graphics.zp0, point_ix, adj) {
          Err(e) => return Err(e)
          Ok(_) => ()
        }
      }
    }
  }
  Ok(())
}

///|
fn TtEngine::op_deltac(
  self : TtEngine,
  opcode : Int,
) -> Result[Unit, HintError] {
  let ppem_u = self.graphics.retained.ppem.reinterpret_as_uint()
  let n0 = match self.value_stack.pop() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  if n0 < 0 {
    if self.graphics.is_pedantic {
      return Err(InvalidStackValue(n0))
    } else {
      return Ok(())
    }
  }
  let n = n0.min(self.value_stack.length() / 2)
  let bias0 : UInt = (match opcode {
    0x74 => 16
    0x75 => 32
    _ => 0
  }).reinterpret_as_uint()
  let bias = bias0 + self.graphics.retained.delta_base.reinterpret_as_uint()
  for _ in 0.. return Err(e)
      Ok(v) => v
    }
    let b0 = match self.value_stack.pop() {
      Err(e) => return Err(e)
      Ok(v) => v
    }
    let c : UInt = ((b0.reinterpret_as_uint() & 0xF0) >> 4) + bias
    if ppem_u == c {
      if cvt_ix < 0 || cvt_ix >= self.cvt.length() {
        if self.graphics.is_pedantic {
          return Err(InvalidCvtIndex(cvt_ix))
        } else {
          continue
        }
      }
      let adj = tt_delta_adjust(self.graphics, b0)
      self.cvt.set(cvt_ix, self.cvt.at(cvt_ix) + adj)
    }
  }
  Ok(())
}

///|
fn TtEngine::op_sdb(self : TtEngine) -> Result[Unit, HintError] {
  let b = match self.value_stack.pop() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  self.graphics.retained.delta_base = b
  Ok(())
}

///|
fn TtEngine::op_sds(self : TtEngine) -> Result[Unit, HintError] {
  let s = match self.value_stack.pop() {
    Err(e) => return Err(e)
    Ok(v) => v
  }
  // FreeType/fontations clamp delta_shift to [0, 6].
  if s < 0 || s > 6 {
    return Err(InvalidStackValue(s))
  }
  self.graphics.retained.delta_shift = s
  Ok(())
}