package cn.muchinfo.rma.netcore.socket.core;


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.InetAddress;
import java.net.SocketAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.concurrent.Semaphore;

/**
 * socket通讯引擎类
 */
public class SocketEngine {

    private static final String TAG = "SocketEngine";

    private BaseSocket mySocket = null;
    private SocketListener mlistener;  //回调接口

    private String host;
    private String port;
    private SocketAddress remoteAddr = null;

    private OutputStream outputStream = null;
    private InputStream inputStream = null;

    private ArrayList<byte[]> sendMessageContents = new ArrayList<byte[]>();  //消息队列 发送的其实是比特数组

    private boolean running = false;
    private int reconnectTime = 0;

    private byte[] buffer = null;

    private Semaphore semaphoreSend = new Semaphore(1);

    private Thread thread;
    private Thread writeThread;

    /** 与外界交互的方法  如果要替换通讯内核 把**/

    /**
     * 初始化
     *
     * @param host IP地址
     * @param port 端口
     * @param mlistener 回调
     */
    public SocketEngine(String host, String port, SocketListener mlistener) {
        this.host = host;
        this.port = port;
        this.mlistener = mlistener;
    }

    /**
     * 启动socket连接
     * @return SocketEngine
     */
    public SocketEngine connect() {
        try {
            //解析地址端口号是否正常
            InetAddress inetAddress = InetAddress.getByName(this.host);
            mySocket = new BaseSocket(inetAddress, Integer.parseInt(port));
            mySocket.setSoTimeout(SocketConstant.WRITE_READ_OUTTIME);  //设置读写超时时间
            mySocket.setKeepAlive(true);
            mySocket.setReceiveBufferSize(SocketConstant.BUFFER_SIZE);  //设置报文长度
            mySocket.setSendBufferSize(SocketConstant.BUFFER_SIZE);

            // 判断客户端和服务器是否连接成功
            if(mySocket.isConnected()) {
                running = true;  //标志开始socket连接
                outputStream = null;
                if (mlistener != null) {
                    mlistener.connectSuccess();
                }
                start();
            }else{
                if(reconnectTime == 0) {
                    //try again but only once
                    connect();
                    reconnectTime++;
                }
            }
        } catch (UnknownHostException e) {
            if (mlistener != null) {
                mlistener.hostError();  //抛出地址错误
            }
            e.printStackTrace();
        } catch (SocketTimeoutException e) {
            if (mlistener != null) {
                mlistener.connectOutTime(host, port);
            }
            stopSocket();
            e.printStackTrace();
        } catch (ConnectException e) {
            if (mlistener != null) {
                mlistener.connectException();
            }
            stopSocket();
            e.printStackTrace();
        } catch (Exception e) {
            stopSocket();
            e.printStackTrace();
        }
        return this;
    }

    /**
     * socket发送数据
     */
    private void writeMessage() {
        if (!isRunning()) {  //正在运行中
            return;
        }
        writeThread = new Thread(new Runnable() {
            @Override
            public void run() {
                //发送前 做好流水号机制
                byte[] bs = null;
                try {
                    while (running) {
                        synchronized (mySocket){
                            if (mySocket == null || mySocket.isClosed() || !mySocket.isConnected()) {
                                if (mlistener != null) {
                                    stopSocket();
                                    mlistener.disConnect(SocketConstant.WRITE_NULL);
                                }
                                return;
                            } else {
                                outputStream = mySocket.getOutputStream();
                            }
                        }

                        if (sendMessageContents.size() == 0 && !semaphoreSend.hasQueuedThreads()) {
                            semaphoreSend.acquireUninterruptibly();
                        }
                        // FIXME : 发送
                        synchronized (sendMessageContents) {
                            if (sendMessageContents.size() > 0) {
                                try {
                                    bs = sendMessageContents.get(0).clone();
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                try {
                                    sendMessageContents.remove(0);//移除已经决定发送的
                                } catch (Exception e) {
                                    e.printStackTrace();
                                }
                                if (bs != null) {
                                    outputStream.write(bs); //发送
                                }
                            }
                        }

                    }
                } catch (IOException e) {
                    if (mlistener != null) {
                        mlistener.disConnect(SocketConstant.WRITE_ERROR);
                        stopSocket();
                    }
                    e.printStackTrace();
                } catch (Exception e) {
                    e.printStackTrace();
                    if (mlistener != null) {
                        mlistener.disConnect(SocketConstant.WRITE_ERROR);
                        stopSocket();
                    }
                }
            }
        });
        writeThread.start();
    }

    /**
     * 发送消息
     * @param sendMessageContent 待发送报文
     */
    public synchronized void send(byte[] sendMessageContent) {
        if(sendMessageContent != null && sendMessageContent.length != 0 && outputStream == null){
            writeMessage();
        }
        sendMessageContents.add(sendMessageContent);// 放入发送数据池
        semaphoreSend.release();// 释放向量
    }

    /**
     * 接收到消息
     */
    private void start() {
        thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (running) {
                    try {
                        if (mySocket == null) {
                            if (mlistener != null) {
                                stopSocket();
                                mlistener.disConnect(SocketConstant.START_NULL);
                            }
                            return;
                        }
                        inputStream = mySocket.getInputStream();
                        if (inputStream.available() > 0) {
                            if (mlistener != null) {
                                buffer = new byte[inputStream.available()];
                                inputStream.read(buffer);
                                mlistener.byteMessage(buffer);
//								LogUtils.e("TasSocket", "get message");
                            }
                            // Thread.sleep(50);
                        } else {
                            if (!thread.isInterrupted()) {
                                try {
                                    Thread.sleep(50);
                                } catch (InterruptedException e) {
                                    running = false;
                                    thread.interrupt();
                                }
                            }
                        }
                    } catch (Exception e) {
                        running = false;
                        e.printStackTrace();
                        if (mlistener != null) {
                            stopSocket();
                            mlistener.disConnect(SocketConstant.START_ERROR);
                        }
                    }
                }
            }
        });
        thread.start();
    }

    /**
     * 重启线程
     */
    private void reStartThreadPool() {
        start();
        writeMessage();
    }

    /**
     * 停止监控、订阅、登陆验证
     */
    private void closeLoop() {
        // thread.interrupt();
        // writeThread.interrupt();
        running = false;
        if (semaphoreSend != null && !semaphoreSend.tryAcquire()) {
            semaphoreSend.release();
        }
    }

    public void stopSocket() {
        try {
            closeLoop();
            if (thread != null) {
                thread.interrupt();
            }
            if (writeThread != null) {
                writeThread.interrupt();
            }

            if (mySocket != null) {
                mySocket.close();
            }

            if (outputStream != null) {
                outputStream.close();
            }

            if (inputStream != null) {
                inputStream.close();
            }

            if (mySocket != null) {
                if (!mySocket.isClosed() && mySocket.isConnected() && !mySocket.isInputShutdown()) {
                    mySocket.shutdownInput();
                    //socket shutdonwOutput时出现异常，这个问题可能是多次关闭同一个socket所导致
                    mySocket.shutdownOutput();
                }

                if (mySocket != null) {
                    if (!mySocket.isClosed()) {
                        mySocket.close();
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
            mlistener.disConnect(SocketConstant.WRITE_ERROR);
        }
    }

    private boolean isRunning() {
        return running;
    }
}
