/*

Set voice commands
0S - 0 Speed
6O - 6 Voice
99P - 99 Pitch
9V - 9 Volume
*/

import java.util.*;
import java.awt.*;
import java.awt.image.*;
import java.awt.event.*;
import java.awt.geom.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
import java.io.*;
import java.util.*;

public class DollChat extends JApplet {

	DollChatFrame frame;
	VideoPanel videoPanel = new VideoPanel();

	SocketThread sThread;
	boolean coboxConnection = false;
	boolean keepThreadAlive = true;
	boolean frameDumpOccuring = false;
	boolean voiceCommandMode = false;

	//static final int PORT_NUMBER = 10181;
	static final int PORT_NUMBER = 10001;
	//static final String HOST_NAME = "128.122.151.181";
	static final String HOST_NAME = "192.168.0.5";

	// Constants
	int frameWidth = 320;
	int frameHeight = 320;

	//int cmuWidth = 48;
	int cmuWidth = 96;
	int cmuHeight = 80;

	int videoPanelStartX = 80;
	int videoPanelStartY = 10;
	int videoPanelHeight = cmuHeight*2; //160
	int videoPanelWidth = cmuHeight*2; //192

	int statusTextWidth = 200;
	int statusTextHeight = 25;
	int statusTextStartX = 90;
	int statusTextStartY = 265;

	int reconnectButtonWidth = 140;
	int reconnectButtonHeight = 25;
	int reconnectButtonStartX = 160;
	int reconnectButtonStartY = 235;

	int voiceCommandButtonWidth = 130;
	int voiceCommandButtonHeight = 25;
	int voiceCommandButtonStartX = 20;
	int voiceCommandButtonStartY = 235;

	int chatTextWidth = 200;
	int chatTextHeight = 25;
	int chatTextStartX = 20;
	int chatTextStartY = 205;

	int sayItButtonWidth = 70;
	int sayItButtonHeight = 25;
	int sayItButtonStartX = 220;
	int sayItButtonStartY = 205;

	int frameDumpButtonStartX = 110;
	int frameDumpButtonStartY = 175;
	int frameDumpButtonWidth = 100;
	int frameDumpButtonHeight = 25;

	BufferedImage cmuImage = new BufferedImage(cmuWidth, cmuHeight, BufferedImage.TYPE_INT_BGR);

	JLabel statusLabel;

