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.utils.NamedThreadFactory
import io.github.toggery.jt808.codec.*
import io.github.toggery.jt808.messagebody.B0100
import io.github.toggery.jt808.messagebody.B0102
import io.github.toggery.jt808.messagebody.B8001
import io.github.toggery.jt808.messagebody.HexUtil
import io.netty.buffer.ByteBuf
import io.netty.buffer.UnpooledByteBufAllocator
import io.netty.util.DefaultAttributeMap
import java.util.concurrent.Executors
import java.util.concurrent.atomic.AtomicReference

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

/**
 * 设备状态
 * @author male
 * */
enum class DeviceStatus {
    /**
     * 未鉴权
     * */
    UnAuthenticate,

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

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

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

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

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

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

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

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

    private val attributeMap by lazy {
        DefaultAttributeMap()
    }

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

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

    override fun onConnect() {
        if (deviceStatus.get() == DeviceStatus.UnAuthenticate) {
            tcpManager.disconnect()
            return
        }
        var retryTimes = 0
        var success = false
        val  isRegistered= Settings.deviceId == DeviceUtils.getUniqueDeviceId() && Settings.token.isNotEmpty() && Settings.isRegistered
        if (isRegistered) {
            do {
                success = doAuthenticate()
                retryTimes++
            } while (retryTimes < CONNECT_MAX_RETRY && success)
            if (!success) {
                handleDisconnect(Error.JT808EncodeError)
            }
        } else {
            do {
                success = sendDeviceSignUpMessage()
                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(){
        if (Settings.isRegistered) {
            doAuthenticate()
        } else {
            sendDeviceSignUpMessage()
        }
    }

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

    /**
     * 发送设备注册消息
     * */
    private fun sendDeviceSignUpMessage(): 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("SignUp Message")
    }

    /**
     * 开始心跳
     * */
    private fun startHeartbeat(){

    }

    /**
     * 结束心跳
     * */
    private fun stopHeartbeat(){

    }

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

    private fun doSendMessage(messageType:String) = if (buffs.isNotEmpty()) {
        val byteArray = ByteArray(buffs[0].readableBytes())
        buffs[0].readBytes(byteArray)
        if (BuildConfig.DEBUG) {
            LogUtils.file("sending $messageType:${HexUtil.dump(byteArray)}")
        } else {
            LogUtils.d("sending $messageType:${HexUtil.dump(byteArray)}")
        }
        tcpManager.send(byteArray)
        buffs.removeAt(0)
        true
    } else {
        if (BuildConfig.DEBUG) {
            LogUtils.d("sending $messageType:buffs is empty,Message encode failed!")
        } else {
            LogUtils.file("sending $messageType:buffs is empty,Message encode failed!")
        }
        false
    }

    override fun onDisconnect(error: Error) {
        TODO("Not yet implemented")
    }

    override fun onRegistered(data: Codec<*>) {
        Settings.deviceId = DeviceUtils.getUniqueDeviceId()
        Settings.isRegistered = true
        Settings.token = (data as B8100Codec).newInstance().token
        doAuthenticate()
    }

    override fun onAuthenticated(data: Codec<*>) {
        val codec = (data as B8001Codec).newInstance()
        when(val code = codec.result){
            B8001.RESULT_SUCCESSFUL->{
                deviceStatus.set(DeviceStatus.Authenticated)
            }
        }
    }

    override fun onCommonResponse(data: Codec<*>) {
        val codec = (data as B8001Codec).newInstance()
        when(codec.result){
            B8001.RESULT_SUCCESSFUL->{

            }
        }
    }

}