/*
 *  PageTemplate.java
 * 
 *  Copyright (c) 2008, Tom Dinchak
 * 
 *  This file is part of Pages.
 *
 *  Pages 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.
 *
 *  Pages 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 Pages; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

package org.monome.pages;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Date;

import javax.sound.midi.MidiMessage;
import javax.sound.midi.Receiver;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTextField;

import org.apache.commons.lang.StringEscapeUtils;

import com.illposed.osc.OSCListener;
import com.illposed.osc.OSCMessage;
import com.illposed.osc.OSCPortIn;
import com.illposed.osc.OSCPortOut;

/**
 * The Template page, a good starting point for creating your own pages.  
 * Usage information is available at:
 * 
 * http://code.google.com/p/monome-pages/wiki/ExternalApplicationPage
 * 
 * @author Tom Dinchak
 *
 */

public class RicemuttClassPage implements Page, ActionListener, OSCListener {

	/**
	 * The MonomeConfiguration this page belongs to
	 */
	private MonomeConfiguration monome;

	/**
	 * This page's index (page number) 
	 */
	private int index;

	/**
	 * This page's GUI / configuration panel 
	 */
	private JPanel panel;

	/**
	 * The selected MIDI output device
	 */
	@SuppressWarnings("unused")
	private Receiver recv;

	/**
	 * The name of the selected MIDI output device 
	 */
	private String midiDeviceName;
	
	/**
	 * The OSC prefix the external application uses
	 */
	private String prefix = "/pagesOSC";

	/**
	 * The hostname that the external application is bound to 
	 */
	private String hostname = "localhost";

	/**
	 * The OSC input port number to receive messages from the external application 
	 */
	private int inPort = 9000;

	/**
	 * The OSCPortIn object for communication with the external application
	 */
	private OSCPortIn oscIn;

	/**
	 * The OSC output port number to send messages to the external application
	 */
	private int outPort = 10000;

	/**
	 * The OSCPortOut object for communication with the external application 
	 */
	private OSCPortOut oscOut;
	
	/*************************************************************************** GUI VARIABLES
	 * The Disable LED Cache checkbox 
	 */
	private JCheckBox disableCache;

	/**
	 * The OSC Prefix label
	 */
	private JLabel prefixLabel;

	/**
	 * The OSC Prefix text field 
	 */
	private JTextField prefixTF;

	/**
	 * The OSC Output Port label
	 */
	private JLabel oscOutLabel;

	/**
	 * The OSC Output Port text field
	 */
	private JTextField oscOutTF;

	/**
	 * The OSC Input Port text field
	 */
	private JLabel oscInLabel;

	/**
	 * The OSC Input Port text field
	 */
	private JTextField oscInTF;

	/**
	 * The OSC hostname label
	 */
	private JLabel hostnameLabel;

	/**
	 * The OSC hostname text field
	 */
	private JTextField hostnameTF;

	/**
	 * The update preferences button
	 */
	private JButton updatePrefsButton;
	
	/** ************************************************************* END GUI VARIABLES */

	/**
	 * Array holding the states of the LEDs
	 */
	private int[][] ledStates = new int[8][8];
	
	private JustPitchSet thePitches;

	/**
	 * @param monome The MonomeConfiguration this page belongs to
	 * @param index The index of this page (the page number)
	 */
	public RicemuttClassPage(MonomeConfiguration monome, int index) {
		this.monome = monome;
		this.index = index;
		initOSC();
		
		thePitches.calcETLattice();
		
		
	}

	/**
	 * Stops OSC communication with the external application
	 */
	public void stopOSC() {
		if (this.oscIn != null) {
			if (this.oscIn.isListening()) {
				this.oscIn.stopListening();
				System.out.println("stopped listening to OSC input");
			}
			this.oscIn.close();
		}

		if (this.oscOut != null) {
			this.oscOut.close();
			System.out.println("oscOut closed");
		}
	}

	/**
	 * Initializes OSC communication with the external application
	 * @throws UnknownHostException 
	 */
	public void initOSC() {
		this.stopOSC();
		try {
			this.oscOut = new OSCPortOut(InetAddress.getByName(this.hostname), this.outPort);
			this.oscIn = new OSCPortIn(this.inPort);
			this.oscIn.addListener(this.prefix + "/sync", this);
			this.oscIn.startListening();
		} catch (SocketException e) {
			e.printStackTrace();
		} catch (UnknownHostException e) {
			e.printStackTrace();
		}
		System.out.println("Now listening to " + this.hostname + ": " + this.inPort);
		System.out.println("Now sending on " + this.hostname + ": " + this.outPort);
		System.out.println(hostname);
	}
	
	/* (non-Javadoc)
	 * @see org.monome.pages.Page#actionPerformed(java.awt.event.ActionEvent)
	 */
	public void actionPerformed(ActionEvent e) {
		if (e.getActionCommand().equals("Update Preferences")) {
			this.prefix = this.prefixTF.getText();
			this.hostname = this.hostnameTF.getText();
			this.inPort = Integer.parseInt(this.oscInTF.getText());
			this.outPort = Integer.parseInt(this.oscOutTF.getText());
			this.initOSC();
		}
		return;
	}



