package com.waytous.anticollision.tcp

import com.blankj.utilcode.util.DeviceUtils
import com.blankj.utilcode.util.LogUtils
import com.waytous.anticollision.BuildConfig
import com.waytous.anticollision.config.DeviceConfig
import com.waytous.anticollision.config.Settings
import com.waytous.anticollision.listener.SessionListener
import com.waytous.anticollision.utils.ConnectStatus
import com.waytous.anticollision.utils.Error
import com.waytous.anticollision.utils.NamedThreadFactory
import com.waytous.anticollision.utils.SignInStatus
import io.github.toggery.jt808.codec.Message
import io.github.toggery.jt808.codec.MessageMetadata
import io.github.toggery.jt808.messagebody.*
import io.netty.buffer.ByteBuf
import io.netty.buffer.UnpooledByteBufAllocator
import io.netty.util.DefaultAttributeMap
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.locks.ReentrantLock

/**
 * 最大尝试链接次数
 * */
const val CONNECT_MAX_RETRY = 3

/**
 * 设备状态
 * @author male
 * */
internal enum class DeviceStatus {
    /**
     * 未注册
     * */
    Unregistered,

    /**
     * 注册中
     * */
    Registering,

    /**
     * 已册中
     * */
    Registered,

    /**
     * 未鉴权
     * */
    UnAuthenticated,

    /**
     * 正在鉴权
     * */
    Authenticating,

    /**
     * 已鉴权
     * */
    Authenticated
}

internal class Schedule {

    companion object {
        const val tcpReadIntervalSecs: Long = 2 * 60 * 1000  // 2 seconds
        const val tcpConnectTimeoutSecs = 40 * 1000       // 40 seconds
        const val signInTimeoutSecs: Long = 60 * 1000       // 60 seconds
        const val heartbeatInterval = 2 * 60 * 1000    // 120 seconds
    }
}

/**
 * jt808网络会话管理
 * @author male
 * */
class Session : ConnectListener, SyncMessageListener<AbstractToStringJoiner> {

    /**
     * 消息内容解析器
     * */
    private val mParser by lazy {
        SyncParser(this)
    }

    /**
     * 开启新线程，将接收到的字节流交给消息内容解析器解析
     * */
    private val parseExecutor by lazy {
        Executors.newSingleThreadExecutor(NamedThreadFactory("Parser"))
    }

    /**
     * 设备状态
     * */
    private val deviceStatus by lazy {
        AtomicReference(DeviceStatus.UnAuthenticated)
    }

    /**
     * tcp链接管理者
     * */
    private val tcpManager by lazy {
        TcpManager(this, 2)
    }

    private val buffs by lazy {
        mutableListOf<ByteBuf>()
    }

    private val listeners by lazy {
        mutableSetOf<SessionListener>()
    }

    private val attributeMap by lazy {
        DefaultAttributeMap()
    }

    private val scheduler by lazy {
        Executors.newScheduledThreadPool(1, NamedThreadFactory("Heartbeat"))
    }

    private var scheduleFutureTask: ScheduledFuture<*>? = null

    private val mOperateLock by lazy {
        ReentrantLock()
    }

    private val mOperateCondition by lazy {
        mOperateLock.newCondition()
    }

    override fun onDataReceived(buf: ByteBuf) {
        parseExecutor.execute { mParser.parse(buf) }
    }

    override fun onConnect() {
        if (deviceStatus.get() == DeviceStatus.UnAuthenticated) {
            tcpManager.disconnect()
            return
        }
        tryLogin(::doDeviceSignUp)
    }

    /**
     * 设备尝试登录
     * */
    private fun tryLogin(block: () -> Boolean) {
        var retryTimes = 0
        var success: Boolean
        do {
            success = block()
            retryTimes++
        } while (retryTimes < CONNECT_MAX_RETRY && !success)
        if (!success) {
            handleDisconnect(Error.JT808EncodeError)
        }
    }

    /**
     * 错误处理逻辑
     * */
    private fun handleDisconnect(error: Error) {
        when (error) {
            Error.IOError, Error.StreamClosed, Error.Timeout, Error.NotConnected -> {

            }
            else -> {}
        }
    }