	public void init()
	{
		try
		{
			frame = new DollChatFrame();
			frame.initComponents();
			frame.setVisible(true);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	public void initThread()
	{
		keepThreadAlive = true;

		try
		{
			sThread = new SocketThread();
			coboxConnection = true;
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	public void reset()
	{
		keepThreadAlive = false;
		try
		{
			Thread.sleep(500);
		}
		catch (Exception e)
		{

		}
		initThread();
	}

	// Main entry point
	static public void main(String[] args)
	{
		DollChat chat = new DollChat();
		chat.init();
	}


	class DollChatFrame extends javax.swing.JFrame
	{
		JPanel chatPanel = new JPanel();
		JTextField chatField = new JTextField(40);
		JButton	chatButton = new JButton();
		JButton resetButton = new JButton("Reset Connection");
		JButton frameDumpButton = new JButton("Grab Frame");
		JButton voiceCommandButton = new JButton("Voice Command");

		// Default Constructor
		public DollChatFrame() {
		}


		public void initComponents() throws Exception
		{
			// Frame initialization
			setSize(frameWidth,frameHeight);
			setLocation(0,0);
			setResizable(true);

			setTitle("Doll Chat");

			// Component initialzation
			chatPanel.setLayout(null);
			chatPanel.setSize(frameWidth,frameHeight);

			chatButton.setSize(sayItButtonWidth,sayItButtonHeight);
			chatButton.setText("Say It!");
			chatButton.setLocation(sayItButtonStartX,sayItButtonStartY);
			chatPanel.add(chatButton);

			statusLabel = new JLabel("No Connection");
			statusLabel.setSize(statusTextWidth,statusTextHeight);
			statusLabel.setLocation(statusTextStartX,statusTextStartY);
			chatPanel.add(statusLabel);

			resetButton.setSize(reconnectButtonWidth,reconnectButtonHeight);
			resetButton.setLocation(reconnectButtonStartX,reconnectButtonStartY);
			chatPanel.add(resetButton);

			chatField.setSize(chatTextWidth,chatTextHeight);
			chatField.setLocation(chatTextStartX,chatTextStartY);
			chatPanel.add(chatField);

			frameDumpButton.setSize(frameDumpButtonWidth,frameDumpButtonHeight);
			frameDumpButton.setLocation(frameDumpButtonStartX,frameDumpButtonStartY);
			chatPanel.add(frameDumpButton);

			voiceCommandButton.setSize(voiceCommandButtonWidth,voiceCommandButtonHeight);
			voiceCommandButton.setLocation(voiceCommandButtonStartX,voiceCommandButtonStartY);
			chatPanel.add(voiceCommandButton);

			videoPanel.setLocation(videoPanelStartX,videoPanelStartY);
			videoPanel.setSize(videoPanelWidth,videoPanelHeight);
			videoPanel.setBackground(Color.WHITE);
			chatPanel.add(videoPanel);

			getContentPane().setLayout(null);
			getContentPane().add(chatPanel);

			setDefaultCloseOperation(EXIT_ON_CLOSE);

			voiceCommandButton.addActionListener(
				new ActionListener()
				{
					public void actionPerformed(ActionEvent event)
					{
						voiceCommandMode = true;
						statusLabel.setText("Voice Command Mode");
					}
				}
			);

			resetButton.addActionListener(
				new ActionListener()
				{
					public void actionPerformed(ActionEvent event)
					{
						try
						{
							reset();
						}
						catch (Exception e)
						{

						}
					}
				}
			);

			chatButton.addActionListener(
				new ActionListener()
				{
					public void actionPerformed(ActionEvent event)
					{
						String message = chatField.getText();

						try
						{
							if (coboxConnection)
							{
								System.out.println("Sending Message");
								statusLabel.setText("Sending Message");
								sThread.sendMessage(message);
							}
							else
							{
								statusLabel.setText("No Connection!");
								System.out.println("No Connection!");
							}
						}
						catch (Exception e)
						{

						}
						chatField.setText("");
					}
				}
			);

			chatField.addKeyListener(new java.awt.event.KeyListener() {
				public void keyTyped(java.awt.event.KeyEvent keyEvent)
				{
				}

				public void keyPressed(java.awt.event.KeyEvent keyEvent)
				{
				}

				public void keyReleased(java.awt.event.KeyEvent keyEvent)
				{
					if (keyEvent.getKeyCode() == KeyEvent.VK_ENTER)
					{
	                         		String message = chatField.getText();

						try
						{
							if (coboxConnection)
							{
								statusLabel.setText("Sending Message");
								sThread.sendMessage(message);
							}
							else
							{
								statusLabel.setText("No Connection!");
								System.out.println("No Connection!");
							}
						}
						catch (Exception e)
						{

						}
						chatField.setText("");
					}

				}
			});

			frameDumpButton.addActionListener(
				new ActionListener()
				{
					public void actionPerformed(ActionEvent event)
					{
						try
						{
							if (coboxConnection && !frameDumpOccuring)
							{
								statusLabel.setText("Sending Frame Dump");
								System.out.println("Sending Frame Dump Command");
								sThread.sendMessage("df");
								frameDumpOccuring = true;
							}
							else
							{
								System.out.println("No Connection!");
								statusLabel.setText("No Connection!");
							}
						}
						catch (Exception e)
						{

						}
					}
				}
			);
		}
	}

	/* To Make the Socket Connection from Java instead of from the CoBox, a more portable app */
	class SocketThread extends Thread
	{
		protected Socket thisSocket;

		protected DataOutputStream outOutput;
		protected DataInputStream inInput;

		//	InputStream is;
		//	is = thisSocket.getInputStream();
		//	inInput = new DataInputStream(is);

		//in = new BufferedReader(new InputStreamReader(thisSocket.getInputStream()));

		public SocketThread() throws IOException
		{
			InetAddress addr = InetAddress.getByName(HOST_NAME);
			System.out.println("Connecting to: " + addr.toString());
			statusLabel.setText("Connecting");
			thisSocket = new Socket(addr, PORT_NUMBER);

			setPriority(Thread.MAX_PRIORITY);
			start();
		}

		public void run()
		{
			try
			{
				initialize();
				System.out.println("Connected");
				statusLabel.setText("Connected");

				voiceCommandMode = true;
				sendMessage("0S");
				voiceCommandMode = true;
				sendMessage("6O");
				voiceCommandMode = true;
				sendMessage("99P");
				voiceCommandMode = true;
				sendMessage("9V");

				while(keepThreadAlive)
				{
					if (!frameDumpOccuring)
					{
						//int incomingVar = inInput.readInt();
						//String incomingVar = inInput.readUTF();

						thisSocket.setSoTimeout(0);
						char incomingVar = inInput.readChar();
						System.out.print(incomingVar);
					}
					else
					{
						// frameDumpOccuring
						// Frame Dump Should Be Occuring

						System.out.println("Dumping Frame");
						statusLabel.setText("Dumping Frame");
						thisSocket.setSoTimeout(1000);

						int currData;
						int col = 0;
						int row = 0;
						int numPixels = 0;
						int numCols = 0;
						int newPixels[] = new int[cmuHeight*cmuWidth];
						System.out.println("Array Length: " + newPixels.length);


						boolean stillDumping = true;

						try
						{
							System.out.println("Reading Byte");
							//currData = inInput.readByte();
							currData = inInput.readByte();

							while (stillDumping && currData != 0)
							{
								//System.out.println("Still Dumping");

								if(currData == 1)  // New frame
								{
									System.out.println("New Frame");
									col = 0;
									row = 0;
								}
								else if(currData == 2) // New col
								{
									//System.out.println("New Column");
									col += 2;
									//col++;
									row = 0;

									//numCols++;
									numCols += 2;
								}
								else if(currData == 3) // End frame
								{
									System.out.println("End Frame");
									// Set the image to be the new pixels
									//source.newPixels(0,0,width,height);

									row = 0;
									col = 0;

									System.out.println("Number of Pixels: " + numPixels);
									System.out.println("Number of Cols: " + numCols);
									System.out.println("Pix per Col: " + numPixels/numCols);

									stillDumping = false;
									frameDumpOccuring = false;
									//cmuImage.setRGB(int startX, int startY, int w, int h, int[] rgbArray, int offset, int scansize);
									//cmuImage.setRGB(1,1,cmuWidth-1,cmuHeight-1,newPixels,0,cmuWidth);
									cmuImage.setRGB(0,0,cmuWidth,cmuHeight,newPixels,0,cmuWidth);
									videoPanel.repaint();
									statusLabel.setText("Frame Dump Complete");
								}
								else
								{
									numPixels += 2;
									//numPixels++;
									//System.out.println("Pixels: " + numPixels);
									//System.out.println("A Red Pixel");
									// A pixel!

									int red,green,blue;

									// 3 bytes per pixel
									red = currData;
									//green = inInput.readByte();
									green = inInput.readByte();
									//System.out.println("A Green Pixel");
									//blue = inInput.readByte();
									blue = inInput.readByte();
									//System.out.println("A Blue Pixel");

									int pix = 0;
									pix += blue;
									pix += green << 8;
									pix += red << 16;

									Color pixColor= new Color(pix);

									// Must be pixel doubling

									pix=0;
									pix+=blue;
									pix+=green<<8;
									pix+=red<<16;

									Color pixColor2= new Color(pix);

									//if (row < cmuHeight && col < cmuWidth)
									if (numPixels < newPixels.length)
									{

										newPixels[numPixels] = pixColor.getRGB();
										newPixels[numPixels+1] = pixColor2.getRGB();
										//newPixels[row*cmuWidth+col] = pixColor.getRGB();
										//newPixels[row*cmuWidth+col+1] = pixColor2.getRGB();
										//newPixels[(row+1)*cmuWidth+col] = pixColor2.getRGB();
										//pixels[row*cmuWidth+col+1] = pixColor2.getRGB();
										row++;
									}
									else
									{
										System.out.println("Pixel out of range");
									}
								}

								currData = inInput.readByte();
							} // while data != 0 and stillDumping
						}
						catch (java.net.SocketTimeoutException ioe)
						{
							frameDumpOccuring = false;
							stillDumping = false;
							System.out.println("Number of Pixels: " + numPixels);
							System.out.println("Number of Cols: " + numCols);
							System.out.println("Pix per Col: " + numPixels/numCols);

							//cmuImage.setRGB(1,1,cmuWidth,cmuHeight,newPixels,0,cmuWidth);
							cmuImage.setRGB(1,1,newPixels.length/cmuHeight-1,cmuHeight-1,newPixels,0,cmuWidth);
							videoPanel.repaint();
							System.out.println("Socket Timeout Inner");
							statusLabel.setText("Frame Dump Complete");
						}
						catch(Exception e)
						{
							e.printStackTrace();
						}

						//source.newPixels(0,0,width,height);
						frameDumpOccuring = false;
					} // else frameDumpOccuring

				} // while keepThreadAlive
				thisSocket.close();
				System.out.println("Closing Connection");
				statusLabel.setText("Closing Connection");
			}
			catch (Exception ioe)
			{
				ioe.printStackTrace();
			};
		} // run

		protected void initialize() throws IOException
		{
			InputStream is;
			is = thisSocket.getInputStream();
			inInput = new DataInputStream(is);

			OutputStream os;
			os = thisSocket.getOutputStream();
			outOutput = new DataOutputStream(os);
		}

		public void sendMessage(String message) throws IOException
		{
			//outOutput.writeUTF(message + "\n");
			if (voiceCommandMode)
			{
				outOutput.writeByte(1);
				for (int i = 0; i < message.length(); i++)
				{
					outOutput.writeByte(message.charAt(i));
					System.out.print(message.charAt(i));
				}
				voiceCommandMode = false;
				statusLabel.setText("Voice Command Sent");
			}
			else
			{
				for (int i = 0; i < message.length(); i++)
				{
					outOutput.writeByte(message.charAt(i));
					System.out.print(message.charAt(i));
				}
				outOutput.writeByte(13);
				System.out.print(13);
				statusLabel.setText("Message Sent");
			}
		}

		public void sendMessage(int message) throws IOException
		{
			outOutput.writeByte(message);
			System.out.print(message);
		}
	}

	private class VideoPanel extends javax.swing.JPanel {
		// Default Constructor
		public VideoPanel() {
		}

		// Override Paint Method
		protected void paintComponent(Graphics g)
		{
			super.paintComponent(g);
			//g.drawImage(cmuImage, 0, 0, this);
			g.drawImage(cmuImage, 0, 0, cmuImage.getWidth() * 2, cmuImage.getHeight() * 2, this);
		}
	}

}