#ifndef _APP_CONTROLLER_H
#define _APP_CONTROLLER_H

#include "ofMain.h"
#include "ofAddons.h"

//our other app objects
#include "guiSettingsManager.h"
#include "laserTracking.h"
#include "laserSending.h"
#include "imageProjection.h"
#include "baseGui.h"
#include "trackPlayer.h"


#define SETTING_SHOW_TIME 180   //this is when in fullscreen mode how long for setting to fade 
								//180 =  aprox 4 seconds

class appController : public baseGui{


	public:
	
		//---------------------------------------------------
		appController(){
			
		}
		
		//all our initalizers
		//----------------------------------------------------
		void setup(){
			
			//some vars
			singleScreenMode 	= false;
			toggleGui 			= false;
			keyTimer  			= 0;
			
			//general settings
			loadSettings();
			
			//load our fonts 
			//for messages
			useTrueTypeFont("fonts/courier.ttf", 10);
			
			//for Tracking Gui
			LT.useTrueTypeFont("fonts/courier.ttf", 10);
			
			//For settings 
			GUI.useTrueTypeFont("fonts/courier.ttf", 10);
			
			//For Brushes
			IP.setup(640,480, "brushes/");
			IP.loadSettings("data/settings/quadProj.xml");
			IP.IB.selectBrush( GUI.getSettingI("BRUSH_NO") );
			
			//For tracks
			TP.loadTracks("tunes/");
						
			camWidth 	= GUI.getSettingI("CAM_WIDTH");
			camHeight 	= GUI.getSettingI("CAM_HEIGHT");
			
			//if we have a camera lets see if the requested dimensions
			//is what we asked for and if not lets update our settings
			//with the real dimensions
			if( GUI.getSettingI("USE_CAMERA") ){
				LT.setupCamera(GUI.getSettingI("CAM_ID"), camWidth, camHeight);
				if(LT.W != 0 && LT.H != 0){
					 camWidth 	= LT.W;
					 camHeight 	= LT.H;
					 GUI.setSetting("CAM_WIDTH", camWidth);
					 GUI.setSetting("CAM_HEIGHT", camHeight);					 
				}
			}
			else LT.setupVideo("videos/lasertag_test.mov");
			
			LT.setupCV("data/settings/quad.xml");
			setupNetwork();
		}
		
		
		//lets read some xml!
		//----------------------------------------------------
		void loadSettings(){
		
			//we read our settings in 
			setCommonText("status: loading settings from xml");
			GUI.readFromFile("data/settings/settings.xml");

			//the order of these settings is the order they are displayed.
			//titles break up the settings into sub menu's
			
			
			GUI.addTitle("Drawing settings");

			GUI.addSetting("Brush mode",		"BRUSH_MODE", 		0, 0, 2, 1);
			GUI.addSetting("Brush width",		"BRUSH_WIDTH",  	4, 0, 128, 1);
			GUI.addSetting("Which brush image", "BRUSH_NO", 		0, 0, 200, 1);
			GUI.addSetting("Line resolution",	"LINE_RES", 		40, 1, 200, 1);
			
			GUI.addTitle("Drip settings");
			GUI.addSetting("Drips enabled",		"DRIPS", 			0, 0, 5, 1);
			GUI.addSetting("Drips freq", 		"DRIPS_FREQ", 		11, 1, 120, 1);
			GUI.addSetting("Drips speed", 		"DRIPS_SPEED", 		0.3f, 0.0f, 12.0f, 0.02f);
			GUI.addSetting("Drips direction",	"DRIP_DIRECTION", 	0, 0,3, 1);
			
									
			GUI.addTitle("Tracking settings");
			GUI.addSetting("Hue Point", 		"HUE_POINT", 		0.35f, 0.0f, 1.0f, 0.02f);
			GUI.addSetting("Hue Thresh Width", 	"HUE_WIDTH", 		0.25f, 0.0f, 1.0f, 0.02f);
			GUI.addSetting("Sat Threshold", 	"SAT_POINT", 		0.2f,  0.0f, 1.0f, 0.02f);
			GUI.addSetting("Value Threshold", 	"VAL_POINT", 		0.15f, 0.0f, 1.0f, 0.02f);
			
			GUI.addSetting("Use blobs",			"USE_BLOBS",		1, 0, 1, 1);
			GUI.addSetting("Min blob size",		"MIN_BLOB_SIZE",	8, 1, 100, 1);
			GUI.addSetting("Activity thresh", 	"ACTIVITY", 		10, 0, 100, 1);
			GUI.addSetting("Jump dist",			"JUMP_DIST",    	0.0f, 0.0f, 1.0f, 0.01f);
			
			GUI.addTitle("Clear zone settings");
			GUI.addSetting("Use clear zone",	"CLEAR_ZONE", 		0, 0, 1, 1);
			GUI.addSetting("Clear x pos"	,	"CLEAR_X", 			0, 0, 320, 2);
			GUI.addSetting("Clear y pos"	,	"CLEAR_Y", 			1, 0, 240, 2);
			GUI.addSetting("Clear width"	,	"CLEAR_W", 			1, 0, 320, 2);
			GUI.addSetting("Clear height"	,	"CLEAR_H", 			1, 0, 240, 2);

			GUI.addTitle("Network settings");
			GUI.addSetting("Enable Network",	"NETWORK_SEND",		0, 0, 1, 1);
			GUI.addSetting("Send data",			"SEND_DATA",		0, 0, 1, 1);
			GUI.addSetting("UDP(0) / TCP(1)",	"UDP_OR_TCP",		0, 0, 1, 1);
			GUI.addSetting("ip: val.xxx.xxx.xxx",	"IP_PT1",		127, 0, 255, 1);
			GUI.addSetting("ip: xxx.val.xxx.xxx",	"IP_PT2",		0, 0, 255, 1);
			GUI.addSetting("ip: xxx.xxx.val.xxx",	"IP_PT3",		0, 0, 255, 1);
			GUI.addSetting("ip: xxx.xxx.xxx.val",	"IP_PT4",		1, 0, 255, 1);
			GUI.addSetting("port:",					"PORT",			5544, 0, 65000, 1);
			
			GUI.addTitle("Music player settings");
			GUI.addSetting("Play music",	"MUSIC", 	0, 0, 1, 1);	
			GUI.addSetting("Which track",	"TRACK", 	0, 0, 999, 1);	
			GUI.addSetting("Volume",		"VOL", 		80, 0, 100, 1);	
			GUI.addSetting("Scratch mode",	"SCRATCH", 	0, 0, 1, 1);	
			
			GUI.addTitle("Camera settings (restart app!)");
			GUI.addSetting("Use camera", 				"USE_CAMERA", 			0, 0, 1, 1);	
			GUI.addSetting("Camera #", 					"CAM_ID", 				0, 0, 900, 1);	
			GUI.addSetting("Camera Width",				"CAM_WIDTH", 			320, 0, 900, 2);	
			GUI.addSetting("Camera Height", 			"CAM_HEIGHT", 			240, 0, 900, 2);	
									
			setCommonText("status: loaded settings from xml");
				
		}

		
		//----------------------------------------------------
		void saveSettings(){
		
			setCommonText("status: saving settings to xml");
		
			GUI.saveToFile("settings/settings.xml");
			LT.QUAD.saveToFile("settings/quad.xml");
			IP.QUAD.saveToFile("settings/quadProj.xml");
			
			setCommonText("status: settings saved to xml");
			
		}
		
