/*
 *
 * MythRemote - a custom remote control daemon for mythfrontend
 *
 */

import java.net.*;
import java.util.*;
import java.io.*;

public class MythRemote extends Thread
{
	//Settings
	public static int PORT=808;
	public static String mythLocation="localhost";
	public static int mythPort=6546;

	//Global static vars
	private static boolean init=false;
	private static boolean kill;
	private static ServerSocket serverSocket;
	private static String html;
	private static Socket myth;

	//Global instance vars
	private Socket s;

	/*
	 * main()
	 */
	public static void main(String[] args)
	{
		startMythRemote();
	}

	/*
	 * Runs the chat server
	 */
	public static void startMythRemote()
	{
		print("MythRemote: STARTED");

		init();

		try
		{
			serverSocket=new ServerSocket(PORT,10);
		}
		catch (IOException e)
		{
			print("ERROR: Couldn't open port: "+PORT,e);
			return;
		}

		try
		{
			while(!kill)
			{
				new MythRemote(serverSocket.accept());
			}
		}
		catch(Exception ex) { print("EX: MythRemote()",ex); }

		try { serverSocket.close(); }
		catch(Exception ex) {}
	}


	/*
	 * inits the chat server
	 */
	public static void init()
	{
		if(init)
			return;

		serverSocket=null;
		kill=false;
		html="";
		myth=null;
		loadCode();
	}

	private MythRemote()
	{
	}

	/*
	 * stop the server
	 */
	public static void kill()
	{
		print("STOP requested");
		kill=true;
		try { serverSocket.close(); }
		catch(Exception ex) {}
	}

	/*
	 * Serves the chat via a socket
	 */
	public MythRemote(Socket s)
	{
		this.s=s;
		start();
	}

	/*
	 * run()
	 */
	public void run()
	{
		try
		{
			PrintWriter out=new PrintWriter(s.getOutputStream(),true);
			BufferedReader in=new BufferedReader(new InputStreamReader(s.getInputStream()));

			String ret;
			try
			{
				ret=service(parseSocket(in));
				out.print(ret);
			}
			catch(Exception ex)
			{
				out.print(getErrorHeader());
				print("EX: service()",ex);
			}
			out.flush();

			out.close();
			in.close();
		}
		catch(Exception ex) { print("EX: run()",ex); }

		try { s.close(); }
		catch(Exception ex) {}
	}

	/*
	 * reads the socket, creates a hashtable of variables
	 */
	private String parseSocket(BufferedReader in) throws Exception
	{
		String action="";
		String header;

		header=in.readLine();

		if(header==null || header.length()==0)
			throw new Exception("Empty request");

		StringTokenizer st=new StringTokenizer(header," ");

		if(!st.hasMoreTokens())
			throw new Exception("Malformed HTTP header");

		if(st.nextToken().equals("GET") && st.hasMoreTokens())
		{
			StringTokenizer st1=new StringTokenizer(st.nextToken(),"?");
			action=st1.nextToken().toLowerCase();
		}
		else
			throw new Exception("Unsupported HTTP");

		if(action.startsWith("/"))
			action=action.substring(1);

		return action;
	}

	/*
	 * Services the request
	 */
	private String service(String action) throws Exception
	{
		doAction(action);

		return getResponseHeader()+html;
	}

	/*
	 * HTTP headers
	 */
	private String getResponseHeader()
	{
		return "HTTP/1.1 200 OK\r\n" +
		       "Server: MythRemote Server\r\n" +
		       "Content-Type: text/html\r\n" +
		       "Connection: close\r\n" +
		       "Cache-Control: no-cache, must-revalidate\r\n" +
		       "Pragma: no-cache\r\n\r\n";
	}

	private String getErrorHeader()
	{
		return "HTTP/1.1 400 BAD REQUEST\r\n\r\n";
	}

	private void doAction(String action)
	{
		print("Action: '"+action+"'");

		if(action.length()==0)
			return;

		if((myth==null || !myth.isConnected()) && !connectToMyth())
			return;

		try
		{
			int len;
			char cbuf[]=new char[1024];
			InputStreamReader in=new InputStreamReader(myth.getInputStream());
			len=in.read(cbuf,0,1024);
			if(!String.valueOf(cbuf,0,len).endsWith("# "))
			{
				myth=null;
				return;
			}
		}
		catch(Exception e)
		{
			myth=null;
			return;
		}

		try
		{
			PrintWriter out=new PrintWriter(myth.getOutputStream(),true);
			out.print("key "+action+"\n");
			out.flush();
			print("Action sent to myth");
		}
		catch(Exception e)
		{
			myth=null;
			return;
	       	}
	}

