/**
 * Copyright 2024 Beijing Baolande Software Corporation
 *
 * 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.
 *
 * Runtime Library Exception to the Apache 2.0 License:
 *
 * As an exception, if you use this Software to compile your source code and
 * portions of this Software are embedded into the binary product as a result,
 * you may redistribute such product without providing attribution as would
 * otherwise be required by Sections 4(a), 4(b) and 4(d) of the License.
 */

package hyperion.objectpool

/**
 * 存储对象的容器
 * 
 * @author yangfuping
 */
public open class PooledObject<T> <: Expirable & Hashable & Equatable<PooledObject<T>> where T <: Hashable & Equatable<T> {
    protected let id: Int64

    private static let idGenerator = AtomicInt64(0)

    private let borrowCount = AtomicUInt64(0)

    private let concurrentUseCount = AtomicUInt64(0)

    private var lastBorrowCount = AtomicUInt64(0)

    private let expirableDelegate = ExpirableItem()

    public init(value: T) {
        id = PooledObject<T>.idGenerator.fetchAdd(1)
        this.valueHolder = value
        createTimeVal = DateTime.now()
    }

    private var valueHolder: T

    public prop value: T {
        get() {
            return valueHolder
        }
    }

    private var createTimeVal: DateTime

    public prop createTime: ?DateTime {
        get() {
            return createTimeVal
        }
    }

    public mut prop idleTimeout: ?Duration {
        get() {
            return expirableDelegate.idleTimeout
        }
        set(value) {
            expirableDelegate.idleTimeout = value
        }
    }

    public func config(poolConfig: PoolConfig) {
        if (let Some(idleTimeout) <- poolConfig.idleTimeout) {
            this.idleTimeout = idleTimeout
        }
    }

    public func cancelExpireTime(): Unit {
        expirableDelegate.cancelExpireTime()
    }

    public func setExpireTimeIfAbsent(): Unit {
        expirableDelegate.setExpireTimeIfAbsent()
    }

    public func setExpireTime(): Unit {
        expirableDelegate.setExpireTime()
    }

    public func isExpired(): Bool {
        return expirableDelegate.isExpired()
    }

    @OverflowWrapping
    public override func hashCode(): Int64 {
        var hashCode = 37 * id
        hashCode = hashCode + valueHolder.hashCode()
        hashCode = hashCode + createTime.hashCode()
        return hashCode
    }

    public operator override func ==(other: PooledObject<T>): Bool {
        if (id != other.id) {
            return false
        }

        if (value != other.value) {
            return false
        }

        if (createTime != other.createTime) {
            return false
        }

        return true
    }

    public operator override func !=(other: PooledObject<T>): Bool {
        if (id != other.id) {
            return true
        }

        if (value != other.value) {
            return true
        }

        if (createTime != other.createTime) {
            return true
        }

        return false
    }

    public func allocate() {
        borrowCount.fetchAdd(1)
        concurrentUseCount.fetchAdd(1)
        cancelExpireTime()
    }

    public func deallocate() {
        concurrentUseCount.fetchSub(1)
    }

    /**
     * 检测两个evict周期之间,连接是否空闲
     */
    public func checkIdleAndSetExpireTime(): Bool {
        let idle = concurrentUseCount.load() == 0
            && borrowCount.load() == lastBorrowCount.load()

        if (idle) {
            // 如果已经设置了超时时间,则沿用原来的超时时间
            setExpireTimeIfAbsent()
        } else {
            lastBorrowCount.store(borrowCount.load())
        }

        return idle
    }
}