		//----------------------------------------------------
		void reloadSettings(){
			
			setCommonText("status: reloading settings from xml");
		
			GUI.reloadSettings();
			LT.QUAD.loadSettings();
			IP.loadSettings("data/settings/quadProj.xml");
			
			setCommonText("status: settings reloaded from xml");
			
		}		
		
		//----------------------------------------------------
		void setupNetwork(){
		
			if( GUI.getSettingI("NETWORK_SEND") ){
				string ipStr 	= ofToString(GUI.getSettingI("IP_PT1")) + ".";
				ipStr +=  ofToString(GUI.getSettingI("IP_PT2")) + ".";
				ipStr +=  ofToString(GUI.getSettingI("IP_PT3")) + ".";
				ipStr +=  ofToString(GUI.getSettingI("IP_PT4"));
								
				int port 		= GUI.getSettingI("PORT"); 
			
				if( GUI.getSettingI("UDP_OR_TCP") ){
					string msg = "status: tcp server listening on port: "+ofToString(port);
					if( LS.setupTCPServer(port) ) 	setCommonText(msg);
				}else{
					string msg = "status: udp sending to: "+ipStr;
					msg +="  port: "+ofToString(port);
					if( LS.setupUDP(ipStr, port) ) setCommonText(msg);
				}
			}else{
				LS.close();
  			 	setCommonText("status: all network i/o closed");
			}
		}

