/*
 * @(#)WebInterface.java - provides simple remote http access
 *
 * Copyright (C) 2005 by dvb.matt, All Rights Reserved.
 * 
 * This file is part of ProjectX, a free Java based demux utility.
 * By the authors, ProjectX is intended for educational purposes only, 
 * as a non-commercial test project.
 * 
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

package net.sourceforge.dvb.projectx.net;

import java.net.ServerSocket;
import java.net.Socket;
import java.net.BindException;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.IOException;

import java.util.StringTokenizer;

import net.sourceforge.dvb.projectx.common.Common;
import net.sourceforge.dvb.projectx.common.Keys;
import net.sourceforge.dvb.projectx.common.JobCollection;
import net.sourceforge.dvb.projectx.xinput.XInputFile;

public class WebInterface implements Runnable {

	private Thread thread = null;

	private long request_number = 0;

	private String last_request = "";
	private String return_string = "";
	private String access_string;

	private boolean isOnline = false;
	private boolean showLog = false;

	private ServerSocket serverSocket = null;
	private Socket connection = null;

	/**
	 *
	 */
	public void run() 
	{
		Thread myThread = Thread.currentThread();

		while (thread == myThread)
		{
			if (!runServer())
				break;

			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {}
		}

		isOnline = false;
		showLog = false;
	}

	public void start()
	{
		if (thread == null)
		{
			thread = new Thread(this, "WebIF");
			thread.setPriority(Thread.MIN_PRIORITY);
			thread.start();

			Common.setMessage("-> re-/start WebIFServer on Port: " + Common.getSettings().getProperty(Keys.KEY_WebServerPort));
		}
	}

	/**
	 *
	 */
	public void stop()
	{
		connection = null;

		try {
			if (serverSocket != null)
				serverSocket.close();

		} catch (Exception ie) { 
		}

		thread = null;
		System.gc();

		Common.setMessage("-> close WebIFServer on Port: " + Common.getSettings().getProperty(Keys.KEY_WebServerPort));
	}

	/**
	 *
	 */
	private boolean runServer() 
	{
		int port_change;
		int port = -1;

		try {

			request_number = 0;

			while (true)
			{
				if ((port_change = getPort()) < 0)
					return false;

				if ((access_string = getAccess()) == null)
					return false;

				isOnline = true;

				// init server
				if (port_change != port)
				{
					port = port_change;
					serverSocket = new ServerSocket(port);
				}

				connection = serverSocket.accept(); // awaiting conn.

				BufferedReader br = new BufferedReader(new InputStreamReader(connection.getInputStream()));

				request_number++;

				if (!getRequest(br.readLine(), connection)) //resolve http command
					return_string = "<B>missing or wrong keyword</B>";

				// answer
				PrintWriter pw = new PrintWriter(connection.getOutputStream());

				pw.println(buildSite(return_string));
				pw.flush();

				connection.close();

			}

		} catch (Exception e) { 

			Common.setMessage("" + e);
			Common.setMessage("" + e.getMessage(), true);

			connection = null;

			try {
				serverSocket.close();

			} catch (Exception ie) { 
			}

		}


		System.gc();

		return true;
	}

	/**
	 *
	 */
	private int getPort() 
	{
		int value = -1;

		try {
			value = Common.getSettings().getIntProperty(Keys.KEY_WebServerPort);

			if (value < 0 || value > 65535)
				throw new Exception();

		} catch (Exception e) {
			Common.setMessage("!> WebIF: invalid port number: '" + value + "'", true);
			value = -1;
		}

		return value;
	}

	/**
	 *
	 */
	private String getAccess() 
	{
		String str = null;

		try {
			str = Common.getSettings().getProperty(Keys.KEY_WebServerAccess).trim();

			if (str == null || str.length() == 0)
				throw new Exception();

		} catch (Exception e) {
			Common.setMessage("!> WebIF: invalid access_string: '" + str + "'", true);
			str = null;
		}

		return str;
	}

	/**
	 *
	 */
	public boolean isOnline() 
	{
		return isOnline;
	}

	/**
	 *
	 */
	private String buildSite(String body) 
	{
		String str = 
			"HTTP/1.0 200 Ok\n" + 
			"Content-type: text/html\n\n" +
			"<HTML><HEAD>" + 
			"<META HTTP-EQUIV=\"PRAGMA\" CONTENT=\"NO-CACHE\">\n" +
			"<META HTTP-EQUIV=\"EXPIRES\" CONTENT=\"0\">\n" +
			"<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"PRIVATE\">\n" +
			"<TITLE>ProjectX Status of " + 
			Common.getVersionName() + "/" + Common.getVersionDate() +
			"</TITLE></HEAD>\n" +
			"<BODY><H1>ProjectX WebInterface</H1>\n" +
			"<BR>request# " + request_number + " - " + Common.getVersionName() + " / " + Common.getVersionDate() +
			" - " + Common.getDateAndTime() +
			"<BR><PRE>" + 
			body + 
			"</PRE></BODY></HTML>";

		return str;
	}

	/**
	 *
	 */
	private boolean getRequest(String str, Socket connection) 
	{
		last_request = str;

		// log the msg
		Common.setMessage("--> request# " + request_number + " from " + connection.getInetAddress().getHostAddress() + " - " + str);

		return_string = "";

		str = str.trim();

		int index_GET = str.indexOf("GET /");

		// only 'get' cmd accepted
		if (index_GET >= 3 || index_GET < 0)
			return false;

		// only http accepted
		if (!str.endsWith("HTTP/1.0") && !str.endsWith("HTTP/1.1"))
			return false;

		// raw parameters
		str = str.substring(index_GET + 5, str.indexOf("HTTP/1"));
		str = str.trim();

		// user access string 
		if (!str.startsWith(access_string))
			return false;

		StringTokenizer st = new StringTokenizer(str, "&");

		while (st.hasMoreTokens())
			getCommand(st.nextToken());

		if (showLog)
			return_string += getLog();

		// std answer body
		return_string += getCollection();

		return true;
	}

	/**
	 *
	 */
	private void getCommand(String str) 
	{
		if (str.equals("addcoll"))
		{
			Common.addCollection();
			Common.setActiveCollection(Common.getCollectionListSize() - 1);
			updateCollectionView();
		}

		else if (str.equals("removecoll"))
		{
			if (Common.removeCollection(Common.getActiveCollection()))
				updateCollectionView();
		}

		else if (str.equals("exit"))
			Common.exitApplication(0);

		else if (str.equals("start"))
		{
			if (Common.startProcess())
				Common.startMainProcess();
		}

		else if (str.equals("stop"))
			Common.breakMainProcess();

		else if (str.equals("getlog"))
			showLog = true;

		else if (str.equals("hidelog"))
			showLog = false;

		else if (str.equals("filelist"))
			return_string += getInputFileList();

		else if (str.equals("getsettings"))
			return_string += getSettings();

		else if (str.startsWith("addfile"))
		{
			JobCollection collection = Common.getCollection();

			if (collection != null)
			{
				Object[] files = Common.reloadInputDirectories();

				int index = Integer.parseInt(str.substring(7));

				if (files.length > index)
					collection.addInputFile(files[index]);
			}

			updateCollectionView();
		}

		else if (str.startsWith("removefile"))
		{
			JobCollection collection = Common.getCollection();

			if (collection != null)
				collection.removeInputFile(Integer.parseInt(str.substring(10)));

			updateCollectionView();
		}

		else if (str.startsWith("setcoll"))
		{
			Common.setActiveCollection(Integer.parseInt(str.substring(7)));
			updateCollectionView();
		}
	}

	/**
	 *
	 */
	private void updateCollectionView()
	{
		Common.getGuiInterface().showActiveCollection(Common.getActiveCollection());
	}

	/**
	 *
	 */
	private String getInputFileList() 
	{
		String str = "";

		Object[] files = Common.reloadInputDirectories();

		str += "<HR><I>List of available files:</I>";

		for (int i = 0; i < files.length; i++)
			str += "<BR><A HREF=" + access_string + "&addfile" + i + ">add file</A> " + i + " - <B>" + files[i].toString() + "</B>";

		return str;
	}

	/**
	 *
	 */
	private String getCollection() 
	{
		String str = "";

		//exit
		str += "<HR><A HREF=" + access_string + "&exit>shutdown remote application</A>";

		//settings
		str += "<HR><A HREF=" + access_string + "&getsettings>get current settings</A>";

		//status
		str += "<HR><I>Process Status:</I> <A HREF=" + access_string + "&getlog>(show message log)</A>";
		str += "<BR>" +	Common.getProcessedPercent() + "% - " + Common.getStatusString();
		str += "<BR><A HREF=" + access_string + "&start>start processing</A>";
		str += " / <A HREF=" + access_string + "&stop>stop processing</A>";

		//collection
		str += "<HR><I>Collection access:</I>";
		str += "<BR><B>active Collection: " + Common.getActiveCollection() + "</B>";
		str += "<BR><A HREF=" + access_string + "&removecoll>remove active Collection</A>";
		str += " / <A HREF=" + access_string + "&addcoll>add new Collection</A>";
		str += "<BR>choose other Collection:";

		for (int i = 0, j = Common.getCollectionListSize(); i < j; i++)
			str += " <A HREF=" + access_string + "&setcoll" + i + ">" + i + "</A>";

		JobCollection collection = Common.getCollection();

		if (collection != null)
		{
			str += "<HR><I>Infos:</I>\n";
			str += "<U>Output to:</U> '" + collection.getOutputDirectory() + "'\n";
			str += collection.getShortSummary();
			str += getCollectionFiles(collection);
		}

		return str;
	}

	/**
	 *
	 */
	private String getCollectionFiles(JobCollection collection) 
	{
		String str = "";

		Object[] files = collection.getInputFiles();

		str += "<HR><I>Content:</I> <A HREF=" + access_string + "&filelist>add file from fixed directories</A>";

		for (int i = 0; i < files.length; i++)
		{
			str += "<BR>" + i + " - <B>" + files[i].toString() + "</B> <A HREF=" + access_string + "&removefile" + i + ">remove</A>";
			str += "<BR><UL>" + ((XInputFile)files[i]).getStreamInfo().getFullInfo() + "</UL>";
		}

		return str;
	}

	/**
	 *
	 */
	private String getLog() 
	{
		String str = "";

		//status
		str += "<HR><I>current processing log:</I> <A HREF=" + access_string + "&hidelog>(hide message log)</A> <A HREF=" + access_string + "&getlog>(refresh log)</A>";
		str += "<BR>" +	Common.getMessageLog();

		return str;
	}

	/**
	 *
	 */
	private String getSettings() 
	{
		String str = "";
		String line;

		//settings
		str += "<HR><I>current settings:</I> <A HREF=" + access_string + ">(hide settings)</A>";

		try {
			BufferedReader br = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(Common.getSettings().storeProperties())));

			while ((line = br.readLine()) != null)
				str += "<BR>" +	line;

		} catch (IOException e) {
		}

		return str;
	}

}