package com.wdkl.ncs.s433.transbox;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.serialport.SerialPort;
import android.util.Log;

import com.wdkl.ncs.s433.transbox.common.Constants;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.security.InvalidParameterException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.zip.CRC32;

import static androidx.constraintlayout.motion.utils.Oscillator.TAG;

public abstract class SerialPortActivity extends Activity {
    String TAG = "SerialPortActivity";

    protected Application mApplication;
    protected SerialPort mSerialPort;
    protected OutputStream mOutputStream;
    private InputStream mInputStream;
    private ReadThread mReadThread;

    private byte[] buffer = new byte[1024];
    //数据包接收缓存
    private Map<Long, List<byte[]>> msgCache = new HashMap<>();

    private class ReadThread extends Thread {
        ByteBuffer byteBuffer = ByteBuffer.allocate(512);
        @Override
        public void run() {
            super.run();
            while (!isInterrupted()) {
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                try {
                    if (mInputStream == null) {
                        return;
                    }

                    /*if(mInputStream.available()>0) {
                        size = mInputStream.read(buffer);
                        if (size > 0) {
                            onDataReceived(buffer, size);
                        }
                    }*/
                    int size;

                    if (mInputStream.available() > 0) {
                        size = mInputStream.read(buffer);
                        Log.e(TAG, "run: buffer length:" + byteBuffer.position());
                        byteBuffer.put(ArrayUtils.subarray(buffer, 0, size));
                        String msg= handlePackage(byteBuffer,120);
                        if(StringUtils.isNotEmpty(msg)){
                            onDataReceivedString(msg);
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    return;
                }
            }
        }
    }


    /**
     * 处理数据包，收到数据包头时开始，读取指定数据包长度后校验数据包是否完整，数据包校验不通过时丢弃。
     * 数据包校验通过，需要获取数据包内容合成数据包内数据消息。
     * @param buffer 接收缓冲区
     * @param packageSize 数据包大小
     * @return
     */
    private String handlePackage(ByteBuffer buffer,int packageSize) {
        try {
            buffer.flip();
            Log.e(TAG, "buff信息:position="+buffer.position()+",limit="+buffer.limit() );
            if (buffer.position() == 0 && buffer.limit() == 0) { //buffer是空的无需处理
                buffer.compact();
                return null;
            }
            for (int i = 0; i < buffer.limit() - 1; i++) {
                //监测到包头
                if (byteArrToHexString(new byte[]{buffer.get(i), buffer.get(i + 1)}).equalsIgnoreCase("AA55")) {
                    if (i > 0) {
                        byte[] incorrect = new byte[i + 1];
                        buffer.get(incorrect, 0, i + 1);
                    }
                    //读取120个字节
                    if (buffer.limit() - i >= packageSize) {
                        byte[] bytes = new byte[packageSize];
                        buffer.get(bytes, 0, packageSize);
                        Log.i(TAG, "handlePackage:  收到：" + ArrayUtils.toString(bytes));
                        //校验数据包
                        CRC32 crc32 = new CRC32();
                        byte[] subarray1 = ArrayUtils.subarray(bytes, 0, bytes.length - 8);
                        Log.i(TAG, "handlePackage: " + ArrayUtils.toString(subarray1));
                        crc32.update(subarray1);
                        //计算出的crc校验
                        long caculateCheck = crc32.getValue();
                        byte[] subarray = ArrayUtils.subarray(bytes, bytes.length - 8, bytes.length);
                        //传输过来的校验
                        long transCheck = ByteBuffer.wrap(subarray).getLong();
                        if (caculateCheck == transCheck) {
                            long msgId = ByteBuffer.wrap(ArrayUtils.subarray(bytes, 5, 13)).getLong();
                            int packageTotal = bytes[4];
                            int packageIndex = bytes[3];
                            int availableLength = bytes[2];
                            byte[] availalbeData = ArrayUtils.subarray(bytes, 13, 13 + availableLength);
                            if (packageTotal == 1) { //单包消息
                                String s = new String(availalbeData);
                                Log.i(TAG, "收到完整消息: " + s);
                                buffer.compact();
                                return s;

                            } else {
                                if (!msgCache.containsKey(msgId)) {
                                    List<byte[]> list = new ArrayList<>(packageTotal);
                                    for (int j = 0; j < packageTotal; j++) {
                                        list.add(null);
                                    }
                                    list.set(packageIndex - 1, availalbeData);
                                    msgCache.put(msgId, list);
                                } else {
                                    List<byte[]> exist = msgCache.get(msgId);
                                    exist.set(packageIndex - 1, availalbeData);
                                    if (exist.stream().allMatch(p->p!=null)) { //消息已经接收完整
                                        int sum = exist.stream().mapToInt(p -> p.length).sum();
                                        ByteBuffer combine = ByteBuffer.allocate(sum);
                                        for (byte[] bytes1 : exist) {
                                            combine.put(bytes1);
                                        }
                                        String s = new String(combine.array());
                                        msgCache.remove(msgId);
                                        Log.i(TAG, "收到完整消息: " + s);
                                        buffer.compact();
                                        return s;
                                    }

                                }
                                Log.i(TAG, "handlePackage: 消息ID：" + msgId + "crc-c:" + caculateCheck + ",crc-t:" + transCheck);
                            }
                        }

                    }
                    break;
                }
            }
            if(buffer.limit()>=360){ //如果在3个数据包的缓冲区长度内都没有发现一个包头，清空buffer,防止其他信号干扰
                buffer.get(new byte[buffer.limit()],0,buffer.limit());
            }
            buffer.compact();
            return null;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 字节数组转16进制字符串
     *
     * @param b
     * @return
     */
    private String byteArrToHexString(byte[] b) {
        String result = "";
        for (int i = 0; i < b.length; i++) {
            result += Integer.toString((b[i] & 0xff) + 0x100, 16).substring(1);
        }
        return result;
    }

    private void DisplayError(int resourceId) {
        AlertDialog.Builder b = new AlertDialog.Builder(this);
        b.setTitle("Error");
        b.setMessage(resourceId);
        b.setPositiveButton("OK", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
//                startActivity(new Intent(SerialPortActivity.this, SettingsActivity.class));
//                SerialPortActivity.this.finish();
            }
        });
        b.show();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mApplication = (Application) getApplication();
        try {
            mSerialPort = mApplication.getSerialPort();
            mOutputStream = mSerialPort.getOutputStream();
            mInputStream = mSerialPort.getInputStream();

			/* Create a receiving thread */
            mReadThread = new ReadThread();
            mReadThread.start();
            Constants.SERIAL_PORT_STATUS=true;


        } catch (SecurityException e) {
            Constants.SERIAL_PORT_STATUS=false;
            Constants.SERIAL_PORT_FAIL_RESON=getResources().getString(R.string.error_security);
            DisplayError(R.string.error_security);
        } catch (IOException e) {
            Constants.SERIAL_PORT_STATUS=false;
            Constants.SERIAL_PORT_FAIL_RESON=getResources().getString(R.string.error_unknown);
            DisplayError(R.string.error_unknown);
        } catch (InvalidParameterException e) {
            Constants.SERIAL_PORT_STATUS=false;
            Constants.SERIAL_PORT_FAIL_RESON=getResources().getString(R.string.error_configuration);
            DisplayError(R.string.error_configuration);
        }
    }

    protected abstract void onDataReceived(final byte[] buffer, final int size);

    protected abstract void onDataReceivedString(String data);

    @Override
    protected void onDestroy() {
        if (mReadThread != null) mReadThread.interrupt();
        mApplication.closeSerialPort();
        mSerialPort = null;
        super.onDestroy();
    }
}