		//----------------------------------------------------
		void sendData(){
			if( LS.isSetup() ){
				LS.sendData(LT.getLaserPointString());
				if(LS.isTCPSetup()){
					if( LS.checkNewTCPClient() ){
						string msg = "status: tcp client connected on: "+LS.getLatestTCPClientIP();
						setCommonText(msg);
					}
				}
			}
		}
		

		//----------------------------------------------------
		void mainLoop(){
		
			///////////////////////////////////////////////////////////
			// Part 1 - tracking the laser
			///////////////////////////////////////////////////////////
		
			//these two settings are used to determin
			//when a new stroke is created
			int activity 	= GUI.getSettingI("ACTIVITY");
			float jump	 	= GUI.getSettingF("JUMP_DIST");
			
			//the minSize of our blobs
			int blobSize 	= GUI.getSettingI("MIN_BLOB_SIZE");
					
			//our color tracking settings
			float hue 		= GUI.getSettingF("HUE_POINT");
			float hueThresh = GUI.getSettingF("HUE_WIDTH"); 
			float sat		= GUI.getSettingF("SAT_POINT"); 
			float value		= GUI.getSettingF("VAL_POINT");
			
			//lets see if we need to update the clearZone
			if( GUI.hasChanged("CLEAR_ZONE") || GUI.hasChanged("CLEAR_X") || GUI.hasChanged("CLEAR_Y")
				|| GUI.hasChanged("CLEAR_W") || GUI.hasChanged("CLEAR_H") ){
				
				
				LT.setUseClearZone(GUI.getSettingI("CLEAR_ZONE"));
				
				LT.setClearZone(GUI.getSettingI("CLEAR_X"), GUI.getSettingI("CLEAR_Y"), 
								GUI.getSettingI("CLEAR_W"), GUI.getSettingI("CLEAR_H"));
								
			}
			
			//lets process the video data to track that laser
			LT.processFrame(hue, hueThresh, sat,  value, blobSize, activity, jump); 
			
			if( LT.isClearZoneHit() ){
				IP.clear();
				setCommonText("status: clear zone hit - clearning image\n");
			}
			
			//only update our color tracking indicator if values have changed				
			if( GUI.hasChanged("HUE_POINT") || GUI.hasChanged("HUE_WIDTH") 
			    || GUI.hasChanged("SAT_POINT") || GUI.hasChanged("VAL_POINT") ){
			
				LT.calcColorRange(hue, hueThresh, sat, value);	
			}
			
			///////////////////////////////////////////////////////////
			// Part 2 - sending the tracking data udp or tcp
			///////////////////////////////////////////////////////////
			
			//look to see if network send has changed
			//if so we might need to open or close
			//a network connection 
			//also look to see if mode between udp and tcp
			//has changed as this will require resetup
			if( GUI.hasChanged("NETWORK_SEND") || ( GUI.hasChanged("UDP_OR_TCP") && GUI.getSettingI("NETWORK_SEND") )  ){
				setupNetwork();
			}
			
			//if sending data is enabled
			//then lets send our data!	
			if( GUI.getSettingI("SEND_DATA") ){
				sendData();
			} 
			
			
			///////////////////////////////////////////////////////////
			// Part 3 - our music player :)
			///////////////////////////////////////////////////////////
			
			//update our mp3 player -- should this be moved to
			//another app?
			bool playMusic = GUI.getSettingI("MUSIC");
			int whichTrack = GUI.getSettingI("TRACK");
			int volume	   = GUI.getSettingI("VOL");
			bool scratch   = GUI.getSettingI("SCRATCH");

			if(playMusic){
				TP.unPause();
				if( whichTrack != TP.getCurrentTrackNo()){
					
					if(whichTrack >= TP.getNumTracks()){
						whichTrack = 0;
						GUI.setSetting("TRACK", whichTrack);
					}
					
					TP.playTrack(whichTrack);
					setCommonText("Playing: "+ofToString(TP.getCurrentTrackNo())+" "+TP.getCurrentTrackName());
				}
				
				if( TP.getFinished() ){
					whichTrack = TP.nextTrack();
					setCommonText("Playing: "+ofToString(TP.getCurrentTrackNo())+" "+TP.getCurrentTrackName());
					GUI.setSetting("TRACK", whichTrack);
				}
				TP.setVolume(volume);
				if(scratch) TP.updatePitch(0.93);
			}else{
				TP.pause();	
			}
			
			
			///////////////////////////////////////////////////////////
			// Part 4 - time to paint!
			///////////////////////////////////////////////////////////
			
			//this is where we do our painting!
			//do we want drips
			int drips 		= GUI.getSettingI("DRIPS");
			int brushMode 	= GUI.getSettingI("BRUSH_MODE");
			
			//graff letters need to be updated - awwww :)
			if(brushMode == 1){
				IP.idleGraffLetters();
			}
			
			if(brushMode == 0 && drips){
				IP.idleDrips();
			}
			
			
			//do we have a laser - no point drawing otherwise!
			if(LT.newData())
			{
				int brushWidth 	= GUI.getSettingI("BRUSH_WIDTH");
				int res			= GUI.getSettingI("LINE_RES");
				
				int freq 		= GUI.getSettingI("DRIPS_FREQ");
				int direction	= GUI.getSettingI("DRIP_DIRECTION");
				float speed		= GUI.getSettingF("DRIPS_SPEED");
				
	
				if(brushMode == 2){
					IP.paintVectorBrush(LT.laserX, LT.laserY, brushWidth, LT.isStrokeNew());
				}
				if(brushMode == 1){
					IP.paintGraffLetters(LT.laserX, LT.laserY, LT.oldX, LT.oldY,  brushWidth, LT.isStrokeNew());
				}
				else if(brushMode == 0){
				
					if( GUI.hasChanged("BRUSH_NO") ){
						int whichBrush = IP.IB.selectBrush( GUI.getSettingI("BRUSH_NO") );
						GUI.setSetting("BRUSH_NO", whichBrush);
					} 
					
					IP.dripSettings( drips, direction, freq, speed);
					IP.paintImageBrush(LT.laserX, LT.laserY, brushWidth, res, LT.isStrokeNew());
				
				}
				
				//this is where we scratch :)
				if(playMusic && scratch )TP.setPitch(1.0 + LT.distY*4 + LT.distX*4);
				
				LT.clearNewStroke();
			}
									
			
			//this is for the singlescreen mode
			//it will show the current setting for a few seconds
			if(keyTimer > 0)keyTimer--;
		}
		
