package matrix;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class LEDMatrix {
	private int 			m_rows = 0;
	private int 			m_columns = 0;
	
	private InputStream		m_in = null;
	private OutputStream	m_out = null;
	
	private	SerialPort		m_port		= null;
	private	final String	m_appName	= "PLM";
	private final int		m_speed		= 115200; // baud rate
	private String			m_devName 	= null;
	
	
	public LEDMatrix(int rows, int columns) {
		m_rows = rows;
		m_columns = columns;
	}
	
	public int getRows() { return m_rows; }
	public int getColumns() { return m_columns; }
	
	public void open(String port) throws IOException {
		m_devName = port;
		try {
			/* by default, RXTX searches only devices /dev/ttyS* and
			 * /dev/ttyUSB*, and will so not find symlinks. The
			 *  setProperty() call below helps 
			 */
			System.setProperty("gnu.io.rxtx.SerialPorts", m_devName);
			CommPortIdentifier ci =
					CommPortIdentifier.getPortIdentifier(m_devName);
			CommPort cp = ci.open(m_appName, 1000);
			if (cp instanceof SerialPort) {
				m_port = (SerialPort)cp;
			} else {
				throw new IllegalStateException("unknown port type");
			}
			m_port.setSerialPortParams(m_speed, SerialPort.DATABITS_8,
					SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
			m_port.setFlowControlMode(SerialPort.FLOWCONTROL_NONE);
			m_port.disableReceiveFraming();
			m_port.disableReceiveThreshold();
			//m_port.enableReceiveThreshold(1000);
			//Do receive timeout so we can check if the reader has to stop
			m_port.enableReceiveTimeout(1000);
			
			m_in  = m_port.getInputStream();
			m_out = m_port.getOutputStream();
		} catch (IOException e) {
			throw e;
		} catch (PortInUseException e) {
			throw new IOException(e);
		} catch (UnsupportedCommOperationException e) {
			throw new IOException(e);
		} catch (NoSuchPortException e) {
			throw new IOException(e);
		} catch (IllegalStateException e) {
			throw new IOException(e);
		}
	}
	
	public void close() throws IOException {
		m_out.close();
		m_in.close();
		m_port.close();
	}
	
	public void writeColumn(boolean[] lights) throws IOException {
		if (lights.length != m_rows)
			throw new IllegalArgumentException("Number of booleans must match number of rows!");
		int activeLights = 0;
		
		int power = 1;
		//Go backwards, the topmost one is "worth" the most
		for (int i = lights.length - 1; i >= 0; i--) {
			if (lights[i]) {
				activeLights += power;
			}
			power *= 2;
		}
		
		activeLights += 128;
		m_out.write(activeLights);
		m_out.flush();
	}
}
