package cn.org.bjca.trust.push.message.client

import android.content.Context
import cn.org.bjca.trust.push.common.LogHelper
import cn.org.bjca.trust.push.common.MessageHelper
import cn.org.bjca.trust.push.common.json.GsonImplHelp
import cn.org.bjca.trust.push.enums.ConnAckReturnCode
import cn.org.bjca.trust.push.enums.MsgType
import cn.org.bjca.trust.push.kit.ImClientInterface
import cn.org.bjca.trust.push.message.msg.SendMessage
import org.eclipse.paho.client.mqttv3.*
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit

class ImClient : ImClientInterface {
    private lateinit var connectOptions: ImConnectOptions
    private lateinit var mqttSendClient: MqttClient
    private lateinit var mqttArrivedClient: MqttClient
    private lateinit var options: MqttConnectOptions
    private lateinit var mContext: Context
    private var isConnecting = false
    override fun connect(context: Context, mImConnectOptions: ImConnectOptions) {
        mContext = context
        connectOptions = mImConnectOptions
        init()
        mConnect()
    }

    private fun mConnect() {
        if (isConnect() || isConnecting) return
        isConnecting = true
        Thread {
            try {
                if (!mqttSendClient.isConnected) mqttSendClient.connect(options)
            } catch (e: java.lang.Exception) {
                connectOptions.callback?.connectError(ConnAckReturnCode.CONNECTED)
            }
            try {
                if (!mqttArrivedClient.isConnected) mqttArrivedClient.connect(options)
            } catch (e: java.lang.Exception) {
                connectOptions.callback?.connectError(ConnAckReturnCode.CONNECTED)
            }
            isConnecting = false
        }.start()
    }

    private fun init() {
        if (!::mqttSendClient.isInitialized) {
            mqttSendClient = MqttClient(
                "tcp://${connectOptions.host}:${connectOptions.port}",
                "${connectOptions.clientId}_send",
                MemoryPersistence()
            )
            options = MqttConnectOptions()
            options.isCleanSession = true
            options.userName = connectOptions.clientId
            options.password = "xuqinmin1022".toCharArray()
            options.connectionTimeout = connectOptions.connectTimeOutSecond
            options.keepAliveInterval = connectOptions.pingInterval
            options.isAutomaticReconnect = false
            mqttSendClient.setCallback(object : MqttCallback {
                override fun connectionLost(cause: Throwable?) {
                    reConnect()
                }

                override fun messageArrived(topic: String?, message: MqttMessage) {
                }

                override fun deliveryComplete(token: IMqttDeliveryToken?) {
                }
            })
        }
        if (!::mqttArrivedClient.isInitialized) {
            mqttArrivedClient = MqttClient(
                "tcp://${connectOptions.host}:${connectOptions.port}",
                connectOptions.clientId,
                MemoryPersistence()
            )
            options = MqttConnectOptions()
            options.isCleanSession = true
            options.userName = connectOptions.clientId
            options.password = "xuqinmin1022".toCharArray()
            options.connectionTimeout = connectOptions.connectTimeOutSecond
            options.keepAliveInterval = connectOptions.pingInterval
            options.isAutomaticReconnect = true
            mqttArrivedClient.setCallback(object : MqttCallbackExtended {
                override fun connectionLost(cause: Throwable?) {
                    reConnect()
                }

                override fun messageArrived(topic: String?, message: MqttMessage) {
                    val sendMessage = GsonImplHelp.get()
                        .toObject(String(message.payload, Charsets.UTF_8), SendMessage::class.java)
                    MessageHelper.parseMessage(
                        MsgType.getMsgType(sendMessage.messageType),
                        sendMessage
                    )?.also {
                        connectOptions.callback?.messageArrived(it)
                    }
                }

                override fun deliveryComplete(token: IMqttDeliveryToken?) {
                    LogHelper.e(
                        "----------IM::deliveryComplete-------------"
                    )
                }

                override fun connectComplete(reconnect: Boolean, serverURI: String) {
                    startReconnect()
                    connectOptions.callback?.connected()
                    mqttArrivedClient.subscribe(connectOptions.clientId, 2)
                }
            })
        }
    }

    override fun reConnect() {
        init()
        mConnect()
    }

    override fun disConnect() {
        init()
        mqttArrivedClient.disconnect()
        mqttSendClient.disconnect()
    }

    private lateinit var scheduler: ScheduledExecutorService

    fun startReconnect() {
        if (!::scheduler.isInitialized) {
            scheduler = Executors.newSingleThreadScheduledExecutor()
            scheduler.scheduleAtFixedRate({
                reConnect()
            }, 2, 5, TimeUnit.SECONDS)
        }

    }

    override fun isConnect(): Boolean {
        return if (::mqttSendClient.isInitialized && ::mqttArrivedClient.isInitialized) {
            mqttSendClient.isConnected && mqttArrivedClient.isConnected
        } else {
            false
        }
    }

    override fun isConnecting(): Boolean {
        return false
    }

    override fun sendMessage(msg: SendMessage) {
        if (!isConnect()) {
            connectOptions.callback?.sendError(msg)
            return
        }
        try {
            mqttSendClient.publish(
                "server",
                GsonImplHelp.get().toJson(msg).toByteArray(Charsets.UTF_8),
                2,
                true
            )
            connectOptions.callback?.sendComplete(msg)
        } catch (e: MqttException) {
            LogHelper.e("发送消息失败", e)

            connectOptions.callback?.sendError(msg)
            e.printStackTrace()
        }
    }
}