		//----------------------------------------------------
		void selectPoint(float x, float y){
			
			if(singleScreenMode){
				IP.selectQuad(x, y, 0, 0, 1024, 768, 60);
			}
			else{
				LT.QUAD.selectPoint(x, y, 0, 0, 320, 240, 30); 
				
				//only try and select the mini quad 
				//if the large quad is not selected
				if( IP.selectQuad(x, y, 1024, 0, 1024, 768, 60) != 1 ){				
					IP.selectMiniQuad(x, y, 30);
				}
			} 
		
		}
		
		//----------------------------------------------------
		void dragPoint(float x, float y){
		
			if(singleScreenMode){
				IP.updateQuad(x, y, 0, 0, 1024, 768);
			}
			else{
				LT.QUAD.updatePoint(x, y, 0, 0, 320, 240);
				
				//only try and update the mini quad 
				//if the large quad is not selected
				if( IP.updateQuad(x, y, 1024, 0, 1024, 768) != 1 ){
					IP.updateMiniQuad(x, y);
				}
			} 	
			
		}
		
		//----------------------------------------------------
		void releasePoint(){
			LT.QUAD.releaseAllPoints();
			IP.releaseAllQuads(); 
		}
			
		//----------------------------------------------------
		void keyPress(int key){
			
			if(key == '1'){
				LT.setCallibrationPoint(1);
			}
			else if (key == '2'){
				LT.setCallibrationPoint(2);
			}
			else if (key == '3'){
				LT.setCallibrationPoint(3);
			}
			else if (key == '4'){
				LT.setCallibrationPoint(4);
			}
			else if(key == '-'){
				singleScreenMode = false;
			}
			else if(key == '='){
				singleScreenMode = true;	
			}
			else if(key == 's'){
				saveSettings();
			}
			else if(key == 'r'){
				reloadSettings();
			}
			else if(key == OF_KEY_UP){
				GUI.selectPrev();
				keyTimer = SETTING_SHOW_TIME;
			}
			else if(key == OF_KEY_DOWN){
				GUI.selectNext();
				keyTimer = SETTING_SHOW_TIME;
			}
			else if(key == OF_KEY_RIGHT){
				GUI.increase();
				keyTimer = SETTING_SHOW_TIME;
			}
			else if(key == OF_KEY_LEFT){
				GUI.decrease();
				keyTimer = SETTING_SHOW_TIME;
			}else if(key == 'c'){
				LT.openCameraSettings();
			}else if(key == 'd'){
				setCommonText("status: clearing projection");
				IP.clear();
			}else if(key == ' '){
				toggleGui = true;
			}
			
		}
		