    /**
     * 设备登录
     * */
    fun login(): SignInStatus {
        mOperateLock.lock()
        try {
            deviceStatus.set(DeviceStatus.Registering)
            tcpManager.connect(DeviceConfig.HostConfig.host, DeviceConfig.HostConfig.port, Schedule.tcpConnectTimeoutSecs)
            return if (mOperateCondition.await(Schedule.signInTimeoutSecs, TimeUnit.MILLISECONDS)) {
                when (deviceStatus.get()) {
                    DeviceStatus.Authenticated -> {
                        SignInStatus.SignIn
                    }
                    else -> {
                        SignInStatus.SignOut
                    }
                }
            } else {
                SignInStatus.SignOut
            }
        } catch (e: InterruptedException) {
            if (BuildConfig.DEBUG) {
                LogUtils.e("登录线程被中断，被中断线程：${Thread.currentThread().name},中断线程：${e.printStackTrace()}")
            } else {
                LogUtils.file("登录线程被中断，被中断线程：${Thread.currentThread().name},中断线程：${e.printStackTrace()}")
            }
            return SignInStatus.SignOut
        } finally {
            mOperateLock.unlock()
        }
    }

    /**
     * 添加监听器
     * */
    fun addSessionListener(listener: SessionListener) {
        synchronized(listeners) {
            listeners.add(listener)
        }
    }

    /**
     * 删除监听器
     * */
    fun removeSessionListener(listener: SessionListener) {
        synchronized(listeners) {
            listeners.remove(listener)
        }
    }

    private fun notifyConnectStatus(status: ConnectStatus) {
        if (deviceStatus.get() != DeviceStatus.Authenticated) {
            return
        }
        synchronized(listeners) {
            listeners.forEach {
                it.onConnectStatusChanged(status)
            }
        }
    }

    private fun notifySignInStatus(status: SignInStatus, error: Error) {
        synchronized(listeners) {
            listeners.forEach {
                if (status == SignInStatus.SignIn) {
                    it.onUserSignIn()
                } else {
                    it.onUserSignOut(error)
                }
            }
        }
    }

    /**
     * 设备鉴权
     * */
    private fun doAuthenticate(): Boolean {
        val b0102 = B0102().apply {
            token = Settings.token
        }
        val request = Message.of(0x0102, b0102).apply {
            simNo = DeviceConfig.phoneNumber
        }
        Message.encode(
            request, MessageMetadata.inbounds(), attributeMap,
            UnpooledByteBufAllocator.DEFAULT::buffer, buffs::add
        )
        deviceStatus.set(DeviceStatus.Authenticating)
        return doSendMessage("设备鉴权")
    }

    /**
     * 发送设备注册消息
     * */
    private fun doDeviceSignUp(): Boolean {
        Settings.isRegistered = false
        Settings.deviceId = ""
        val b0100 = B0100().apply {
            province = DeviceConfig.province
            city = DeviceConfig.city
            model = DeviceUtils.getModel()
            plateColor = B0100.PLATE_COLOR_YELLOW
            maker = DeviceUtils.getManufacturer()
            id = DeviceUtils.getUniqueDeviceId()
        }
        val request = Message.of(0x0100, b0100).apply {
            simNo = DeviceConfig.phoneNumber
        }
        Message.encode(
            request, MessageMetadata.inbounds(), attributeMap,
            UnpooledByteBufAllocator.DEFAULT::buffer, buffs::add
        )
        return doSendMessage("设备注册")
    }

    /**
     * 开始心跳
     * */
    private fun startHeartbeat() {
        scheduleFutureTask = scheduler.scheduleAtFixedRate(
            ::sendPing,
            0,
            Schedule.tcpReadIntervalSecs,
            TimeUnit.MILLISECONDS
        )
    }

    /**
     * 结束心跳
     * */
    private fun stopHeartbeat() {
        if (scheduleFutureTask?.isDone == false) {
            scheduleFutureTask?.cancel(true)
        }
    }

    /**
     * 发送心跳
     * */
    private fun sendPing() {
        val request = Message.of(0x0002).apply {
            simNo = DeviceConfig.phoneNumber
        }
        Message.encode(
            request, MessageMetadata.inbounds(), attributeMap,
            UnpooledByteBufAllocator.DEFAULT::buffer, buffs::add
        )
        doSendMessage("发送心跳")
    }

    /**
     * 发送心跳
     * */
    private fun doSendMessage(name: String) = if (buffs.isNotEmpty()) {
        synchronized(buffs) {
            val byteArray = ByteArray(buffs[0].readableBytes())
            buffs[0].readBytes(byteArray)
            if (BuildConfig.DEBUG) {
                LogUtils.d("【$name】:${HexUtil.dump(byteArray)}")
            } else {
                LogUtils.file("【$name】:${HexUtil.dump(byteArray)}")
            }
            tcpManager.send(byteArray)
            buffs.removeAt(0)
        }
        true
    } else {
        if (BuildConfig.DEBUG) {
            LogUtils.e("【$name】:buffs is empty,Message encode failed!")
        } else {
            LogUtils.file("【$name】:buffs is empty,Message encode failed!")
        }
        false
    }

