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

///|
/// Shaping caches (font metadata + feature store).
///
/// Ported from `upstream swash/src/shape/cache.rs`.
pub type Epoch = UInt64

///|
pub struct FontEntry {
  metrics : @swash.MetricsProxy
  charmap : @swash.CharmapProxy
  coord_count : UInt16
  metadata : EngineMetadata
}

///|

///|
pub fn FontEntry::FontEntry(font : @swash.FontRef) -> FontEntry {
  let coords = font.variations().iter().to_array()
  {
    metrics: @swash.MetricsProxy::from_font(font),
    charmap: @swash.CharmapProxy::from_font(font),
    coord_count: coords.length().to_uint16(),
    metadata: EngineMetadata::from_font(font),
  }
}

///|
priv struct FeatureEntry {
  mut epoch : Epoch
  mut id0 : UInt64
  mut id1 : UInt64
  coords : Array[Int32]
  mut tags : (UInt32, UInt32, UInt32, UInt32)
  store : Ref[FeatureStore]
}

///|
priv struct FeatureCache {
  entries : Array[FeatureEntry]
  mut epoch : Epoch
  max_entries : Int32
}

///|

///|
priv enum FeatureCacheEntry {
  New(Ref[FeatureStore])
  Present(Ref[FeatureStore])
}

///|
fn FeatureCache::FeatureCache(max_entries : Int32) -> FeatureCache {
  let max_entries = if max_entries < 1 { 1 } else { max_entries }
  { entries: [], epoch: 0, max_entries, }
}

///|
fn FeatureCache::entry(
  self : FeatureCache,
  id : (UInt64, UInt64),
  coords : ArrayView[Int32],
  has_feature_vars : Bool,
  tags : (UInt32, UInt32, UInt32, UInt32),
) -> FeatureCacheEntry {
  let (found, index) = self.find_entry(id, coords, has_feature_vars, tags)
  let e = self.entries.at(index)
  e.epoch = self.epoch
  self.entries.set(index, e)
  if found {
    Present(e.store)
  } else {
    New(e.store)
  }
}

///|
fn coords_eq(a : ArrayView[Int32], b : Array[Int32]) -> Bool {
  if a.length() != b.length() {
    return false
  }
  for i in 0.. (Bool, Int32) {
  let mut lowest_epoch = self.epoch
  let mut lowest_index = 0
  for i in 0..