/*
 *  Copyright (C) 2006  Takashi Kasuya <kasuya@sfc.keio.ac.jp>
 *
 * This library is free software; you can redistribute it and/or
 *　modify it under the terms of the GNU Lesser General Public
 *　License as published by the Free Software Foundation; either
 *　version 2.1 of the License, or (at your option) any later version.
 *　This library 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
 *　Lesser General Public License for more details.
 *
 *　You should have received a copy of the GNU Lesser General Public
 *　License along with this library; if not, write to the Free Software
 *　Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */

package jp.ac.naka.ec.plugin.serial;

import gnu.io.CommPort;
import gnu.io.CommPortIdentifier;
import gnu.io.SerialPort;
import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.Properties;

import jp.ac.naka.ec.EventSource;
import jp.ac.naka.ec.Main;
import jp.ac.naka.ec.db.DatabaseConnector;
import jp.ac.naka.ec.entity.Entity;
import jp.ac.naka.ec.entity.EntityContainer;
import jp.ac.naka.ec.entity.EntityEvent;

import org.apache.log4j.Logger;

import com.sun.media.rtsp.protocol.SetupMessage;

/**
 * シリアル通信のためのクラス。
 * シリアル入力からパーサによって解析、取得されるSensorDataオブジェクトをSerialListenerに対して配布する。
 * 
 * @author 粕谷
 * 
 */
public class SerialManager extends EventSource implements
		SerialPortEventListener {

	private static DatabaseConnector con;
	private static Logger logger = Logger.getLogger(SerialManager.class);

	private Entity source;
	private SerialInputParser parser;
	public static String commPort = "COM1";
	private int baud_rate = 9600;
	private final int timeout = 20000;

	// Thread th;

	private OutputStream out;
	private BufferedReader comReader;

	public SerialManager() {

	}

	
	/* (non-Javadoc)
	 * @see jp.ac.naka.ec.Plugin#init(jp.ac.naka.ec.entity.EntityContainer)
	 */
	public void init(EntityContainer source) throws Exception {
		this.source = source;
		// COM port setting
		parser = new DefaultParser();
		initialize();
	}


	/*
	 * (non-Javadoc)
	 * @see jp.ac.naka.ec.Plugin#init(jp.ac.naka.ec.entity.EntityContainer, java.util.Properties)
	 */
	public void init(EntityContainer source, Properties prop)
			throws Exception {
		this.source = source;
		
		if (Main.con != null) 
			SerialManager.con = Main.con;
		// COM port setting
		String port = prop.getProperty("port");
		if (port != null && !port.equals("")) {
			commPort = port;
		}
		// baud rate setting
		String baud = prop.getProperty("baudRate");
		if (baud != null && !baud.equals("")) {
			baud_rate = Integer.valueOf(baud);
		}
		
		// SerialInputParser setting
		String a = prop.getProperty("SerialInputParser");
		if (a != null && !a.equals("")) {
			// TODO
			try {
				Class clz = Class.forName(a);
				parser = (SerialInputParser) clz
						.newInstance();
				
			} catch (Exception e) {
				logger.warn("Invalid SerialInputParser Class :" + a, e);
			}
		} 
		if (parser == null)
			parser = new DefaultParser();
		
		initialize();
	}

	
	public void initialize() throws IOException {
		try {
			CommPortIdentifier portIdentifier = CommPortIdentifier
					.getPortIdentifier(commPort);
			if (portIdentifier.isCurrentlyOwned()) {
				logger.error("Error: Port is currently in use");
			} else {
				// 名前とタイムアウトを指定
				CommPort commPort = portIdentifier.open(getClass().getName(),
						timeout);

				if (commPort instanceof SerialPort) {
					SerialPort serialPort = (SerialPort) commPort;
					serialPort.setSerialPortParams(baud_rate,
							SerialPort.DATABITS_8, SerialPort.STOPBITS_1,
							SerialPort.PARITY_NONE);
					serialPort.addEventListener(this);
					serialPort.notifyOnDataAvailable(true);
					InputStream in = serialPort.getInputStream();
					out = serialPort.getOutputStream();
					comReader = new BufferedReader(new InputStreamReader(in));
				} else {
					logger.error("Error: Only serial ports are handled by this example.");
				}
			}
		} catch (Exception e) {
			throw new IOException(e.getMessage());
		}
	}


	/*
	 * (non-Javadoc)
	 * @see gnu.io.SerialPortEventListener#serialEvent(gnu.io.SerialPortEvent)
	 */
	public void serialEvent(SerialPortEvent event) {
		switch (event.getEventType()) {
		case SerialPortEvent.BI:
		case SerialPortEvent.OE:
		case SerialPortEvent.FE:
		case SerialPortEvent.PE:
		case SerialPortEvent.CD:
		case SerialPortEvent.CTS:
		case SerialPortEvent.DSR:
		case SerialPortEvent.RI:
		case SerialPortEvent.OUTPUT_BUFFER_EMPTY:
			// Data Available 以外のイベントは処理しない
			break;
		case SerialPortEvent.DATA_AVAILABLE:
			// Data Available の処理
			String buffer = null;
			try {
				while (comReader.ready()) {
					// データの読み込み
					buffer = comReader.readLine();
					EntityEvent evt = parser.parse(buffer, source);
					if (evt!= null)
						getEventDispatcher().dispatchEvent(evt);
					//System.out.println("> " + buffer);
					
					
				}
			} catch (IOException e) {
			}
			break;
		}
	}

	@Override
	public void receiveMessage(EntityEvent evt) {
		String str = evt.getMessage();
		if (str != null) {
			byte[] bytes = str.getBytes();
			try {
				out.write(bytes, 0, bytes.length);
			} catch (IOException e) {
				logger.error("Error while input message to serial", e);
			}
		}
	}
	
	public static void main(String[] args) {
		try {
			SerialManager sc = new SerialManager();
			sc.initialize();
			
		} catch (Exception e) {
			e.printStackTrace();
		}

	}


	/**
	 * 文字列解析用のパーサの設定
	 * @param parser2
	 */
	public void setSeialInputParser(SerialInputParser parser2) {
		this.parser = parser2;
	}

}