	/* (non-Javadoc) *****************************************BEGIN GUI
	 * @see org.monome.pages.Page#getPanel()
	 */
	public JPanel getPanel() {
		if (this.panel != null) {
			return this.panel;
		}

		JPanel panel = new JPanel();
		panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
		panel.setPreferredSize(new java.awt.Dimension(400, 200));

		JLabel label = new JLabel("Page " + (this.index + 1) + ": Ricemutt Class");
		prefixLabel = new JLabel();
		prefixLabel.setText("OSC Prefix");
		hostnameLabel = new JLabel();
		hostnameLabel.setText("OSC Hostname");
		oscInLabel = new JLabel();
		oscInLabel.setText("OSC In Port");
		oscOutLabel = new JLabel();
		oscOutLabel.setText("OSC Out Port");
		oscInTF = new JTextField();
		oscInTF.setText(String.valueOf(this.inPort));
		oscOutTF = new JTextField();
		panel.add(oscOutTF);
		panel.add(oscOutLabel);
		panel.add(oscInTF);
		panel.add(oscInLabel);
		oscInLabel.setBounds(12, 65, 85, 14);
		oscInTF.setBounds(97, 62, 100, 21);
		oscOutLabel.setBounds(12, 86, 85, 14);
		oscOutTF.setText(String.valueOf(this.outPort));
		oscOutTF.setBounds(97, 83, 100, 21);
		updatePrefsButton = new JButton();
		updatePrefsButton.setText("Update Preferences");
		updatePrefsButton.addActionListener(this);
		prefixTF = new JTextField();
		prefixTF.setText(this.prefix);
		hostnameTF = new JTextField();
		panel.add(hostnameTF);
		panel.add(hostnameLabel);
		panel.add(prefixTF);
		panel.add(prefixLabel);
		panel.add(label);
		panel.add(updatePrefsButton);
		panel.add(getDisableCache());
		updatePrefsButton.setBounds(12, 130, 169, 21);
		label.setBounds(0, 0, 129, 14);
		prefixLabel.setBounds(12, 23, 85, 14);
		prefixTF.setBounds(97, 20, 100, 21);
		hostnameLabel.setBounds(12, 44, 85, 14);
		hostnameTF.setText(this.hostname);
		hostnameTF.setBounds(97, 41, 100, 21);

		this.panel = panel;
		return panel;
	} // **************************************************************** END GUI

	//This is basic handlePress behavior that will make the monome LED's toggle on and off.
	public void handlePress(int x, int y, int value) {
		//Print incoming message
		//System.out.println("x: " + x + " y: "  + y + " value: " +value);
		
		if (value == 1){
			if (ledStates[x][y] == 0){
				ledStates[x][y] = 1;}
			else {
				ledStates[x][y] = 0;}
			
		this.monome.led(x, y, ledStates[x][y], this.index);
		System.out.println("ledStates[" + x + "][" + y + "] : " + ledStates[x][y]);
		
		Object args[] = new Object[1];
		args[0] = new Integer(ledStates[x][y]);
		OSCMessage msg = new OSCMessage(this.prefix + "/smutbunny", args);
		try {
			this.oscOut.send(msg);
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		}
		
		
	}

	/* accept message on incoming OSC port */	
	public void acceptMessage(Date arg0, OSCMessage msg) {
		
		System.out.println("OSC message received on " + this.inPort + " : " + msg);
		
	}
	
	/* (non-Javadoc)
	 * @see org.monome.pages.Page#redrawMonome()
	 */
	public void redrawMonome() {
		for (int i=0; i<this.monome.sizeX ; i++){
			for(int j = 0; j<this.monome.sizeY; j++){
				this.monome.led(i, j, ledStates[i][j], this.index);
			}
		}
	}

	/* (non-Javadoc)
	 * @see org.monome.pages.Page#send(javax.sound.midi.MidiMessage, long)
	 */
	public void send(MidiMessage message, long timeStamp) {
		// TODO add code to handle midi input from midi clock source (can be any type of input)
	}

	/* (non-Javadoc)
	 * @see org.monome.pages.Page#toXml()
	 */
	public String toXml() {
		String xml = "";
		xml += "    <page>\n";
		xml += "      <name>Template Page</name>\n";
		xml += "      <selectedmidioutport>" + StringEscapeUtils.escapeXml(this.midiDeviceName) + "</selectedmidioutport>\n";
		xml += "    </page>\n";
		return xml;
	}
	
	public JCheckBox getDisableCache() {
		if(disableCache == null) {
			disableCache = new JCheckBox();
			disableCache.setText("Disable LED Cache");
			disableCache.setBounds(97, 106, 180, 18);
		}
		return disableCache;
	}

	/**
	 * @param cacheDisabled "true" if the cache should be disabled
	 */
	public void setCacheDisabled(String cacheDisabled) {
		if (cacheDisabled.equals("true")) {
			this.getDisableCache().setSelected(true);
		} else {
			this.getDisableCache().setSelected(false);
		}
	}

	/* (non-Javadoc)
	 * @see org.monome.pages.Page#getCacheDisabled()
	 */
	public boolean getCacheDisabled() {
		if (this.getDisableCache().isSelected()) {
			return true;
		} else {
			return false;
		}
	}

	/* (non-Javadoc)
	 * @see org.monome.pages.Page#destroyPage()
	 */
	public void destroyPage() {
		return;
	}

	/* (non-Javadoc)
	 * @see org.monome.pages.Page#addMidiOutDevice(java.lang.String)
	 */
	public void addMidiOutDevice(String deviceName) {
		this.recv = this.monome.getMidiReceiver(deviceName);
		this.midiDeviceName = deviceName;
	}
	

	/* (non-Javadoc)
	 * @see org.monome.pages.Page#handleReset()
	 */
	public void handleReset() {
		// TODO add code to handle a reset position message from the midi clock source (stop button twice generally)
	}

	/* (non-Javadoc)
	 * @see org.monome.pages.Page#handleTick()
	 */
	public void handleTick() {
		// TODO add code to handle a 'tick' from the midi clock source (every 1/96th of a bar)
	}

	/* (non-Javadoc)
	 * @see org.monome.pages.Page#getName()
	 */
	public String getName() {
		return "Ricemutt Class";
	}
//end class
}

