/*
Copyright Daniel Bisig, March 2005
*/

import java.io.*;
import java.net.*;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.geom.*;
import java.util.*;

public class AscRefVis extends JFrame implements Runnable, KeyListener
{	
	// gui
	PlotPanel plotPanel;
	int selectedSculpture = 0;
	int selectedSensor = 0;
	
	// data acquisition
	static int captureInterval = 20;
	static String s_configFile = "sound.conf";
	static String rfidKey = "rfid=";
	static int rfidKeyLength = rfidKey.length();
	// soundfiles
	static Vector soundFileCollections;
	
	int tagId = -1; 
	int oldTagId = -1;
	static long startPlayTime;

	Thread thread;
	Socket socket = null;
	DataOutputStream oStream = null;
	BufferedReader iStream = null;	
	
	public static void main(String[] args)
	{
		AscRefVis gui = new AscRefVis();
		gui.readConfigFile();	
	}

	public AscRefVis()
	{
		super("Smart Sculpture Sensor Plotter");
		
		addKeyListener(this);
		addWindowListener(new WindowAdapter()
		{
			public void windowClosing(WindowEvent e)
			{
				System.exit(0);
			}
		}
		);
		
		createSocket();
		createGui();
		
		pack();
		setVisible(true);
		
		thread = new Thread(this);
		thread.start();
	}	
	
	public void readConfigFile()
	{
		String thisLine;
		String[] stringParts;
		SoundFileCollection soundFileCollection;
		double idleLimit;
		String soundFileName;
		double soundFileDuration;
		
		soundFileCollections = new Vector();
	
		try 
		{
			FileInputStream fin =  new FileInputStream(s_configFile);
			// JDK1.1+
			BufferedReader configReader = new BufferedReader(new InputStreamReader(fin));
			while ((thisLine = configReader.readLine()) != null) 
			{  
				System.out.println(thisLine);
				
				stringParts = thisLine.split(",");
				
				idleLimit = Double.parseDouble(stringParts[0]); 
				
				soundFileCollection = new SoundFileCollection(idleLimit, false);
				
				System.out.println("New SoundFileCollection idle limit " + idleLimit);
				
				for(int i=1; i<stringParts.length; i+=2)
				{
					soundFileName = stringParts[i];
					soundFileDuration = Double.parseDouble(stringParts[i+1]); 
					
					soundFileCollection.addSoundFile(soundFileName, soundFileDuration);
					
					System.out.println("New SoundFile Name " + soundFileName + " duration " + soundFileDuration);
				}
				
				soundFileCollections.add(soundFileCollection);
			}
		}
		catch (Exception e) 
		{
			e.printStackTrace();
		}
	}
	
	public void createSocket()
	{
		try
		{
			socket = new Socket("192.168.0.101", 1522);
			oStream = new DataOutputStream(socket.getOutputStream());
			iStream = new BufferedReader(new InputStreamReader(socket.getInputStream()));
		}
		catch (UnknownHostException e)
		{
			System.err.println("Don't know about host: hostname");
		}
		catch (IOException e)
		{
			System.err.println("Couldn't get I/O for the connection to: hostname");	
		}		
	}
	
	public void createGui()
	{
		Container cPane = getContentPane();
		cPane.setLayout(new BoxLayout(cPane, BoxLayout.X_AXIS));
		
		plotPanel = new PlotPanel();
		cPane.add(plotPanel);
	}
	
	public void pollSensorData()
	{
		try
		{
			oStream.writeBytes("POLL\n");
	
			String responseLine;
			while((responseLine = iStream.readLine()) != null)
			{
				if(responseLine.indexOf("OVER") != -1) break;
				else 
				{
					updateSensorData(responseLine);
				}
			}
		}
		catch (UnknownHostException e)
		{
			System.err.println("Trying to connect to unknown host: " + e);
		}	
		catch (IOException e)
		{
			System.err.println("IOException: " + e);	
		}		
	}
	