		//----------------------------------------------------
		void keyRelease(int key){
			if(toggleGui && key == ' '){
				toggleGui = false;
			}
		}	
		
		//----------------------------------------------------
		void draw(){
			if( singleScreenMode ){
				IP.drawProjectionTex(0, 0, 1024, 768);
				if(toggleGui)IP.drawProjectionToolHandles(0, 0, 1024, 768, false, true);
				
				ofSetColor(0x555555);
				if(toggleGui)ofDrawBitmapString("fps: "+ofToString(ofGetFrameRate()), 10, 740);
				if(keyTimer > 0){
					ofDrawBitmapString("press '-' key to return to main view", 10, 704);
					GUI.drawSelected(10, 720, 200);
				}
			}
			else{
				drawGUI();
				
				ofSetColor(150,150,150);
				ofDrawBitmapString("fps: "+ofToString(ofGetFrameRate()), 10, 740);
				IP.drawProjectionTex(1024, 0, 1024, 768);
				if(toggleGui)IP.drawProjectionToolHandles(1024, 0, 1024, 768, false, true);
				//LTdrawCallibrationMarkers(1024, 0, 1024, 768);
			}

		}
		
		
		//----------------------------------------------------
		void drawGUI(){
			LT.draw(0,0);
			
			ofSetColor(255, 255, 255);
			
				
			drawText("Colors tracked", 320, 280);
			LT.drawColorRange(320, 295, 120, 40);
		
			ofSetColor(255, 255, 255);
			drawText("Current brush", 470, 280);
			IP.drawBrush(470, 295, 32, 32, true);
			
			
			drawText("LASER TAG 2.0 CONTROLS\n\
+  : single screen mode\n\
-  : dual screen mode\n\
sp : show edge of projection\n\
f  : switches to fullscreen\n\
w  : switches to windowed mode\n\
c  : opens camera settings\n\
d  : clears the screen\n\
s  : saves your current settings\n\
r  : reloads settings from last save\n\n\
Arrow keys for settings list", 320, 370);

						
			GUI.drawCollapsedStyle(10, 260, 200, 12.0);
			
			ofSetColor(255,255,255,90);
			IP.drawPreviewTex(320, 0, 320, 240);
			IP.drawMiniProjectionTool(640, 260, true);
				
			drawStatusMessage();
		}
		
		//----------------------------------------------------		
		void drawStatusMessage(){
			
			if(textFade > 0){
				
				ofSetColor(textFade*2, textFade*2, textFade*2);
				ofRect(0, 752, 500, 16);
				
				ofSetColor(textFade, 0, 0);
				drawText(commonText, 5, 762);
				textFade--;
			}
		}
		
		
		laserTracking LT;
		laserSending  LS;
		guiSettingsManager GUI;
		imageProjection IP;
		trackPlayer TP;
		
		bool toggleGui, singleScreenMode;
		int camWidth, camHeight, keyTimer;
		
		float somePoo;
	
};

#endif