    override fun onDisconnect(error: Error) {
        stopHeartbeat()
        if (BuildConfig.DEBUG) {
            LogUtils.e(error.reason)
        } else {
            LogUtils.file(error.reason)
        }
    }

    override fun onSignUp(payload: AbstractToStringJoiner) {
        Settings.deviceId = DeviceUtils.getUniqueDeviceId()
        Settings.isRegistered = true
        Settings.token = (payload as B8100).token
        deviceStatus.set(DeviceStatus.Registered)
        tryLogin(::doAuthenticate)
    }

    override fun onCommonResponse(payload: AbstractToStringJoiner) {
        val messageBody = (payload as B8001)
        when (messageBody.replyId) {
            0x0100 -> {
                when (messageBody.result) {
                    B8001.RESULT_SUCCESSFUL -> {
                        deviceStatus.set(DeviceStatus.Registered)
                        if (BuildConfig.DEBUG) {
                            LogUtils.d("设备注册成功！")
                        } else {
                            LogUtils.file("设备注册成功！")
                        }
                    }
                    B8001.RESULT_FAILED -> {
                        deviceStatus.set(DeviceStatus.Unregistered)
                        if (BuildConfig.DEBUG) {
                            LogUtils.e("设备注册失败！")
                        } else {
                            LogUtils.file("设备注册失败！")
                        }
                    }
                    B8001.RESULT_WRONG -> {
                        deviceStatus.set(DeviceStatus.Unregistered)
                        if (BuildConfig.DEBUG) {
                            LogUtils.e("设备注册消息有误！")
                        } else {
                            LogUtils.file("设备注册消息有误！")
                        }
                    }
                }
            }
            0x0102 -> {
                when (messageBody.result) {
                    B8001.RESULT_SUCCESSFUL -> {
                        if (deviceStatus.get() == DeviceStatus.Authenticating) {
                            deviceStatus.set(DeviceStatus.Authenticated)
                            startHeartbeat()
                            if (BuildConfig.DEBUG) {
                                LogUtils.d("设备鉴权成功！")
                            } else {
                                LogUtils.file("设备鉴权成功！")
                            }
                        } else {
                            deviceStatus.set(DeviceStatus.UnAuthenticated)
                            if (BuildConfig.DEBUG) {
                                LogUtils.e("设备鉴权失败！")
                            } else {
                                LogUtils.file("设备鉴权失败！")
                            }
                        }
                    }
                    B8001.RESULT_FAILED -> {
                        deviceStatus.set(DeviceStatus.UnAuthenticated)
                        if (BuildConfig.DEBUG) {
                            LogUtils.e("设备鉴权失败！")
                        } else {
                            LogUtils.file("设备鉴权失败！")
                        }
                    }
                    B8001.RESULT_WRONG -> {
                        deviceStatus.set(DeviceStatus.UnAuthenticated)
                        if (BuildConfig.DEBUG) {
                            LogUtils.e("设备鉴权消息有误！")
                        } else {
                            LogUtils.file("设备鉴权消息有误！")
                        }
                    }
                }
            }
            0x0002 -> {
                when (messageBody.result) {
                    B8001.RESULT_SUCCESSFUL -> {
                        if (BuildConfig.DEBUG) {
                            LogUtils.d("心跳响应成功！")
                        } else {
                            LogUtils.file("心跳响应成功！")
                        }
                    }
                    B8001.RESULT_FAILED -> {
                            if (BuildConfig.DEBUG) {
                                LogUtils.e("心跳响应失败！")
                            } else {
                                LogUtils.file("心跳响应失败！")
                            }
                    }
                    B8001.RESULT_WRONG -> {
                        if (BuildConfig.DEBUG) {
                            LogUtils.e("心跳消息有误！")
                        } else {
                            LogUtils.file("心跳消息有误！")
                        }
                    }
                }
            }
        }
    }

    override fun onDeviceSetting(payload: AbstractToStringJoiner) {
        val messageBody = payload as B8103
        Settings.x0001 = messageBody.x0001
        Settings.x0002 = messageBody.x0002
        Settings.x0003 = messageBody.x0003
        Settings.x0010 = messageBody.x0010
        Settings.x0011 = messageBody.x0011
        Settings.x0012 = messageBody.x0012
        Settings.x0013 = messageBody.x0013
        Settings.x0014 = messageBody.x0014
        Settings.x0015 = messageBody.x0015
        Settings.x0016 = messageBody.x0016
        Settings.x0017 = messageBody.x0017
        Settings.x0018 = messageBody.x0018
        Settings.x001A = messageBody.x001A
        Settings.x001B = messageBody.x001B
    }

}