/*
 * @Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 */

package memorycache

import std.collection.*

public class CustomMap<K, V> where K <: Hashable & Equatable<K> {
    private var map: HashMap<K, V> = HashMap<K, V>()
    private var sequence: ArrayList<K> = ArrayList<K>()

    public func getFirstKey(): K {
        return sequence.get(0).getOrThrow()
    }
    
    public func getLastKey(): K {
        return sequence.get(sequence.size-1).getOrThrow()
    }

    public func hasKey(k: K): Bool { 
        return this.map.contains(k)
    }

    public func each(fn: (K, V) -> Unit) {
        for ((k, v) in this.map) {
            fn(k, v)
        }
    }

    protected func put(key: K, value: V) {
        this.map.add(key, value)
        sequenceAdd(key)
    }

    protected func putAll(elements: HashMap<K, V>) {
        this.map.add(all: elements)
        var iterator = elements.keys().iterator()
        for (i in 0..elements.size) {
            sequenceAdd(iterator.next().getOrThrow())
        }
    }

    public func get(key: K): ?V {
        sequenceAdd(key)
        this.map.get(key)
    }

    public func isEmpty(): Bool {
        this.map.isEmpty()
    }

    public func remove(key: K): Bool {
        if (this.map.remove(key).isSome()) {
            sequenceReduce(key)
            return true
        }
        return false
    }

    public func size(): Int64 {
        this.map.size
    }

    public func clear() {
        this.map.clear()
        sequence.clear()
    }

    public func keys(): EquatableCollection<K> {
        this.map.keys()
    }

    private func getIndex(key: K): Int64 {
        var index: Int64 = 0
        for (i in 0..sequence.size) {
            if (sequence.get(i).hashCode() == key.hashCode()) {
                index = i
                break
            }
        }
        return index
    }
    
    private func sequenceAdd(key: K) {
        if (sequence.contains(key)) {
            sequence.remove(at: getIndex(key))
            sequence.add(key)
        } else {
            sequence.add(key)
        }
    }

    private func sequenceReduce(key: K) {
        if (sequence.contains(key)) {
            sequence.remove(at: getIndex(key))
        }
    }
}