package com.common.bluetooth.service.bt;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.os.Environment;
import android.util.Log;

import com.common.bluetooth.BtConstants;
import com.common.bluetooth.callback.BtMsgListener;
import com.common.bluetooth.service.BluetoothCommon;
import com.common.bluetooth.utils.BtUtils;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 客户端和服务端的基类，用于管理socket长连接
 *
 * @author wangym
 * @since 2021-12-2
 */
public class BluetoothClassicBase extends BluetoothCommon {
    private static final String TAG = "BluetoothClassicBase";
    private static final String FILE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/bluetooth/";
    private static final int FLAG_MSG = 0;  //消息标记
    private static final int FLAG_FILE = 1; //文件标记

    protected static BluetoothSocket mSocket;
    private DataOutputStream mOut;
    private BtMsgListener mListener;
    /**
     * 是否连接中
     */
    private final AtomicBoolean isConnected = new AtomicBoolean(false);
    /**
     * 是否在发送中
     */
    private final AtomicBoolean isSending = new AtomicBoolean(false);

    /**
     * 消息队列
     */
    private final LinkedBlockingQueue<byte[]> msgQueue = new LinkedBlockingQueue<>();

    public BluetoothClassicBase(Context context) {
        super(context);
    }

    /**
     * 设置消息的监听
     *
     * @param mListener 监听
     */
    public void setListener(BtMsgListener mListener) {
        this.mListener = mListener;
    }

    /**
     * 循环读取对方数据(若没有数据，则阻塞等待)
     */
    void loopRead(BluetoothSocket socket) {
        mSocket = socket;
        DataInputStream in = null;
        try {
            if (!mSocket.isConnected()) {
                mSocket.connect();
            }
            String mac = mSocket.getRemoteDevice().getAddress();
            notifyUI(BtMsgListener.CONNECTED, mac);
            // 将链接成功的MAC地址保存下来
            BtUtils.INSTANCE.saveConnectMac(getMContext(), mac);
            mOut = new DataOutputStream(mSocket.getOutputStream());
            in = new DataInputStream(mSocket.getInputStream());
            isConnected.set(true);
        } catch (Throwable e) {
            closeByConnect();
        }
        doRead(in);
    }

    private void doRead(DataInputStream in) {
        if (in == null) {
            Log.e(TAG, "got in is null");
            return;
        }
        try {
            while (isConnected.get()) { //死循环读取
                switch (in.readInt()) {
                    case FLAG_MSG: //读取短消息
                        String msg = in.readUTF();
                        Log.d(TAG, "接收短消息：" + msg);
                        notifyUI(BtMsgListener.MSG, msg);
                        break;
                    case FLAG_FILE: //读取文件
                        new File(FILE_PATH).mkdirs();
                        String fileName = in.readUTF(); //文件名
                        long fileLen = in.readLong(); //文件长度
                        // 读取文件内容
                        long len = 0;
                        int r;
                        byte[] b = new byte[4 * 1024];
                        FileOutputStream out = new FileOutputStream(FILE_PATH + fileName);
                        Log.d(TAG, "正在接收文件(" + fileName + "),请稍后...");
                        while ((r = in.read(b)) != -1) {
                            out.write(b, 0, r);
                            len += r;
                            if (len >= fileLen) {
                                break;
                            }
                        }
                        Log.d(TAG, "文件接收完成(存放在:" + FILE_PATH + ")");
                        break;
                }
            }
        } catch (Throwable e) {
            close();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 发送短消息
     */
    public void sendMsg(byte[] msg) {
        Log.d(TAG, "send msg");
        if (mSocket == null || !mSocket.isConnected()) {
            Log.e(TAG, "sendMsg got socket is not connect");
            return;
        }
        msgQueue.add(msg);
        if (checkSend()) {
            Log.d(TAG, "sending wait");
            return;
        }
        synchronized (BluetoothClassicBase.class) {
            isSending.set(true);
            // 循环发送消息
            while (true) {
                if (!doSendMsg()) {
                    break;
                }
            }
            isSending.set(false);
        }
    }

    /**
     * 发送消息
     */
    public boolean doSendMsg() {
        final byte[] msgItem = msgQueue.poll();
        if (msgItem == null) {
            Log.d(TAG, "doTranslate Queue is null");
            return false;
        }
        try {
            mOut.writeInt(FLAG_MSG); //消息标记
            mOut.writeUTF(new String(msgItem, BtConstants.INSTANCE.getDEFAULT_CHARSET()));
            mOut.flush();
            Log.d(TAG, "发送短消息：" + new String(msgItem, BtConstants.INSTANCE.getDEFAULT_CHARSET()));
        } catch (Throwable e) {
            close();
            isSending.set(false);
        }
        return true;
    }

    /**
     * 发送文件
     */
    public void sendFile(final String filePath) {
        if (checkSend()) return;
        isSending.set(true);
        Executors.newCachedThreadPool().execute(new Runnable() {
            @Override
            public void run() {
                try {
                    FileInputStream in = new FileInputStream(filePath);
                    File file = new File(filePath);
                    mOut.writeInt(FLAG_FILE); //文件标记
                    mOut.writeUTF(file.getName()); //文件名
                    mOut.writeLong(file.length()); //文件长度
                    int r;
                    byte[] b = new byte[4 * 1024];
                    notifyUI(BtMsgListener.MSG, "正在发送文件(" + filePath + "),请稍后...");
                    while ((r = in.read(b)) != -1)
                        mOut.write(b, 0, r);
                    mOut.flush();
                    notifyUI(BtMsgListener.MSG, "文件发送完成.");
                } catch (Throwable e) {
                    closeByConnect();
                }
                isSending.set(false);
            }
        });
    }

    /**
     * 连接失败关闭socket
     */
    public void closeByConnect() {
        Log.d(TAG, "closeByConnect");
        try {
            String mac = "";
            if (mSocket != null) {
                mSocket.close();
                mac = mSocket.getRemoteDevice().getAddress();
            }
            // 如果是从连接状态断开，那么发送断开事件
            // 如果是从未连接状态则认为是连接异常
            notifyUI(BtMsgListener.CONN_ERROR, mac);
            isConnected.set(false);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * 关闭Socket连接
     */
    public void close() {
        Log.d(TAG, "close");
        try {
            String mac = "";
            if (mSocket != null) {
                mSocket.close();
                mac = mSocket.getRemoteDevice().getAddress();
            }
            // 如果是从连接状态断开，那么发送断开事件
            // 如果是从未连接状态则认为是连接异常
            notifyUI(BtMsgListener.DISCONNECTED, mac);
            isConnected.set(false);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * 无回调关闭Socket
     */
    public void closeSilence() {
        Log.d(TAG, "closeSilence");
        try {
            isConnected.set(false);
            if (mSocket != null) {
                mSocket.close();
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    /**
     * 当前设备与指定设备是否连接
     *
     * @param mac 设备MAC地址
     * @return 是否已经和对应设备拦截
     */
    public boolean isConnected(String mac) {
        BluetoothDevice dev = getBtDeviceByMac(mac);
        boolean connected = (mSocket != null && mSocket.isConnected());
        if (dev == null) {
            return connected;
        }
        return connected && mSocket.getRemoteDevice().equals(dev);
    }

    /**
     * 是否在发送中
     *
     * @return 是否发送中
     */
    private synchronized boolean checkSend() {
        return isSending.get();
    }

    /**
     * 通知外部接收信息
     *
     * @param state 消息类型
     * @param msg   消息内容
     */
    protected void notifyUI(final int state, final String msg) {
        if (mListener != null) {
            mListener.messageReceive(state, msg);
        }
    }
}
