/*
    Copyright 2015-2018 Will Winder

    This file is part of Universal Gcode Sender (UGS).

    UGS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    UGS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with UGS.  If not, see <http://www.gnu.org/licenses/>.
*/
package com.willwinder.universalgcodesender.connection;

import jssc.SerialPort;
import jssc.SerialPortEventListener;
import jssc.SerialPortEvent;
import jssc.SerialPortException;

/**
 * A serial connection object implementing the connection API.
 *
 * @author wwinder
 */
public class JSSCConnection extends Connection implements SerialPortEventListener {
    @Deprecated private String lineTerminator;

    // General variables
    private SerialPort serialPort;
    private StringBuilder inputBuffer = null;

    public JSSCConnection() {
        this("\r\n");
    }
    
    public JSSCConnection(String terminator) {
        lineTerminator = terminator;
    }
    
    @Deprecated public void setLineTerminator(String lt) {
        this.lineTerminator = lt;
    }
    
    @Deprecated public String getLineTerminator() {
        return this.lineTerminator;
    }
    @Override
    synchronized public boolean openPort(String name, int baud) throws Exception {
        this.inputBuffer = new StringBuilder();
        
        this.serialPort = new SerialPort(name);
        this.serialPort.openPort();
        this.serialPort.setParams(baud, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE, true, true);
        this.serialPort.addEventListener(this);

        if (this.serialPort == null) {
            throw new Exception("Serial port not found.");
        }

        return serialPort.isOpened();
    }
        
    @Override
    public void closePort() throws Exception {
        if (this.serialPort != null) {
            try {
                this.serialPort.removeEventListener();

                if (this.serialPort.isOpened()) {
                    this.serialPort.closePort();
                }
            } finally {
                this.inputBuffer = null;
                this.serialPort = null;
            }
        }
    }

    @Override
    public boolean isOpen() {
        return serialPort != null && serialPort.isOpened();
    }

    /**
     * Sends a command to the serial device. This actually streams the bits to
     * the comm port.
     * @param command   Command to be sent to serial device.
     */
    @Override
    public void sendStringToComm(String command) throws Exception {
        this.serialPort.writeString(command);
    }
        
    /**
     * Immediately sends a byte, used for real-time commands.
     */
    @Override
    public void sendByteImmediately(byte b) throws Exception {
        this.serialPort.writeByte(b);
    }
    
    /**
     * Reads data from the serial port. RXTX SerialPortEventListener method.
     */
    @Override
    public void serialEvent(SerialPortEvent evt) {
        if (inputBuffer == null) {
            inputBuffer = new StringBuilder();
        }

        try {
            byte[] buf = this.serialPort.readBytes();
            if (buf != null && buf.length > 0) {
                String s = new String(buf, 0, buf.length);
                inputBuffer.append(s);
                // Check for line terminator and split out command(s).
                if (inputBuffer.toString().contains(comm.getLineTerminator())) {
                    // Split with the -1 option will give an empty string at
                    // the end if there is a terminator there as well.
                    String []commands = inputBuffer.toString().split(comm.getLineTerminator(), -1);
                    for (int i=0; i < commands.length; i++) {
                        // Make sure this isn't the last command.
                        if ((i+1) < commands.length) {
                            comm.responseMessage(commands[i]);
                        // Append last command to input buffer because it didn't have a terminator.
                        } else {
                            inputBuffer = new StringBuilder().append(commands[i]);
                        }
                    }
                }
            }
        } catch ( Exception e ) {
            e.printStackTrace();
            System.exit(-1);
        }
    }
}

