/*
 * Copyright (C) 2007 ETH Zurich
 *
 * This file is part of Fosstrak (www.fosstrak.org).
 *
 * Fosstrak is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software Foundation.
 *
 * Fosstrak 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 Fosstrak; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */

package org.fosstrak.ale.client;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;

import org.apache.log4j.Logger;
import org.fosstrak.ale.util.DeserializerUtil;
import org.fosstrak.ale.wsdl.ale.epcglobal.ImplementationException;
import org.fosstrak.ale.wsdl.ale.epcglobal.ImplementationExceptionResponse;
import org.fosstrak.ale.xsd.ale.epcglobal.ECReports;
import org.fosstrak.reader.rp.proxy.RPProxyException;

/**
 * This class listen to a specified port for ec reports and notifies his subscribers about new ec reports.
 * 
 * @author regli
 */
public class ReportHandler implements Runnable {

	/** the logger */
	private static Logger log = Logger.getLogger(ReportHandler.class);
	
	/** the thread */
	private final Thread thread;
	
	/** contains the subscribers of this ReportHandler */
	private final List<ReportHandlerListener> listeners = new Vector<ReportHandlerListener>();
	
	/** server socket to communicate with the ALE */
	private final ServerSocket ss;

	/**
	 * Constructor opens the server socket and starts the thread.
	 * 
	 * @param port on which the ALE notifies
	 * @throws ImplementationException if server socket could not be created on specified port.
	 */
	public ReportHandler(int port) throws ImplementationExceptionResponse {
		
		try {
			ss = new ServerSocket(port);
		} catch (IOException e) {
			throw new ImplementationExceptionResponse(e.getMessage());
		}
		
		thread = new Thread(this);
		thread.start();
		
	}
	
	/**
	 * This mehtod adds a new subscriber to the list of listeners.
	 * 
	 * @param listener to add to this ReportHandler
	 */
	public void addListener(ReportHandlerListener listener) {
		
		listeners.add(listener);
		
	}
	
	/**
	 * This method removes a subscriber from the list of listeners.
	 * 
	 * @param listener to remove from this ReportHandler
	 */
	public void removeListener(ReportHandlerListener listener) {
		
		listeners.remove(listener);
		
	}
	
	/**
	 * This method contains the main loop of the thread, in which data is read from the socket
	 * and forwarded to the method notifyListeners().
	 */
	public void run() {
		while (true) {
			try {
				Socket s = ss.accept();
				BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream()));
				
				String data = in.readLine();
				// ignore the HTTP header
				data = in.readLine();
				data = in.readLine();
				data = in.readLine();
				data = in.readLine();

				StringBuffer buffer = new StringBuffer();
				while (null != data) {
					buffer.append(data);
					data = in.readLine();
				}
				log.debug(buffer.toString());

				// create a stream from the buffer
				InputStream parseStream = new ByteArrayInputStream(
						buffer.toString().getBytes());

				// parse the string
				ECReports reports = DeserializerUtil.deserializeECReports(parseStream);
				if (null != reports) {
					notifyListeners(reports);
				}
				s.close();
			} catch (Exception e) {
				log.error(String.format("Could not receive report: %s", 
						e.getMessage()));
			}
		}

	}
	
	/**
	 * This method stops the thread and closes the socket
	 */
	public void stop() {
		
		// stop thread
		if (thread.isAlive()) {
			thread.interrupt();
		}
		
		// close socket
		try {
			ss.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}
	
	/**
	 * This method starts the ReportHandler.
	 * 
	 * @param args command line arguments, which can contain the port number
	 * @throws RPProxyException if something goes wrong while creating the ReportHandler
	 */
	public static void main(String[] args) throws ImplementationExceptionResponse {
		
		int port = 9000;
		if (args.length == 1) {
			try {
				port = Integer.parseInt(args[0]);
			} catch (NumberFormatException e) {}
		}
		new ReportHandler(port);
		
	}
	
	//
	// private
	//
	
	/**
	 * dispatch the reports.
	 * 
	 * @param ecReports the reports.
	 * @throws Exception 
	 */
	private void notifyListeners(ECReports ecReports) throws Exception {
			
		Iterator<ReportHandlerListener> listenerIt = listeners.iterator();
		while (listenerIt.hasNext()) {
			listenerIt.next().dataReceived(ecReports);
		}
	
	}

}