package com.mogo.telematic.client.handler

import android.util.Log
import com.mogo.telematic.TelematicHandler
import com.mogo.telematic.client.NettyTcpClient
import com.mogo.telematic.client.listener.NettyClientListener
import com.mogo.telematic.client.status.ConnectState
import com.mogo.telematic.client.status.ConnectState.STATUS_CONNECT_CLOSED
import io.netty.channel.ChannelHandlerContext
import io.netty.channel.ChannelInboundHandlerAdapter
import kotlin.math.max

class ReconnectHandler(
    private val listener: NettyClientListener<*>
) : ChannelInboundHandlerAdapter() {

    private val maxRetries = Int.MAX_VALUE
    @Volatile
    private var retries = 0
    private val baseSleepTimeMs = 3000
    private val maxSleepMs = 10 * 1000

    companion object {
        private const val TAG = "ReconnectHandler"
        private const val MAX_RETRIES_LIMIT = 29
    }

    override fun channelActive(ctx: ChannelHandlerContext) {
        retries = 0
        ctx.fireChannelActive()
    }

    @Throws(Exception::class)
    override fun exceptionCaught(ctx: ChannelHandlerContext, cause: Throwable) {
        Log.e(TAG, "ReconnectHandler#exceptionCaught cause message is:${cause.message}")
        listener.onClientStatusConnectChanged(
            ConnectState.STATUS_CONNECT_ERROR,
            cause.message,
            ctx.channel()
        )
        cause.printStackTrace()
        ctx.close()
    }

    @Throws(Exception::class)
    override fun channelInactive(ctx: ChannelHandlerContext) {
        Log.d(TAG, "ReconnectHandler channelInactive ...")
        if (retries == 0) {
            synchronized(NettyTcpClient::class.java) {
                NettyTcpClient.sSERVER_SN = ""
            }
            listener.onClientStatusConnectChanged(
                STATUS_CONNECT_CLOSED,
                "channelInactive",
                ctx.channel()
            )
        }
        var allowRetry = false
        if (retries < maxRetries) {
            allowRetry = true
        }
        if (allowRetry) {
            var sleepTimeMs: Long
            require(retries >= 0) { "Retries count must greater than 0." }
            if (retries > MAX_RETRIES_LIMIT) {
                retries = MAX_RETRIES_LIMIT
            }
            sleepTimeMs = (baseSleepTimeMs * max(1, 1 shl retries)).toLong()
            if (sleepTimeMs > maxSleepMs) {
                sleepTimeMs = maxSleepMs.toLong()
            }
            Log.d(TAG, "${sleepTimeMs}ms后执行重连操作！")
            TelematicHandler.delayReConnect(sleepTimeMs)
            ++retries
        } else {
            Log.d(TAG, "Try to reconnect to the server Over Retry count:${retries + 1}")
        }
    }
}