/**
 * 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 redis_sdk.client

public open class ClusterConnectionProvider <: Hashable & Equatable<ClusterConnectionProvider> & ToString {
    private static let logger: Logger = LoggerFactory.getLogger("redis-sdk")

    private let id: Int64

    private static let idGenerator = AtomicInt64(0)

    private let cache: ClusterInfoCache

    private let clusterConfig: ClusterRedisClientConfig

    private let initialized = AtomicBool(false)

    public init(clusterConfig: ClusterRedisClientConfig) {
        this(clusterConfig, subscribeMode: false)
    }

    public init(clusterConfig: ClusterRedisClientConfig, subscribeMode!: Bool) {
        this.id = idGenerator.fetchAdd(1)
        this.clusterConfig = clusterConfig
        this.cache = ClusterInfoCache.getClusterInfoCache(clusterConfig, subscribeMode: subscribeMode)
    }

    public open func initialize() {
        if (initialized.compareAndSwap(false, true)) {
            cache.addClusterConnectionProvider(this)
        }
    }

    public open func close() {
        cache.removeClusterConnectionProvider(this)
    }

    public func getRespVersion(): ?ProtocolVersion {
        return cache.getRespVersion()
    }

    public open func getEndpoint(): ClientTcpEndpoint {
        return cache.getConnection()
    }

    public open func getEndpoint(arg: CommandArgs): ClientTcpEndpoint {
        return cache.getConnection(arg)
    }

    public open func getEndpoint(node: HostAndPort): ClientTcpEndpoint {
        return cache.getConnection(node)
    }

    public func getConnectionMap(): HashMap<String, ClientTcpEndpoint> {
        return cache.getNodes()
    }

    public func getConnectionFromSlot(slot: Int64): ClientTcpEndpoint {
        return cache.getConnectionFromSlot(slot)
    }

    public func executeCommand(clientTcpEndPoint: ClientTcpEndpoint, command: RedisCommand): RedisMessage {
        return cache.executeCommand(clientTcpEndPoint, command)
    }

    public func renewSlotCache(): Unit {
        cache.renewClusterSlots(None)
    }

    public func renewSlotCache(clientTcpEndpoint: ClientTcpEndpoint): Unit {
        cache.renewClusterSlots(clientTcpEndpoint)
    }

    public override func toString(): String {
        return "ClusterConnectionProvider{id: ${id}, start nodes: ${clusterConfig.getStartNodes()}}"
    }

    @OverflowWrapping
    public override func hashCode(): Int64 {
        var hashCode = 53 * id
        hashCode = hashCode + clusterConfig.hashCode()
        return hashCode
    }

    public operator override func ==(other: ClusterConnectionProvider): Bool {
        if (id != other.id) {
            return false
        }

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

        return true
    }

    public operator override func !=(other: ClusterConnectionProvider): Bool {
        if (id != other.id) {
            return true
        }

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

        return false
    }
}