package com.casic.common.detector.gd.utils.tcp

import android.util.Log
import io.netty.bootstrap.Bootstrap
import io.netty.buffer.ByteBuf
import io.netty.buffer.Unpooled
import io.netty.channel.AdaptiveRecvByteBufAllocator
import io.netty.channel.Channel
import io.netty.channel.ChannelFuture
import io.netty.channel.ChannelFutureListener
import io.netty.channel.ChannelInitializer
import io.netty.channel.ChannelOption
import io.netty.channel.nio.NioEventLoopGroup
import io.netty.channel.socket.SocketChannel
import io.netty.channel.socket.nio.NioSocketChannel
import io.netty.handler.codec.bytes.ByteArrayDecoder
import io.netty.handler.codec.bytes.ByteArrayEncoder
import io.netty.handler.timeout.IdleStateHandler
import java.util.concurrent.TimeUnit

class SocketClient(builder: Builder) {

    private val kTag = "SocketClient"
    private val hostname = builder.hostname
    private val port = builder.port
    private val timeout = builder.timeout
    private val listener = builder.listener

    private var bootstrap: Bootstrap
    private var channel: Channel? = null

    class Builder {
        lateinit var hostname: String
        var port = 0
        var timeout = 0L
        lateinit var listener: OnSocketConnectListener

        fun setHostname(hostname: String): Builder {
            this.hostname = hostname
            return this
        }

        fun setPort(port: Int): Builder {
            this.port = port
            return this
        }

        fun setTimeout(timeout: Long): Builder {
            this.timeout = timeout
            return this
        }

        fun setOnSocketListener(listener: OnSocketConnectListener): Builder {
            this.listener = listener
            return this
        }

        fun build(): SocketClient {
            return SocketClient(this)
        }
    }

    init {
        val eventLoopGroup = NioEventLoopGroup()
        bootstrap = Bootstrap()
        bootstrap.group(eventLoopGroup)
            .channel(NioSocketChannel::class.java)
            .option(ChannelOption.TCP_NODELAY, true) //无阻塞
            .option(ChannelOption.SO_KEEPALIVE, true) //长连接
            .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10 * 1000) //连接超时时间
            .option(
                ChannelOption.RCVBUF_ALLOCATOR, AdaptiveRecvByteBufAllocator(64, 2048, 65536)
            )
            .handler(object : ChannelInitializer<SocketChannel>() {
                override fun initChannel(socketChannel: SocketChannel) {
                    val pipeline = socketChannel.pipeline()
                    pipeline.addLast(IdleStateHandler(60, 10, 0, TimeUnit.SECONDS))
                        .addLast(ByteArrayDecoder())
                        .addLast(ByteArrayEncoder())
                        .addLast(SocketChannelHandler(listener))
                }
            })
    }

    fun connect() {
        synchronized(this) {
            try {
                Log.d(kTag, "connect: 连接TCP服务器")
                //连接监听
                val channelFuture = bootstrap.connect(hostname, port)
                    .addListener(object : ChannelFutureListener {
                        override fun operationComplete(channelFuture: ChannelFuture) {
                            if (channelFuture.isSuccess) {
                                channel = channelFuture.channel()
                            }
                        }
                    }).sync()
                // 等待连接关闭
                channelFuture.channel().closeFuture().sync()
            } catch (e: Exception) {
                retryConnect()
            }
        }
    }

    fun disconnect() {
        channel?.close()?.addListener(ChannelFutureListener.CLOSE)
        Log.d(kTag, "disconnect: 断开TCP服务器")
    }

    private fun retryConnect() {
        try {
            Thread.sleep(timeout)
            connect()
        } catch (ex: InterruptedException) {
            Log.d(kTag, "retryConnect: ${ex.localizedMessage}")
        }
    }

    fun sendData(message: String) {
        val byteBuf = Unpooled.wrappedBuffer(message.toByteArray())
        sendData(byteBuf)
    }

    fun sendData(buffer: ByteBuf) {
        channel?.writeAndFlush(buffer)
    }
}