package cn.wintec.wtandroidjar;

import android.os.Build;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class ComIO {
    private String devicePath;
    private int[] readHandle;
    private int[] readHandle2;
    private Lock lock;

    /**
     * Constructor
     *
     * @param devicePath UART device path，for example�?/dev/ttymxc1
     */
    public ComIO(String devicePath) {
        this.devicePath = devicePath;
        this.readHandle = new int[4];
        this.readHandle[0] = -1;
        this.readHandle[1] = -1;
        this.readHandle[2] = -1;
        this.readHandle[3] = 0;
        this.readHandle2 = new int[4];
        this.lock = new ReentrantLock();
    }

    /**
     * Write data to the uart
     *
     * @param string the string need to write to uart
     * @throws IOException
     */
    public void write(String string) throws IOException {
        File file = new File(devicePath);
        FileOutputStream stream = new FileOutputStream(file);

        try {
            stream.write(string.getBytes());
        } finally {
            stream.flush();
            stream.close();
        }
    }

    /**
     * Write buffer contents to the UART
     *
     * @param buffer The data needs to be written to the uart
     * @throws IOException
     */
    public void write(byte[] buffer)
            throws IOException {
        File file = new File(devicePath);
        FileOutputStream stream = new FileOutputStream(file);
        try {
            stream.write(buffer);
        } finally {
            stream.flush();
            stream.close();
        }
    }

    /**
     * read data from the uart
     *
     * @param buffer the buffer of store read data
     * @param offset the start position data stored in the buffer
     * @param length Maximum read length
     * @return The actual number of bytes read. If the device does not exist, returns -1
     * @throws IOException
     */
    public int read(byte[] buffer, int offset, int length) {
        lock.lock();
        startReadFile(devicePath, readHandle);
        readHandle2[0] = readHandle[0];
        readHandle2[1] = readHandle[1];
        readHandle2[2] = readHandle[2];
        readHandle2[3] = readHandle[3];
        lock.unlock();
        int ret = readFile(readHandle2, buffer, offset, length, -1);
        return ret;
    }

    /**
     * read data from the uart,overtime
     *
     * @param buffer      the buffer of store read data
     * @param offset      the start position data stored in the buffer
     * @param length      Maximum read length
     * @param millisecond timeout
     * @return The actual number of bytes read. If the device does not exist, returns -1
     */
    public int read(byte[] buffer, int offset, int length, long millisecond) {
        lock.lock();
        startReadFile(devicePath, readHandle);
        readHandle2[0] = readHandle[0];
        readHandle2[1] = readHandle[1];
        readHandle2[2] = readHandle[2];
        readHandle2[3] = readHandle[3];
        lock.unlock();
        //Log.v("Read3", String.format("beginx %d", readHandle2[0]));
        int lenRemain = length;
        int ret = 0;
        int length1 = offset;
        boolean dataReceived = false;
        long beginReadTime = System.currentTimeMillis();
        while (true) {
            if (lenRemain > 10) {
                ret = readFile(readHandle2, buffer, length1, 10, 5);
            } else {
                ret = readFile(readHandle2, buffer, length1, lenRemain, 5);
            }
            lenRemain -= ret;
            if (ret > 0)//ԭ����!=0�޸�Ϊ����0
                dataReceived = true;
            if (!dataReceived) {
                long usedTime = System.currentTimeMillis() - beginReadTime;
                if (usedTime > millisecond) {
                    return 0;
                }
            }
            length1 += ret;
            if (dataReceived && ret < 10)
                break;
        }
        // Log.v("Read", "over");
        return length1 - offset;
    }

    /**
     * If an object is called off ComIO read function, be sure to call this function recovery resources;
     * This function also cancel're reading read function
     */
    public void readFinish() {
        lock.lock();
        stopReadFile(readHandle);
        lock.unlock();
    }

    public enum Baudrate {
        BAUD_115200, BAUD_57600,
        BAUD_38400, BAUD_19200, BAUD_9600, BAUD_4800,
        BAUD_2400, BAUD_1200, BAUD_600, BAUD_300,
        BAUD_UNKNOWN,
    }

    public enum VerifyBit {
        NONE, ODD, EVEN,
    }

    public enum DataBit {
        DATA_BIT_8,
        DATA_BIT_7,
        DATA_BIT_6,
        DATA_BIT_5,
    }

    public enum StopBit {
        STOP_BIT_1,
        STOP_BIT_2
    }

    public enum FlowControl {
        NONE, RTS_CTS, DTR_DSR,
    }

    public class Param {
        public Baudrate baudrate;
        public VerifyBit verifyBit;
        public DataBit dataBit;
        public StopBit stopBit;
        public FlowControl flowControl;
    }

    ;

    /**
     * set UART parameter
     *
     * @param baudrate
     * @param verifyBit
     * @param dataBit
     * @param stopBit
     * @param flowControl
     */
    public void setSerialParam(
            Baudrate baudrate,
            VerifyBit verifyBit,
            DataBit dataBit,
            StopBit stopBit,
            FlowControl flowControl) {
        String verifyBit_ = verifyBit.toString();
        String baudrate_ = baudrate.toString().substring(9);
        String dataBit_ = "cs" + dataBit.toString().substring(9);
        String stopBit_ = dataBit.toString();

        if (verifyBit == VerifyBit.NONE) verifyBit_ = "-parenb";
        else if (verifyBit == VerifyBit.ODD) verifyBit_ = "parodd";
        else if (verifyBit == VerifyBit.EVEN) verifyBit_ = "_parodd";

        if (stopBit == StopBit.STOP_BIT_1) stopBit_ = "-cstopb";
        else if (stopBit == StopBit.STOP_BIT_2) stopBit_ = "cstopb";

        String pathpredix = "busybox stty -F ";
        String pathsuffix = devicePath + " " + baudrate_ + " " + verifyBit_ + " " + dataBit_ + " " + stopBit_;
        String cmd = pathpredix.concat(pathsuffix);
        try {
            Process setSerialParam = Runtime.getRuntime().exec(cmd);
            setSerialParam.waitFor();
            System.out.println("yeah!");
        } catch (IOException e) {
            System.out.println(cmd);
        } catch (InterruptedException e) {
        }
    }

    public void enableRTS_CTS() {
        String cmd = "busybox stty -F " + devicePath + " xonxoff";
//        String cmd        = "busybox stty -F " + devicePath + " crtscts";
        try {
            Process setSerialParam = Runtime.getRuntime().exec(cmd);
            setSerialParam.waitFor();
            System.out.println("yeah!");
        } catch (IOException e) {
            System.out.println(cmd);
        } catch (InterruptedException e) {
        }
    }

    public void disableRTS_CTS() {
        String cmd = "busybox stty -F " + devicePath + " -xonxoff";
        //       String cmd        = "busybox stty -F " + devicePath + " -crtscts";
        try {
            Process setSerialParam = Runtime.getRuntime().exec(cmd);
            setSerialParam.waitFor();
            System.out.println("yeah!");
        } catch (IOException e) {
            System.out.println(cmd);
        } catch (InterruptedException e) {
        }
    }

    /**
     * get UART parameter
     *
     * @return collection of UART parameter
     */
    public Param getSerialParam() {
        Param param = new Param();
        param.baudrate = Baudrate.BAUD_115200;
        param.verifyBit = VerifyBit.NONE;
        param.dataBit = DataBit.DATA_BIT_8;
        param.stopBit = StopBit.STOP_BIT_1;
        param.flowControl = FlowControl.NONE;

        String cmd = "busybox stty -F " + devicePath + " -a";
        StringBuffer content = new StringBuffer();
        try {
            Process setSerialParam = Runtime.getRuntime().exec(cmd);
            InputStream comExeResult = setSerialParam.getInputStream();
            int temp;
            while ((temp = comExeResult.read()) != -1) {
                if ((char) temp == ';') {
                    content.append(" ");
                } else {
                    content.append((char) temp);
                }
            }

            String comParameters = content.toString();
            String baudrate, dataBit;
            boolean verify, verifyBit;
            Pattern p1 = Pattern.compile("(\\r|\\n|.)*speed\\s+(300|600|1200|2400|4800|9600|19200|" +
                    "38400|43000|56000|57600|115200)\\b(\\r|\\n|.)*");
            Matcher m1 = p1.matcher(comParameters);
            m1.matches();
            baudrate = m1.group(2);
            if (baudrate.equals("300")) param.baudrate = Baudrate.BAUD_300;
            else if (baudrate.equals("600")) param.baudrate = Baudrate.BAUD_600;
            else if (baudrate.equals("1200")) param.baudrate = Baudrate.BAUD_1200;
            else if (baudrate.equals("2400")) param.baudrate = Baudrate.BAUD_2400;
            else if (baudrate.equals("4800")) param.baudrate = Baudrate.BAUD_4800;
            else if (baudrate.equals("9600")) param.baudrate = Baudrate.BAUD_9600;
            else if (baudrate.equals("19200")) param.baudrate = Baudrate.BAUD_19200;
            else if (baudrate.equals("38400")) param.baudrate = Baudrate.BAUD_38400;
            else if (baudrate.equals("57600")) param.baudrate = Baudrate.BAUD_57600;
            else if (baudrate.equals("115200")) param.baudrate = Baudrate.BAUD_115200;

            Pattern p2 = Pattern.compile("(\r|\n|.)*(-parenb)(\r|\n|.)*");
            Matcher m2 = p2.matcher(comParameters);
            verify = m2.matches();
            Pattern p3 = Pattern.compile("(\r|\n|.)*(-parodd)(\r|\n|.)*");
            Matcher m3 = p3.matcher(comParameters);
            verifyBit = m3.matches();
            if (verify) param.verifyBit = VerifyBit.NONE;
            else if (verify == false && verifyBit == true) param.verifyBit = VerifyBit.EVEN;
            else if (verify == false && verifyBit == false) param.verifyBit = VerifyBit.ODD;

            Pattern p4 = Pattern.compile("(\r|\n|.)*(-cstopb)(\r|\n|.)*");
            Matcher m4 = p4.matcher(comParameters);
            if (m4.matches()) param.stopBit = StopBit.STOP_BIT_1;
            else param.stopBit = StopBit.STOP_BIT_2;

            Pattern p5 = Pattern.compile("(\r|\n|.)*(-ixon)(\r|\n|.)*");
            Matcher m5 = p5.matcher(comParameters);
            if (m5.matches()) param.flowControl = FlowControl.NONE;
            else param.flowControl = FlowControl.DTR_DSR;

            Pattern p6 = Pattern.compile("(\r|\n|.)*(cs5|cs6|cs7|cs8)(\r|\n|.)*");
            Matcher m6 = p6.matcher(comParameters);
            m6.matches();
            dataBit = m6.group(2);
            if (dataBit.equals("cs8")) param.dataBit = DataBit.DATA_BIT_8;
            else if (dataBit.equals("cs7")) param.dataBit = DataBit.DATA_BIT_7;
            else if (dataBit.equals("cs6")) param.dataBit = DataBit.DATA_BIT_6;
            else if (dataBit.equals("cs5")) param.dataBit = DataBit.DATA_BIT_5;
            //Log.d("默认参数已取�?,"GetCurrentSystemParameters");
        } catch (IOException e) {
            System.out.println(cmd + e.toString());
        }
        return param;
    }

    /**
     * get baudrate of UART
     *
     * @return baudrate of UART
     */
    public Baudrate getSerialBaudrate() {
        int value = tcgetattr(devicePath);
        switch (value) {
            case 115200:
                return Baudrate.BAUD_115200;
            case 57600:
                return Baudrate.BAUD_57600;
            case 38400:
                return Baudrate.BAUD_38400;
            case 19200:
                return Baudrate.BAUD_19200;
            case 9600:
                return Baudrate.BAUD_9600;
            case 4800:
                return Baudrate.BAUD_4800;
            case 2400:
                return Baudrate.BAUD_2400;
            case 1200:
                return Baudrate.BAUD_1200;
            case 600:
                return Baudrate.BAUD_600;
            case 300:
                return Baudrate.BAUD_300;
            default:
                return Baudrate.BAUD_UNKNOWN;
        }
    }

    /**
     * set baudrate of UART
     *
     * @param baudrate
     */
    public void setSerialBaudrate(Baudrate baudrate) {
        int value;
        switch (baudrate) {
            case BAUD_115200:
                value = 115200;
                break;
            case BAUD_57600:
                value = 57600;
                break;
            case BAUD_38400:
                value = 38400;
                break;
            case BAUD_19200:
                value = 19200;
                break;
            case BAUD_9600:
                value = 9600;
                break;
            case BAUD_4800:
                value = 4800;
                break;
            case BAUD_2400:
                value = 2400;
                break;
            case BAUD_1200:
                value = 1200;
                break;
            case BAUD_600:
                value = 600;
                break;
            case BAUD_300:
                value = 300;
                break;
            default:
                return;
        }
        tcsetattr(devicePath, value);
    }

    /**
     * set baudrate of UART
     *
     * @param baudrate
     */
    public void setSerialBaudrate(int baudrate) {
        tcsetattr(devicePath, baudrate);
    }

    public void clearBuffer() {
        tcflush(devicePath);
    }

    private native void startReadFile(String path, int[] fds);

    private native void stopReadFile(int[] fds);

    private native int readFile(int[] fds, byte[] buffer, int offset, int length, long millisecond);

    private native int tcsetattr(String path, int baudrate);

    private native int tcgetattr(String path);

    private native void tcflush(String path);

    static {
        if (isArm()) {
            System.loadLibrary("jni");
        }
    }

    /**
     * 获取cpu支持的架构类型
     *
     * @return 支持的架构字符串
     */
    public static List<String> getCpuSupportAbis() {
        String[] abis;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            abis = Build.SUPPORTED_ABIS;
        } else {
            abis = new String[]{Build.CPU_ABI, Build.CPU_ABI2};
        }
        List<String> list = new ArrayList<>();
        for (String tmp : abis) {
            list.add(tmp);
        }
        return list;
    }

    /**
     * 是否是arm架构
     *
     * @return
     */
    public static boolean isArm() {
        boolean isArm = false;
        for (String tmp : getCpuSupportAbis()) {
            if (tmp.contains("armeabi")) {
                //是arm架构
                isArm = true;
                break;
            }
        }
        return isArm;
    }
}
	