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

///|
pub struct SparseSecondaryMap[K, V] {
  slots : Map[Int, (Int, V)]
  _key : K?
}

///|
pub fn[K, V] SparseSecondaryMap::new(
  capacity? : Int = 0,
) -> SparseSecondaryMap[K, V] {
  { slots: Map([], capacity~), _key: None }
}

///|
pub fn[K, V] SparseSecondaryMap::default() -> SparseSecondaryMap[K, V] {
  SparseSecondaryMap::new()
}

///|
pub fn[K, V] SparseSecondaryMap::with_capacity(
  capacity : Int,
) -> SparseSecondaryMap[K, V] {
  SparseSecondaryMap::new(capacity~)
}

///|
pub fn[K, V] SparseSecondaryMap::length(self : SparseSecondaryMap[K, V]) -> Int {
  self.slots.length()
}

///|
pub fn[K, V] SparseSecondaryMap::len(self : SparseSecondaryMap[K, V]) -> Int {
  self.length()
}

///|
pub fn[K, V] SparseSecondaryMap::is_empty(
  self : SparseSecondaryMap[K, V],
) -> Bool {
  self.slots.is_empty()
}

///|
pub fn[K, V] SparseSecondaryMap::capacity(
  self : SparseSecondaryMap[K, V],
) -> Int {
  self.slots.capacity()
}

///|
pub fn[K : Key, V] SparseSecondaryMap::contains_key(
  self : SparseSecondaryMap[K, V],
  key : K,
) -> Bool {
  if key.is_null() {
    return false
  }
  let index = key.index()
  if index < 0 {
    return false
  }
  match self.slots.get(index) {
    Some((version, _)) => version == key.version()
    None => false
  }
}

///|
pub fn[K : Key, V] SparseSecondaryMap::contains(
  self : SparseSecondaryMap[K, V],
  key : K,
) -> Bool {
  self.contains_key(key)
}

///|
pub fn[K : Key, V] SparseSecondaryMap::insert(
  self : SparseSecondaryMap[K, V],
  key : K,
  value : V,
) -> V? {
  if key.is_null() {
    return None
  }
  let index = key.index()
  if index < 0 {
    return None
  }
  match self.slots.get(index) {
    Some((version, current)) if version == key.version() => {
      self.slots[index] = (version, value)
      Some(current)
    }
    Some((version, _)) if version_is_older(key.version(), version) => None
    _ => {
      self.slots[index] = (key.version(), value)
      None
    }
  }
}

///|
#alias("_[_]=_")
pub fn[K : Key, V] SparseSecondaryMap::set(
  self : SparseSecondaryMap[K, V],
  key : K,
  value : V,
) -> Unit {
  ignore(self.insert(key, value))
}

///|
pub fn[K : Key, V] SparseSecondaryMap::remove(
  self : SparseSecondaryMap[K, V],
  key : K,
) -> V? {
  if key.is_null() {
    return None
  }
  let index = key.index()
  if index < 0 {
    return None
  }
  match self.slots.get(index) {
    Some((version, value)) if version == key.version() => {
      self.slots.remove(index)
      Some(value)
    }
    _ => None
  }
}

///|
pub fn[K : Key, V] SparseSecondaryMap::get(
  self : SparseSecondaryMap[K, V],
  key : K,
) -> V? {
  if key.is_null() {
    return None
  }
  let index = key.index()
  if index < 0 {
    return None
  }
  match self.slots.get(index) {
    Some((version, value)) if version == key.version() => Some(value)
    _ => None
  }
}

///|
#alias("_[_]")
pub fn[K : Key, V] SparseSecondaryMap::at(
  self : SparseSecondaryMap[K, V],
  key : K,
) -> V {
  match self.get(key) {
    Some(value) => value
    None => abort("invalid sparse secondary key")
  }
}

///|
pub fn[K : Key, V] SparseSecondaryMap::update(
  self : SparseSecondaryMap[K, V],
  key : K,
  updater : (V?) -> V?,
) -> Unit {
  if key.is_null() {
    ignore(updater(None))
    return
  }
  let index = key.index()
  if index < 0 {
    ignore(updater(None))
    return
  }
  match self.slots.get(index) {
    Some((version, current)) if version == key.version() =>
      match updater(Some(current)) {
        Some(value) => self.slots[index] = (version, value)
        None => ignore(self.slots.remove(index))
      }
    Some((version, _)) if version_is_older(key.version(), version) =>
      ignore(updater(None))
    _ =>
      match updater(None) {
        Some(value) => self.slots[index] = (key.version(), value)
        None => ()
      }
  }
}

