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

package memorycache

import std.sync.Mutex
import std.collection.HashMap
import std.collection.LinkedList

public class MemoryCache <: Cache {
    private var cache: HashMap<String, String>
    private var capacity: Int64
    private var mtx: Mutex
    private var trackingQueue: LinkedList<String>

    public init(capacity: Int64) {
        if (capacity < 1) {
            throw MemoryCacheException("capacity must be greater than 0")
        }
        this.capacity = capacity
        this.cache = HashMap<String, String>()
        this.mtx = Mutex()
        this.trackingQueue = LinkedList<String>()
    }

    private func trackingRemove(key: String) {
        var node = trackingQueue.firstNode
        while (true) {
            match (node) {
                case Some(v) => if (v.value == key) {
                    trackingQueue.remove(v)
                    break
                } else {
                    node = v.next
                }
                case None => break
            }
        }
    }

    public func contains(key: String): Bool {
        synchronized(mtx) {
            return cache.contains(key)
        }
    }

    public func get(key: String): Option<String> {
        synchronized(mtx) {
            var val = cache.get(key)
            match (val) {
                case Some(v) =>
                    trackingRemove(key)
                    trackingQueue.addLast(key)
                case None => ()
            }
            return val
        }
    }

    public func put(key: String, value: String): Unit {
        synchronized(mtx) {
            match (cache.get(key)) {
                case Some(V) =>
                    cache.add(key, value)
                    trackingRemove(key)
                    trackingQueue.addLast(key)
                case None =>
                    if (cache.size == capacity) {
                        cache.remove(trackingQueue.removeFirst().getOrThrow())
                    }
                    cache.add(key, value)
                    trackingQueue.addLast(key)
            }
        }
    }

    public func remove(key: String): Option<String> {
        synchronized(mtx) {
            trackingRemove(key)
            return cache.remove(key)
        }
    }

    public func clear(): Unit {
        synchronized(mtx) {
            cache.clear()
            trackingQueue.clear()
        }
    }

    public func getCapacity(): Int64 {
        synchronized(mtx) {
            return capacity
        }
    }

    public func setCapacity(capacity: Int64) {
        if (capacity < 1) {
            throw MemoryCacheException("capacity must be greater than 0")
        }
        synchronized(mtx) {
            this.capacity = capacity
            while (cache.size > capacity) {
                var key = trackingQueue.removeLast().getOrThrow()
                cache.remove(key)
            }
        }
    }
}