import gnu.io.*;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.Reader;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.ArrayList;
import java.util.List;

import com.sun.xml.internal.bind.v2.runtime.unmarshaller.XsiNilLoader.Array;

public class KT88 {
	
    private DataInputStream portIn = null;
    private DataOutputStream portOut = null;
    private CommPortIdentifier portId = null;
    private SerialPort port = null;
    
    public static final int NoChannels = 16;
    
    public static final List<String> ChannelNames = Collections.unmodifiableList(Arrays.asList("Fp1","Fp2","F3","F4","C3","C4","P3","P4","O1","O2","F7","F8","T3","T4","T5","T6"));
    
	public static List<String> GetSerialPorts()
	{
		ArrayList<String> ports = new ArrayList<String>();
		
		CommPortIdentifier port = null;
		
		@SuppressWarnings("unchecked")
		Enumeration<CommPortIdentifier>  portList = (Enumeration<CommPortIdentifier>)CommPortIdentifier.getPortIdentifiers();
		
	    while (portList.hasMoreElements()) {
	    	port = portList.nextElement();
	    	//System.out.println(portId.getName()+", "+portId.toString());
	    	if (port.getPortType() == CommPortIdentifier.PORT_SERIAL)
	    		ports.add(port.getName());
	    }
	    return (List<String>)ports;
	}
	
	public KT88(String comPort)
	{
		CommPortIdentifier port = null;
		
		@SuppressWarnings("unchecked")
		Enumeration<CommPortIdentifier>  portList = (Enumeration<CommPortIdentifier>)CommPortIdentifier.getPortIdentifiers();
	    while (portList.hasMoreElements()) {
	    	port = portList.nextElement();
	        if (port.getPortType() == CommPortIdentifier.PORT_SERIAL && port.getName().equals(comPort)){
	        	portId=port;
	        	break;
	        }
	    }
	    if (portId==null){
	    	System.err.println("KT88. COM port "+comPort+" not found in port enumeration");
	    }

		
	}
	
	public void OpenDevice()
	{
		if (portId==null) return;
		
		 // open port
	    
	    try {
	    	
	        port = (SerialPort) portId.open(
	            KT88_BioEra.class.getName(), // Name of the application asking for the port 
	            10000   // Wait max. 10 sec. to acquire port
	        );
	        
	    } catch(PortInUseException e) {
	        System.err.println("KT88. Can't open COM port: " + e);
	        return;
	    }
	    
	 // set communication parameters
	    try {
	    		    
		    port.setSerialPortParams(
		    	921600,//57600,
	    	    SerialPort.DATABITS_8,
	    	    SerialPort.STOPBITS_1,
	    	    SerialPort.PARITY_NONE);
		    
		    port.enableReceiveTimeout(5000);
		    
	        //port.setFlowControlMode(SerialPort.FLOWCONTROL_RTSCTS_IN  | SerialPort.FLOWCONTROL_RTSCTS_OUT);
		    
	        port.setRTS(true);
	        port.setDTR(true);
	        
	    } catch(Exception e) {
	        System.err.println("KT88. Can't set port parameters: " + e);
	        ClosePort();
	        return;
	    }
	    
	    // open streams
	    try {
	    	
	    	portIn = new DataInputStream(port.getInputStream());
	    	portOut = new DataOutputStream(port.getOutputStream());
	    	
	    } catch (IOException e) {
	      System.err.println("KT88. Can't open input and/or output streams: "+e);
	      ClosePort();
	    }
	    
	    if (portIn!=null && portOut!=null)
	    {
	    	try
	    	{
	        	// open device
	        	portOut.write(0x08);
	        	portOut.flush();
	        	
	        	byte[] buffer = new byte[NoChannels*2];
	        	int n = 0;
	        	while (n<NoChannels*2)
	        		n += portIn.read(buffer,n,NoChannels*2-n);
	        	
	        	portOut.write(new byte[] {(byte) 0x83,(byte) 0x88});
	        	portOut.flush();
	        	
	    	} catch (Exception e)
	    	{
	    		System.err.print("KT88: error reading initial packet");
	    		e.printStackTrace();
	    		ClosePort();
	    	}
	    }
	}
	
	public void CloseDevice()
	{
		  if (portIn!=null && portOut!=null) {
		    	try {
		        	// close device
		        	portOut.write(0xff);
		        	
		    	} catch (Exception e) {
		    		System.err.println("KT88. Error while communicating with KT88");
		    		e.printStackTrace();
		    	}
		  }
		  
		  ClosePort();
	}
	
	private void ClosePort()
	{
	    // close streams and port
		if (portIn != null) 
			try {
				portIn.close();
				portIn = null;
			} catch (Exception e) {
				System.err.println("KT88. Error closing read stream: "+e);
			}
		
		
		if (portOut != null) 
			try {
				portOut.close();
				portOut = null;
			} catch (Exception e) {
				System.err.println("KT88. Error closing write stream: "+e);
			}
		
		if (port != null) {
			port.close();
			port = null;
		}

	}
	
	public int[] ReadPacket()
	{
		if (portIn!=null)
		{
			int bufLen = NoChannels*2+2;
			byte[] buf = new byte[bufLen];
			int bufPos = 0;
			boolean inSynch = false;
			
			while (bufPos!=bufLen || !inSynch) // get in synch
			{
				
				if (!inSynch) { // get in synch
					int i = 0;	
					for(; i < bufPos - 1; i++)
						if (buf[i]==-1 && buf[i+1]==-1) break;
			
					System.arraycopy(buf,i,buf,0,bufPos-i);
					bufPos = bufPos-i;
				}
				try {
					
					bufPos += portIn.read(buf,bufPos,bufLen - bufPos);
					
				} catch (Exception e) {
					System.err.println("KT88: error reading packet");
					e.printStackTrace();
					ClosePort();
					return null;
				}	
				inSynch = bufPos>1 && buf[0]==-1 && buf[1]==-1;
			}
			
			int[] packet = new int[NoChannels];
			for (int i = 0; i < NoChannels; i++) 
				packet[i] = (buf[2 + i*2] & 0xff) * 16 + (buf[3 + i*2] & 0x0f) - 2048;
			
			return packet;
		}
		return null;
	}
	

	public boolean isOpen()
	{
		return port!=null;
	}
	
	

}