///|
pub fn[K : Key, V] SparseSecondaryMap::get_or_default(
  self : SparseSecondaryMap[K, V],
  key : K,
  default : V,
) -> V? {
  if key.is_null() {
    return None
  }
  let index = key.index()
  if index < 0 {
    return None
  }
  match self.slots.get(index) {
    Some((version, value)) if version == key.version() => Some(value)
    Some((version, _)) if version_is_older(key.version(), version) => None
    _ => {
      self.slots[index] = (key.version(), default)
      Some(default)
    }
  }
}

///|
pub fn[K : Key, V] SparseSecondaryMap::get_or_init(
  self : SparseSecondaryMap[K, V],
  key : K,
  init : () -> V,
) -> V? {
  if key.is_null() {
    ignore(init())
    return None
  }
  let index = key.index()
  if index < 0 {
    ignore(init())
    return None
  }
  match self.slots.get(index) {
    Some((version, value)) if version == key.version() => Some(value)
    Some((version, _)) if version_is_older(key.version(), version) => {
      ignore(init())
      None
    }
    _ => {
      let value = init()
      self.slots[index] = (key.version(), value)
      Some(value)
    }
  }
}

///|
pub fn[K : Key, V] SparseSecondaryMap::retain(
  self : SparseSecondaryMap[K, V],
  predicate : (K, V) -> Bool,
) -> Unit {
  self.slots.retain(fn(index, pair) {
    predicate(K::from_raw_parts(index, pair.0), pair.1)
  })
}

///|
pub fn[K, V] SparseSecondaryMap::clear(self : SparseSecondaryMap[K, V]) -> Unit {
  self.slots.clear()
}

///|
pub fn[K : Key, V] SparseSecondaryMap::drain(
  self : SparseSecondaryMap[K, V],
) -> Array[(K, V)] {
  let items : Array[(K, V)] = Array::new(capacity=self.length())
  self.slots.each(fn(index, pair) {
    items.push((K::from_raw_parts(index, pair.0), pair.1))
  })
  self.clear()
  items
}

///|
pub fn[K : Key, V] SparseSecondaryMap::to_array(
  self : SparseSecondaryMap[K, V],
) -> Array[(K, V)] {
  let items : Array[(K, V)] = Array::new(capacity=self.length())
  self.slots.each(fn(index, pair) {
    items.push((K::from_raw_parts(index, pair.0), pair.1))
  })
  items
}

///|
pub fn[K : Key, V] SparseSecondaryMap::iter(
  self : SparseSecondaryMap[K, V],
) -> Array[(K, V)] {
  self.to_array()
}

///|
#alias(iterator2, deprecated)
pub fn[K : Key, V] SparseSecondaryMap::iter2(
  self : SparseSecondaryMap[K, V],
) -> Iter2[K, V] {
  let inner = self.slots.iter2()
  Iter2::new(fn() {
    match inner.next() {
      Some((index, pair)) => Some((K::from_raw_parts(index, pair.0), pair.1))
      None => None
    }
  })
}

///|
pub fn[K : Key, V] SparseSecondaryMap::each(
  self : SparseSecondaryMap[K, V],
  visit : (K, V) -> Unit,
) -> Unit {
  self.slots.each(fn(index, pair) {
    visit(K::from_raw_parts(index, pair.0), pair.1)
  })
}

///|
pub fn[K : Key, V] SparseSecondaryMap::keys(
  self : SparseSecondaryMap[K, V],
) -> Array[K] {
  let keys : Array[K] = Array::new(capacity=self.length())
  self.slots.each(fn(index, pair) {
    keys.push(K::from_raw_parts(index, pair.0))
  })
  keys
}

///|
pub fn[K, V] SparseSecondaryMap::values(
  self : SparseSecondaryMap[K, V],
) -> Array[V] {
  let values : Array[V] = Array::new(capacity=self.length())
  self.slots.each(fn(_, pair) { values.push(pair.1) })
  values
}