/*
 *  Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 *  This source file is part of the Cangjie project, licensed under Apache-2.0
 *  with Runtime Library Exception.
 *
 *  See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

/**
 * @file base_memory_cache.cj
 * @brief Memory-based cache implementation base class
 *
 */

package stdx.string_intern

import std.collection.{Map, HashMap, Set}
import std.sync.Mutex

/**
 * @brief  Cache implementation base class, which stores cached data in map mode.
 */
internal abstract class BaseMemoryCache<K, V> <: ICache<K, V> where K <: Hashable & Equatable<K> {
    protected let cacheItemMap: HashMap<K, ICacheObj<K, V>> = HashMap<K, ICacheObj<K, V>>()
    protected var lock: Mutex = Mutex()
    protected var cacheConfigs: ?ICacheConfig

    init(cacheConfigs!: ?ICacheConfig = Option<ICacheConfig>.None) {
        this.cacheConfigs = cacheConfigs
    }

    /**
     * Release a specified number of caches: For example, in the LRU cache, the specified number of least used elements are released.
     *
     *  @param numberToReleasee Number of cached elements released
     *  @return Number of cached elements that are actually released. If 10 elements are requested to be released but there are only three elements in the cache, the value 3 is returned.
     */
    public func release(numberToRelease: Int64): Int64

    /**
     * Cache hit post-processing, which is implemented by subclasses
     *
     * @param cacheObj Current hit cache
     */
    protected func adjustCacheAfterHit(cacheObj: ICacheObj<K, V>): Unit

    /**
     * Put a cache object into the cache.
     * <p>
     * @param cobj Cache object
     */
    public func put(cobj: ICacheObj<K, V>): Unit

    /**
     * Reset the cache and clear data.
     */
    public func initializeCache(): Unit

    /**
     * Get a cache object from the cache
     *
     * @param key Cached key value
     * @return If the cache element of the corresponding key exists, the type of Option is returned. Otherwise, Option.None is returned.
     */
    public func get(key: K): Option<ICacheObj<K, V>> {
        synchronized(lock) {
            let valueOP: Option<ICacheObj<K, V>> = cacheItemMap.get(key)
            if (let Some(value) <- valueOP) {
                adjustCacheAfterHit(value)
            }
            return valueOP
        }
    }

    public func putIfAbsent(cobj: ICacheObj<K, V>): ?ICacheObj<K, V> {
        var key: K = cobj.key
        var cobjOp = get(key)
        if (cobjOp.isSome()) {
            return cobjOp
        }
        put(cobj)
        return None
    }

    /**
     * Obtains the size of the current cache.
     * <p>
     * @return Number of elements in the cache
     */
    public func getSize(): Int64 {
        return cacheItemMap.size
    }

    /**
     * Removes an element object whose key is key from the cache.
     * <p>
     * @param key Cache key value
     * @return Whether the element object corresponding to the cache exists
     */
    public open func remove(key: K): Bool {
        synchronized(lock) {
            return !cacheItemMap.remove(key).isNone()
        }
    }

    /**
     * Delete all elements from the cache.
     */
    public open func removeAll(): Unit {
        synchronized(lock) {
            cacheItemMap.clear()
        }
    }

    /**
     * Obtains multiple cache objects based on key values..
     * <p>
     * @param keys
     * @return Multiple objects are stored through a map. The key of the map is the key value, and the value of the map is the cache object.
     */
    public func getCacheObjs(keys: Set<K>): Map<K, Option<ICacheObj<K, V>>> {
        synchronized(lock) {
            let multipleCacheObjMap = HashMap<K, Option<ICacheObj<K, V>>>()
            for (key in keys) {
                multipleCacheObjMap.add(key, cacheItemMap.get(key))
            }
            return multipleCacheObjMap
        }
    }

    /**
     * Cache initialization
     * <p>
     * @param cacheConfigs Definition of Cache Configuration Attributes
     */
    public func initialize(cacheConfigs: ICacheConfig): Unit {
        this.cacheConfigs = cacheConfigs
        cacheItemMap.clear()
        initializeCache()
    }
}