	public void updateSensorData(String string)
	{
		String soundFileName;
		int tmp = string.indexOf(rfidKey);
		if(tmp == -1) return;
		int tagIdA, tagIdB, tagIdC;
		
		//double playDuration = ((double)(System.currentTimeMillis() - startPlayTime) / 1000.0);

		System.out.println("string " + string + " rfidKey " + rfidKey + " pos " + tmp + " substr " + string.substring(tmp + rfidKeyLength));
		
		tagIdA = Integer.parseInt(string.substring(tmp + rfidKeyLength, tmp + rfidKeyLength + 8), 16);
		tagIdB = Integer.parseInt(string.substring(tmp + rfidKeyLength + 9, tmp + rfidKeyLength + 16), 16);
		tagIdC = Integer.parseInt(string.substring(tmp + rfidKeyLength, tmp + rfidKeyLength + 2), 16) - 1;
		
		System.out.println("tagIdA " + tagIdA + " tagIdC " + tagIdC +  " oldTagId " + oldTagId + " playDuration ");
		
		if(soundFileCollections.size() <= tagIdC) return;
		
		soundFileName = ((SoundFileCollection)soundFileCollections.elementAt(tagIdC)).getNextSoundFile();
		
		if(soundFileName == null)
		{
			System.out.println("next file not yet ready\n");
		}
		else 
		{
			System.out.println("next filename: " + soundFileName);
			
			try
			{
				oStream.writeBytes("PUSH M " + soundFileName + "\n");
			}
			catch (UnknownHostException e)
			{
				System.err.println("Trying to connect to unknown host: " + e);
			}	
			catch (IOException e)
			{
				System.err.println("IOException: " + e);	
			}	
		}
				
		plotPanel.displayTag(tagIdC);
		
		oldTagId = tagIdC;
	}
	
	public void run()
	{
		while (true) 
        {
        	pollSensorData(); 	
        	plotPanel.repaint();
        	
            try 
            {
                Thread.sleep(captureInterval);
            } 
            catch (InterruptedException e)
            {}
        }
	}
	    
        public void keyPressed(KeyEvent e)
    {
         switch(e.getKeyCode())
         {
            //Kill app
            case 81: // q
            {
            	try
				{
					System.out.println("LOGOUT");
					oStream.writeBytes("LOGOUT\n");
					System.exit(0);
				}
				catch (UnknownHostException er)
				{
					System.err.println("Trying to connect to unknown host: " + er);
				}	
				catch (IOException er)
				{
					System.err.println("IOException: " + er);	
				}	
            	break;
            }

            default :
               if(e.getKeyCode()<250) // only interested in first 250 key codes, are there more?
               {
                  System.out.println(e.getKeyCode());
                 // keys[e.getKeyCode()]=true;	
               }
               break;
         }
      }
      
      public void keyReleased(KeyEvent e)
      {
      }
      
      public void keyTyped(KeyEvent e)
      {
      }
}

class PlotPanel extends JPanel
{
	double[] sensorValues;
	
	double minValue = 0.0;
	double maxValue = 255.0;
	
	Dimension dim = new Dimension(600, 600);

	public PlotPanel()
	{
		super();
		
		initSensorValues(100);
		
		setPreferredSize(dim);
	}
	
	public void initSensorValues(int valueCount)
	{
		sensorValues = new double[valueCount];
		
		for(int i=0; i<valueCount; i++) 
		{
			sensorValues[i] = 0.0;
		}
	}
	
	public void displayTag(double value)
	{
		for(int i=1; i<sensorValues.length; i++) 
		{
			sensorValues[i - 1] = sensorValues[i];
		}
		
		sensorValues[sensorValues.length - 1] = value;
	}
	
	public void updateSensorValue()
	{
		for(int i=1; i<sensorValues.length; i++) 
		{
			sensorValues[i - 1] = sensorValues[i];
		}
	}
	
	public void paint(Graphics g)
	{
		Graphics2D g2 = (Graphics2D)g;
		
		clear(g2);
		drawValues(g2);
	}
	