	private boolean connectToMyth()
	{
		try
		{
			myth=new Socket(mythLocation,mythPort);
			print("Connected to "+mythLocation+":"+mythPort);
			return true;
		}
		catch(Exception e)
		{
			print("ERROR: cannot connect to "+mythLocation+":"+mythPort);
			myth=null;
			return false;
		}
	}

	/*
	 * rez chat html
	 */
	/* RC.START INDEX.HTML
	<html>
	<head>
	<title>Remote Control</title>
	<style type="text/css">
	body
	{
		color: white;
		size: +1;
	}
	td
	{
		text-align: center; 
		width: 33%;
	}
	.go
	{
		background-color: green;
	}
	.stop
	{
		background-color: red;
	}
	.nav
	{
		background-color: purple;
	}
	.extra
	{
		background-color: gray;
	}
	.enter
	{
		background-color: blue;
	}
	</style>
	</head>
	<body>
	<table width=100% height=100%>
		<tr>
			<td valign=top class=stop onClick="document.location.href='/escape';">X</td>
			<td valign=top class=nav onClick="document.location.href='/up';">^</td>
			<td valign=top class=go onClick="document.location.href='/enter';">O</td>
		</tr>
		<tr>
			<td valign=top class=nav onClick="document.location.href='/left';">&lt;</td>
			<td valign=top class=enter onClick="document.location.href='/space';"></td>
			<td valign=top class=nav onClick="document.location.href='/right';">&gt;</td>
		</tr>
		<tr>
			<td valign=top class=extra onClick="document.location.href='/q';">Prev</td>
			<td valign=top class=nav onClick="document.location.href='/down';">v</td>
			<td valign=top class=extra onClick="document.location.href='/z';">Next</td>
		</tr>
		<tr>
			<td valign=top class=extra onClick="document.location.href='/p';">Pause</td>
			<td valign=top class=extra onClick="document.location.href='/m';">Menu</td>
			<td valign=top class=extra onClick="document.location.href='/w'">Aspect</td>
		</tr>
	</table>
	<form name=sub action=/ method=GET></form>
	<script>
	document.onkeydown=checkKeycode;
	function checkKeycode(e)
	{
		var keycode;
		if (window.event) keycode = window.event.keyCode;
		else if (e) keycode = e.which;
		else return;
		if((keycode>=65 && keycode<=90) || (keycode>=48 && keycode<=57))
		{
			document.sub.action="/"+String.fromCharCode(keycode);
			document.sub.submit();
			return false;
		}
		switch(keycode)
		{
			case 32:
				document.sub.action="/enter";
				break;
			case 38:
				document.sub.action="/up";
				break;
			case 37:
				document.sub.action="/left";
				break;
			case 40:
				document.sub.action="/down";
				break;
			case 39:
				document.sub.action="/right";
				break;
			default:
				return;
		}
		document.sub.submit();
		return false;
	}
	</script>
	</body>
	</html>
	RC.STOP */

	/*
	 * loads RC code from source
	 */
	public static void loadCode()
	{
		String start="/* rc.start ";
		String end="rc.stop */";
		String line;
		String id="";
		String content="";
		int space=0;
		BufferedReader in;
		boolean js=false;
		try { in=new BufferedReader(new FileReader("MythRemote.java")); }
		catch(Exception ex) { print("EX: loadCode(), open file",ex); return; }

		try
		{
			while((line=in.readLine())!=null)
			{
				if(line.trim().toLowerCase().startsWith(start))
				{
					js=true;
					id=line.trim().toLowerCase().substring(start.length());
					print("Loading: "+id);
					space=line.toLowerCase().indexOf(start);
					content="";
				}
				else if(line.trim().toLowerCase().endsWith(end))
				{
					js=false;
					if(id.equals("index.html"))
						html=content;
				}
				else if(js)
				{
					if(space>0 && line.length()>=space && line.substring(0,space).trim().equals(""))
						line=line.substring(space);
					content+=line+"\n";
				}
			}
		}
		catch(Exception ex) { print("EX: loadCode(), read file",ex); }
		try { in.close(); }
		catch(Exception ex) {}
	}

	/*
	 * debug print to a file
	 */
	public static void print(String str)
	{
		print(str,null);
	}

	public static void print(String str,Exception ex)
	{
		System.out.println(str);
		if(ex!=null)
			ex.printStackTrace();
	}
}

