package com.asreader.asr033w.asr033w.network;

import android.util.Log;
import java.io.DataInputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ASR033WNetworkTCP {
    private String serverIP = "";
    private int serverPort = -1;
    private PrintWriter pw;
    private InputStream is;
    private DataInputStream dis;
    private boolean isRun = false;
    private byte buff[] = new byte[4096];
    private String rcvMsg;
    private Socket socket;
    private int rcvLen;

    private int soTimeOut = 0;
    private Runnable msgRun;
    private Runnable connRun;
    private Runnable sendMsgRun;
    private ASR033WNetworkTCPCallBack asr033WNetworkTCPCallBack;
    private int numberOfCores = Runtime.getRuntime().availableProcessors();
    private int keepAliveTime = 1;
    private BlockingQueue<Runnable> taskQueue = new LinkedBlockingQueue<Runnable>();
    private boolean needReConn = false;
    private int reConnTime = 10;
    public boolean isConnected = false;
    ExecutorService executorService = new ThreadPoolExecutor(numberOfCores,
            numberOfCores * 2, keepAliveTime, TimeUnit.SECONDS, taskQueue);
    public ASR033WNetworkTCP() {
    }

    private void setServerIP(String serverIP) {
        this.serverIP = serverIP;
    }

    private void setServerPort(int serverPort) {
        this.serverPort = serverPort;
    }

    public void setSoTimeOut(int soTimeOut) {
        this.soTimeOut = soTimeOut;
    }

    public void setNeedReConn(boolean needReConn) {
        this.needReConn = needReConn;
    }

    public void setReConnTime(int reConnTime) {
        this.reConnTime = reConnTime;
    }

    public void setASR033WNetworkTCPCallBack(ASR033WNetworkTCPCallBack listener) {
        this.asr033WNetworkTCPCallBack = listener;
    }

    public void connect(String serverIp, int port){
        setServerIP(serverIp);
        setServerPort(port);
        reconnect();
    }

    public void reconnect() {
        connRun = new Runnable() {
            @Override
            public void run() {
                if (isRun) {
                    disconnect();
                    try {
                        Thread.sleep(reConnTime * 1000);
                        reconnect();
                    } catch (Exception e) {

                    }
                    return;
                }
                try {
                    Log.d("TAG", "=================run:new socket  serverIP: "+serverIP+" serverPort:"+serverPort);
                    socket = new Socket(serverIP, serverPort);
                    socket.setSoTimeout(soTimeOut);
                    pw = new PrintWriter(socket.getOutputStream(), true);
                    is = socket.getInputStream();
                    dis = new DataInputStream(is);
                    startListen();
                } catch (Exception e) {
                    Log.d("TAG", "============== new Socket:"+e.getMessage());
                    if (asr033WNetworkTCPCallBack != null) {
                        asr033WNetworkTCPCallBack.onTCPDisconnect();
                    }
                    disconnect();
                    return;
                }
                isConnected = true;
                if (asr033WNetworkTCPCallBack != null){
                    asr033WNetworkTCPCallBack.onTCPConnected();
                }
            }
        };
        executorService.execute(connRun);
    }

    private void startListen() {
        if (isRun) {
            //当前消息监听尚未停止，无法执行startListen()
            return;
        }
        if (socket == null || pw == null || is == null) {
            //socket初始化失败，无法执行startListen()
            return;
        }
        isRun = true;
        msgRun = new Runnable() {
            @Override
            public void run() {
                while (isRun) {
                    try {
                        rcvLen = dis.read(buff);
                        if (rcvLen > 0){
                            byte[] receivedBytes = new byte[rcvLen];
                            System.arraycopy(buff, 0, receivedBytes, 0, rcvLen);
                            if (asr033WNetworkTCPCallBack != null) {
                                asr033WNetworkTCPCallBack.onTCPReceive(receivedBytes);
                            }
                        }

                    } catch (Exception e) {

                        Log.d("TAG", "============== startListen e:"+e.getMessage());
                        if (asr033WNetworkTCPCallBack != null){
                            asr033WNetworkTCPCallBack.onTCPErrorReceive(e);
                        }
                        disconnect();
                    }
                }
            }
        };
        executorService.execute(msgRun);
    }
    public void sendData(final byte[] data) {
        sendMsgRun = new Runnable() {
            @Override
            public void run() {
                try {
                    socket.getOutputStream().write(data);
                    socket.getOutputStream().flush();
                } catch (Exception e) {
                    if (asr033WNetworkTCPCallBack != null){
                        asr033WNetworkTCPCallBack.onTCPErrorReceive(e);
                    }
                }
            }
        };
        executorService.execute(sendMsgRun);
    }
    public void sendData(final String msg) {
        sendMsgRun = new Runnable() {
            @Override
            public void run() {
                try {

                    pw.println(msg);
                    pw.flush();
                } catch (Exception e) {
                    if (asr033WNetworkTCPCallBack != null){
                        asr033WNetworkTCPCallBack.onTCPErrorReceive(e);
                    }
                }
            }
        };
        executorService.execute(sendMsgRun);
    }

    public void disconnect() {
        try {
            isRun = false;
            isConnected = false;
            pw.close();
            is.close();
            dis.close();
            socket.close();
        } catch (Exception e) {
            if (asr033WNetworkTCPCallBack != null){
                asr033WNetworkTCPCallBack.onTCPErrorReceive(e);
            }
        }
    }
}