	public void clear(Graphics2D g2)
	{
		g2.setColor(Color.white);
		g2.fill(new Rectangle2D.Double(0f, 0f,
        (double)getWidth(), (double)getHeight()));
	}
	
	public void drawValues(Graphics2D g2)
	{	
		double xPos1 = 0.0;
		double xPos2 = 0.0;
		double yPos1 = 0.0;
		double yPos2 = 0.0;
		
		g2.setColor(Color.black);
		
		for(int i=0; i<sensorValues.length; i++)
		{
			xPos1 = xPos2;
			yPos1 = yPos2;
			
			xPos2 = ((double)getWidth() / (double)sensorValues.length) * i;
			yPos2 = (double)getHeight() - (((double)getHeight() / maxValue) * sensorValues[i]);
		
			if(i==0)
			{
				xPos1 = xPos2;
				yPos1 = yPos2;
			}
			
			g2.drawLine((int)xPos1, (int)yPos1, (int)xPos2, (int)yPos2);
		}
	}   
}

class SoundFile
{
	String m_fileName;
	double m_duration;

	public SoundFile(String p_fileName, double p_duration)
	{
		m_fileName = p_fileName;
		m_duration = p_duration;
	}
	
	public String getFileName() 
	{
		return m_fileName;
	}
	
	public double getDuration()
	{
		return m_duration;
	}
}

class SoundFileCollection
{
	Vector m_soundFiles;
	long m_startPlayTime;
	int m_soundFileIndex;
	boolean m_loopThoughSounds = true;
	double m_inactivityResetTime = 100; // fileIndex will be reset after m_inactivityResetTime sec

	public SoundFileCollection()
	{
		m_soundFiles = new Vector();
		m_soundFileIndex = -1;
	}
	
	public SoundFileCollection(double p_inactivityResetTime, boolean p_loopThoughSounds)
	{
		m_soundFiles = new Vector();
		m_soundFileIndex = -1;
		m_inactivityResetTime = p_inactivityResetTime;
		m_loopThoughSounds = p_loopThoughSounds;
	}
	
	public void addSoundFile(String p_fileName, double p_duration)
	{
		SoundFile soundFile = new SoundFile(p_fileName, p_duration);
		m_soundFiles.add(soundFile);
	}
	
	public String getNextSoundFile()
	{
		SoundFile soundFile;
		String soundFileName = null;
		double playDuration;
	
		if(m_soundFileIndex == -1) // first time we are getting a sound file
		{
			m_soundFileIndex = 0;
			soundFile = (SoundFile)m_soundFiles.elementAt(m_soundFileIndex);
			soundFileName = soundFile.getFileName();
			m_startPlayTime = System.currentTimeMillis();
		}
		else
		{
			soundFile = (SoundFile)m_soundFiles.elementAt(m_soundFileIndex);
			playDuration = ((double)(System.currentTimeMillis() - m_startPlayTime) / 1000.0);
		
			if(playDuration - soundFile.getDuration() > m_inactivityResetTime) // start with first audio file if there has been no interaction for a certain time
			{
				m_soundFileIndex = 0;
				soundFile = (SoundFile)m_soundFiles.elementAt(m_soundFileIndex);
				soundFileName = soundFile.getFileName();
				m_startPlayTime = System.currentTimeMillis();			
			}
			else if(playDuration > soundFile.getDuration()) // skip to next soundFile
			{
				m_soundFileIndex++;
				if(m_soundFileIndex >= m_soundFiles.size()) 
				{
					if(m_loopThoughSounds == true) m_soundFileIndex = 0;
					else m_soundFileIndex = m_soundFiles.size() - 1;
				}
			
				soundFile = (SoundFile)m_soundFiles.elementAt(m_soundFileIndex);
				soundFileName = soundFile.getFileName();		
				m_startPlayTime = System.currentTimeMillis();
			}		
		}
		
		return soundFileName;
	}
}