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

///|
/// Internal "small" style collection types.
///
/// Ported from `fontations/skrifa/src/collections.rs` (Apache-2.0 OR MIT).
///
/// NOTE: Upstream uses const-generics for inline capacity. In MoonBit we keep
/// `inline_cap` as a runtime parameter; this preserves behavior while still
/// allowing the inline/heap split used by later ports.
priv enum Storage[T] {
  Inline(FixedArray[T], Int)
  Heap(Array[T])
}

///|
/// A growable vector type with a logical "inline" storage mode.
///
/// Unlike upstream, `inline_cap` is a runtime value.
priv struct SmallVec[T] {
  storage : Storage[T]
  inline_cap : Int
}

///|
fn[T : Default] SmallVec::SmallVec(inline_cap : Int) -> SmallVec[T] {
  let buf = FixedArray::make(inline_cap, T::default())
  { storage: Inline(buf, 0), inline_cap }
}

///|
fn[T] SmallVec::with_len(len : Int, value : T, inline_cap : Int) -> SmallVec[T] {
  if len <= inline_cap {
    let buf = FixedArray::make(inline_cap, value)
    { storage: Inline(buf, len), inline_cap }
  } else {
    let vec = Array::make(len, value)
    { storage: Heap(vec), inline_cap }
  }
}

///|
fn[T] SmallVec::len(self : SmallVec[T]) -> Int {
  match self.storage {
    Inline(_buf, len) => len
    Heap(vec) => vec.length()
  }
}

///|
fn[T] SmallVec::is_inline(self : SmallVec[T]) -> Bool {
  match self.storage {
    Inline(_, _) => true
    Heap(_) => false
  }
}

///|
fn[T] SmallVec::as_view(self : SmallVec[T]) -> ArrayView[T] {
  match self.storage {
    Inline(buf, len) => buf[0:len]
    Heap(vec) => vec.op_as_view()
  }
}

///|
fn[T] SmallVec::to_array(self : SmallVec[T]) -> Array[T] {
  match self.storage {
    Inline(buf, len) => {
      let out = Array::new(capacity=len)
      for i in 0.. vec.copy()
  }
}

///|
fn[T] SmallVec::clear(self : SmallVec[T]) -> SmallVec[T] {
  match self.storage {
    Inline(buf, _len) =>
      { storage: Inline(buf, 0), inline_cap: self.inline_cap }
    Heap(vec) => {
      vec.clear()
      { storage: Heap(vec), inline_cap: self.inline_cap }
    }
  }
}

///|
fn[T] SmallVec::try_reserve(
  self : SmallVec[T],
  additional : Int,
) -> (SmallVec[T], Bool) {
  match self.storage {
    Inline(buf, len) => {
      let need = len + additional
      if need > self.inline_cap {
        let vec = Array::new(capacity=need)
        for i in 0.. {
      vec.reserve_capacity(vec.length() + additional)
      ({ storage: Heap(vec), inline_cap: self.inline_cap }, true)
    }
  }
}

///|
fn[T] SmallVec::push(self : SmallVec[T], value : T) -> SmallVec[T] {
  match self.storage {
    Inline(buf, len) =>
      if len + 1 > self.inline_cap {
        let vec = Array::new(capacity=len + 1)
        for i in 0.. {
      vec.push(value)
      { storage: Heap(vec), inline_cap: self.inline_cap }
    }
  }
}

///|
fn[T] SmallVec::pop(self : SmallVec[T]) -> (SmallVec[T], T?) {
  match self.storage {
    Inline(buf, len) =>
      if len > 0 {
        let v = buf.at(len - 1)
        (
          { storage: Inline(buf, len - 1), inline_cap: self.inline_cap },
          Some(v),
        )
      } else {
        (self, None)
      }
    Heap(vec) => {
      let v = vec.pop()
      ({ storage: Heap(vec), inline_cap: self.inline_cap }, v)
    }
  }
}

///|
fn[T] SmallVec::truncate(self : SmallVec[T], new_len : Int) -> SmallVec[T] {
  match self.storage {
    Inline(buf, len) => {
      let next_len = if new_len < len { new_len } else { len }
      { storage: Inline(buf, next_len), inline_cap: self.inline_cap }
    }
    Heap(vec) => {
      vec.truncate(new_len)
      { storage: Heap(vec), inline_cap: self.inline_cap }
    }
  }
}

///|
fn[T] SmallVec::get(self : SmallVec[T], i : Int) -> T? {
  match self.storage {
    Inline(buf, len) => if i >= 0 && i < len { Some(buf.at(i)) } else { None }
    Heap(vec) => vec.get(i)
  }
}

///|
fn[T] SmallVec::set(self : SmallVec[T], i : Int, v : T) -> SmallVec[T] {
  match self.storage {
    Inline(buf, len) => {
      if i >= 0 && i < len {
        buf.set(i, v)
      } else {
        // Keep behavior simple: ignore out-of-range writes (only used in tests).
        i |> ignore
      }
      { storage: Inline(buf, len), inline_cap: self.inline_cap }
    }
    Heap(vec) => {
      if i >= 0 && i < vec.length() {
        vec.set(i, v)
      } else {
        i |> ignore
      }
      { storage: Heap(vec), inline_cap: self.inline_cap }
    }
  }
}

///|
test "choose_inline" {
  let vec = SmallVec::with_len(4, 0, 4)
  inspect(vec.is_inline(), content="true")
  inspect(vec.len(), content="4")
}

///|
test "choose_heap" {
  let vec = SmallVec::with_len(5, 0, 4)
  inspect(vec.is_inline(), content="false")
  inspect(vec.len(), content="5")
}

///|
test "store_and_read_inline" {
  let mut vec = SmallVec::with_len(8, 0, 8)
  for i in 0..