#include "slideApp.h"

using namespace std;


////////////////////////////////////////
// FUNCTIONS
////////////////////////////////////////
void parseHexColour(string colourStr, Colour colour)	{
	
}


bool parseBoolean(string valueStr)	{
	return valueStr == "true" || valueStr == "0";
}

int parseImageDisplay(string valueStr)	{
	if(valueStr == "scale")	{
		return IMG_SCALE;
	} else if(valueStr == "stretch")	{
		return IMG_STRETCH;
	} else {
		return IMG_NORMAL;
	} 
}

////////////////////////////////////////
// CLASSES
////////////////////////////////////////
void SlideApp::exit()
{
	sendPlaystate(STOPPED);
	delete []statusmessage;
	delete []slidelookup;
	delete []slideimages;
	delete []statements;
	if(totalSenders>0)	{
		delete []senders;
	};
}


void SlideApp::setup(){	
	
	ofSetWindowTitle("...");
	ofSetFrameRate(12);
	
	// random
	ofSeedRandom();
	
	// graphics
	ofBackground(0,0,0);
	
	//time
	nowtime = 0;
	timeout = 0;
	doingtime = false;
	
	// display
	showdata = false;
	statusmessage = new char[255];
	messageFont.loadFont("fonts/NotCourier-sans.ttf", 9);
	msgX = 5;
	msgY = 471;
	
	
	// prepare
	currentslideimage = 0;
	
	colourpalette = new Colour[MAX_COLOURS];
	screencolour.r = 0;
	screencolour.g = 0;
	screencolour.b = 0;
	receiveosc = false;
	
	// run
	enterScene(STARTUP);
}


void SlideApp::prepareToRun()
{
	ofBackground(screencolour.r,screencolour.g,screencolour.b);
	ofHideCursor();
	ofSetFullscreen(fullscreen);
	if(fullscreen)	{
		width = ofGetScreenWidth();
		height = ofGetScreenHeight();
		ofSetWindowPosition(0,0);
	} else {
		width = ofGetWidth();
		height = ofGetHeight();
	}
	msgX = 5;
	msgY = height-8;
	// slides
	currentslideimage = -1;
	currentStatement= -1;
	// statements
	displayText.setup(width, height, maxdisplaylines);
	displayText.setBackgroundColour(bgcolour);
	displayText.setForeColour(forecolour);
	// test
	letterframe = 0;
}


void SlideApp::loadSettings(char * filename)
{
	ofxXmlSettings XML;
	int numStatementTags = 0;
	totalStatements = 0;
	int numSlideImageTags = 0;
	totalSlideImages = 0;
	int numTransitionTags = 0;
	int maxid =0;
	
	if( XML.loadFile(filename) ){
		//colours
		if(XML.getNumTags("colours") > 0)	{
			XML.pushTag("colours", 0);
			totalcolours = XML.getNumTags("colour");
			if(totalcolours > 0)	{
				for(int i = 0; i < totalcolours; i++)	{
					colourpalette[i].name = XML.getValue("colour:name", "", i);
					colourpalette[i].r = XML.getValue("colour:r", 20, i);
					colourpalette[i].g = XML.getValue("colour:g", 255, i);
					colourpalette[i].b = XML.getValue("colour:b", 0, i);
				}
			}
			XML.popTag(); //colours
		}
		
		// project
		if(XML.getNumTags("project") > 0)	{
			// colours
			setColourTo(&screencolour, XML.getValue("project:screencolour", "screen"));
			setColourTo(&bgcolour, XML.getValue("project:bgcolour", "screen"));
			setColourTo(&forecolour, XML.getValue("project:forecolour", "screen"));
			// display
			fullscreen = parseBoolean(XML.getValue("project:fullscreen", "false"));
			printf("fullscreen %d\n", fullscreen);
			autorun = parseBoolean(XML.getValue("project:autorun", "true"));
			printf("autorun %d\n", autorun);
			pausetime = XML.getValue("project:pause", 0);
			printf("pausetime %d\n", pausetime);
			fromtop = parseBoolean(XML.getValue("project:fromtop", "true"));
			printf("fromtop %d\n", fromtop);
			maxdisplaylines = XML.getValue("project:displaylines", DEFAULT_DISPLAYLINES);
			printf("maxdisplaylines %d\n", maxdisplaylines);
			imagedisplay = parseImageDisplay(XML.getValue("project:imagedisplay", "scale"));
			printf("imagedisplay %d\n", imagedisplay);
			framesperletter = XML.getValue("project:framesperletter", DEFAULT_FRAMESPERLETTER);
			printf("framesperletter %d\n", framesperletter);
			imagetimeout = XML.getValue("project:imagetimeout", DEFAULT_IMAGETIMEOUT);
			printf("imagetimeout %d\n", imagetimeout);
			texttimeout = XML.getValue("project:texttimeout", DEFAULT_TEXTTIMEOUT);
			printf("texttimeout %d\n", texttimeout);
		}
		
		// osc
		if(XML.getNumTags("osc") > 0)	{
			XML.pushTag("osc", 0);
			oscname = XML.getValue("oscname", "slideapp", 0);
			printf("oscname: <%s>\n", oscname.c_str());
			oscport = XML.getValue("oscport", 0, 0);
			printf("oscport: <%d>\n", oscport);
			if(oscport > 0)	{
				receiver.setup(oscport);
				receiveosc = true;
			} else {
				receiveosc = false;
			}
			XML.pushTag("recipients", 0);
			int numSendersTags = XML.getNumTags("recipient");
			senders = new ofxOscSender[numSendersTags];
			totalSenders = 0;
			for(int i = 0; i < numSendersTags; i++)	{
				string host = XML.getValue("recipient:host", "", i);
				int port = XML.getValue("recipient:port", 0, i);
				if(host != "" && port > 0)	{
					senders[totalSenders].setup(host.c_str(), port);
					totalSenders++;
				}
			}
			XML.popTag(); //senders
			XML.popTag(); //osc
		}
		
		// statements
		if(XML.getNumTags("statements") > 0)	{
			XML.pushTag("statements", 0);
			numStatementTags = XML.getNumTags("statement");
			if(numStatementTags > 0)	{
				statements = new Statement[numStatementTags];
				statementprobabilities.setup(numStatementTags);
				for(int i = 0; i < numStatementTags; i++)	{
					XML.pushTag("statement", i);
					XML.pushTag("text", 0);
					string str = "";
					int numParagraphs = XML.getNumTags("p");
					if(numParagraphs > 0)	{
						for(int p = 0; p < numParagraphs-1; p++)	{
							str += XML.getValue("p", "", p);
							str += "\n";
						}
						str += XML.getValue("p", "", numParagraphs-1);
						XML.popTag(); //text
					} else {
						XML.popTag(); //text
						str = XML.getValue("text", "", i);
					}
					XML.popTag(); //statement
					int textID = XML.getValue("statement:id", -1, i);
					if(str != "" && textID > -1)	{
						statements[i].text = str;
						statements[i].id = textID;
						statements[i].textwait = XML.getValue("statement:textwait", texttimeout, i);
						statements[i].pausewait = XML.getValue("statement:pausewait", texttimeout, i);
						totalStatements++;
						if(textID > maxid)	{
							maxid = textID;
						}
					}
					//XML.popTag(); //statement
				}
			}
			XML.popTag(); //statements
		}
		
		// statement lookup
		maxid++;
		statementlookup = new int[maxid];
		for(int i = 0; i < maxid; i++)	{
			statementlookup[i] = -1;
		}
		for(int i = 0; i < totalStatements; i++)	{
			statementlookup[statements[i].id] = i;
		}
		
		// statement transitions
		if(XML.getNumTags("statements") > 0)	{
			XML.pushTag("statements", 0);
			numStatementTags = XML.getNumTags("statement");
			if(numStatementTags > 0)	{
				for(int i = 0; i < numStatementTags; i++)	{
					int textID = XML.getValue("statement:id", -1, i);
					if(textID > -1)	{
						statementprobabilities.setTransition(statementlookup[textID], statementlookup[textID], 0);
						if(XML.getNumTags("statement:transitions") > 0)	{
							XML.pushTag("statement", i);
							XML.pushTag("transitions", 0);
							numTransitionTags = XML.getNumTags("transition");
							if(numTransitionTags > 0)	{
								for(int t = 0; t < numTransitionTags; t++)	{
									int tranID = XML.getValue("transition:id", -1, t);
									int tranWeight = XML.getValue("transition:weight", 20, t);
									if(tranID > -1 && tranID != textID)	{
										statementprobabilities.setTransition(statementlookup[textID], statementlookup[tranID], tranWeight);
									}
								}
							}
							XML.popTag(); //transitions
							XML.popTag(); //statement
						}
					}
				}
			}
			XML.popTag(); //statements
		}
		
		// slide images
		maxid =0;
		if(XML.getNumTags("slides") > 0)	{
			XML.pushTag("slides", 0);
			numSlideImageTags = XML.getNumTags("slide");
			if(numSlideImageTags > 0)	{
				slideimages = new SlideImage[numSlideImageTags];
				slideprobabilities.setup(numSlideImageTags);
				for(int i = 0; i < numSlideImageTags; i++)	{
					string str = XML.getValue("slide:image", "", i);
					printf("loading: %s\n", str.c_str());
					int imageID = XML.getValue("slide:id", -1, i);
					if(str != "" && imageID > -1)	{
						slideimages[i].filepath = str.c_str();
						slideimages[i].id = imageID;
						slideimages[i].timeout = XML.getValue("slide:timeout", imagetimeout, i);
						totalSlideImages++;
						preloadSlideImage(i);
						if(imageID > maxid)	{
							maxid = imageID;
						}
					}
				}
			}
			XML.popTag(); //slides
		}
		
		// slide lookup
		maxid++;
		slidelookup = new int[maxid];
		for(int i = 0; i < maxid; i++)	{
			slidelookup[i] = -1;
		}
		for(int i = 0; i < totalSlideImages; i++)	{
			slidelookup[slideimages[i].id] = i;
		}
		
		// slide transitions
		if(XML.getNumTags("slides") > 0)	{
			XML.pushTag("slides", 0);
			numSlideImageTags = XML.getNumTags("slide");
			if(numSlideImageTags > 0)	{
				for(int i = 0; i < numSlideImageTags; i++)	{
					string str = XML.getValue("slide:image", "", i);
					int imageID = XML.getValue("slide:id", -1, i);
					if(str != "" && imageID > -1)	{
						slideprobabilities.setTransition(slidelookup[imageID], slidelookup[imageID], 0);
						if(XML.getNumTags("slide:transitions") > 0)	{
							XML.pushTag("slide", i);
							XML.pushTag("transitions", 0);
							numTransitionTags = XML.getNumTags("transition");
							if(numTransitionTags > 0)	{
								for(int t = 0; t < numTransitionTags; t++)	{
									int tranID = XML.getValue("transition:id", -1, t);
									int tranWeight = XML.getValue("transition:weight", 20, t);
									if(tranID > -1 && tranID != imageID)	{
										slideprobabilities.setTransition(slidelookup[imageID], slidelookup[tranID], tranWeight);
									}
								}
							}
							XML.popTag(); //transitions
							XML.popTag(); //slide
						}
					}
				}
			}
			XML.popTag(); //slides
		}
	} 
}

// colours
int SlideApp::getColourIndex(string name)
{
	int idx =-1;
	
	for(int i = 0; i < totalcolours; i++)	{
		if(colourpalette[i].name == name)	{
			idx = i;
		}
	}
	return idx;
}

void SlideApp::setColourTo(Colour *colour, string name)
{
	int idx = getColourIndex(name);
	if(idx > -1)	{
		colour->r = colourpalette[idx].r;
		colour->g = colourpalette[idx].g;
		colour->b = colourpalette[idx].b;
	}
}


// slides
void SlideApp::preloadSlideImage(int slideIndex)
{
	if(slideIndex < totalSlideImages)	{
		slideimages[slideIndex].image.loadImage(slideimages[slideIndex].filepath);
		slideimages[slideIndex].image.setImageType(OF_IMAGE_COLOR);
		slideimages[slideIndex].width = slideimages[slideIndex].image.width;
		slideimages[slideIndex].height = slideimages[slideIndex].image.height;
	}
}

void SlideApp::displaySlideImage(int slideIndex)
{
	if(slideIndex < totalSlideImages)	{
		
	}
}

void SlideApp::nextSlideImage()
{
	int imgID = slideprobabilities.nextEvent(slideimages[currentslideimage].id);
	setSlideImage(imgID);
}
	
void SlideApp::setSlideImage(int imageID)
{
	currentslideimage = slidelookup[imageID];
	sendNewSlideImage(imageID);
	
	if(autorun)	{
		nextimagetimeout = slideimages[currentslideimage].timeout;
		setTimeout(nextimagetimeout);
	}
	
	switch(imagedisplay)	{
		case IMG_SCALE:
			if(slideimages[currentslideimage].image.width > slideimages[currentslideimage].image.height)	{
				float ratio = width/slideimages[currentslideimage].image.width;
				slideWidth = slideimages[currentslideimage].image.width*ratio;
				slideHeight = slideimages[currentslideimage].image.height*ratio;
				slideX = 0;
				slideY = (height - slideHeight)/2.0;
			} else {
				float ratio = height/slideimages[currentslideimage].image.height;
				slideWidth = slideimages[currentslideimage].image.width*ratio;
				slideHeight = slideimages[currentslideimage].image.height*ratio;
				slideX = (width - slideWidth)/2.0;
				slideY = 0;
			}
			break;
		case IMG_STRETCH:
			slideX = 0;
			slideY = 0;
			slideWidth = width;
			slideHeight = height;
			break;
		case IMG_NORMAL:
			slideX = (width - slideimages[currentslideimage].image.width)/2.0;
			slideY = (height - slideimages[currentslideimage].image.height)/2.0;
			slideWidth = slideimages[currentslideimage].image.width;
			slideHeight = slideimages[currentslideimage].image.height;
			break;
	}
}


// statements
void SlideApp::nextStatement()
{
	int textID = statementprobabilities.nextEvent(statements[currentStatement].id);
	setStatement(textID);
}

void SlideApp::setStatement(int textID)
{
	currentStatement = statementlookup[textID];
	sendNewStatement(textID);
	
	displayText.setText(statements[currentStatement].text.c_str());
	displayText.setTextwait(statements[currentStatement].textwait);
	displayText.setPausewait(statements[currentStatement].pausewait);
}



void SlideApp::update(){
	switch(scene)	{
		case STARTUP:
			updateTimeout();
			break;
		case LOADING:
			updateTimeout();
			break;
		case PREPAUSE:
			updateIncomingOSC();
			updateTimeout();
			break;
		case PLAY:
			updateIncomingOSC();
			if(playing)	{
				updateTimeout();
				if(letterframe == framesperletter)	{
					letterframe = -1;
					displayText.nextChar();
				}
				letterframe++;
				displayText.updateTimeout();
				if(displayText.hasExpired() && autorun)	{
					nextStatement();
				}
			}
			break;
		case END:
			updateIncomingOSC();
			updateTimeout();
			break;
	}
}


void SlideApp::draw(){
	switch(scene)	{
		case STARTUP:
			ofSetColor(0xFFFFFF);
			
			break;
		case LOADING:
			ofSetColor(0xFFFFFF);
			
			//ofSetColor(255,255,255);
			//messageFont.drawString(statusmessage, msgX,msgY);
			break;
		case PREPAUSE:
			break;
		case PLAY:
			ofSetColor(0xFFFFFF);
			if(currentslideimage > -1)	{
				slideimages[currentslideimage].image.draw(slideX,slideY,slideWidth,slideHeight);
			}
			displayText.draw();
			break;
		case END:
			ofSetColor(0xFFFFFF);
			//ofSetColor(255,255,255);
			//messageFont.drawString(statusmessage, msgX,msgY);
			break;
	}
	
}

void SlideApp::statusMessage(char *msg)
{
	sprintf(statusmessage, "%s", msg);
	messageFont.drawString(statusmessage, msgX,msgY);
}


void SlideApp::enterScene(int newscene)
{
	int sndGroup;
	int midheight = height/2;
	int widththird = width/3;
	
	scene = newscene;
	switch(scene)	{
		case STARTUP:
			ofBackground(0,0,0);
			setTimeout(500);
			break;
		case LOADING:
			endScene();
			sendEnterScene(scene);
			ofBackground(0,0,0);
			//statusMessage("loading project ...");
			loadSettings("settings.xml");
			setTimeout(500);
			break;
		case PREPAUSE:
			endScene();
			sendEnterScene(scene);
			enterPlaystate(PAUSED);
			printf("PREPAUSE\n");
			prepareToRun();
			setTimeout(pausetime);
			break;
		case PLAY:
			endScene();
			sendEnterScene(scene);
			enterPlaystate(PLAYING);
			printf("PLAY\n");
			if(autorun)	{
				nextSlideImage();
				nextStatement();
			}
			break;
		case END:
			endScene();
			sendEnterScene(scene);
			enterPlaystate(STOPPED);
			ofBackground(0,0,0);
			//statusMessage("shutting down ...");
			//setTimeout(3000);
			break;
		default:
			ofBackground(0,255,0);
			break;
	}
}


void SlideApp::enterPlaystate(int newstate)
{
	switch(newstate)	{
		case PLAYING:
			playing = true;
			if(scene != PLAY)	{
				enterScene(PLAY);
			}
			sendPlaystate(newstate);
			break;
		case PAUSED:
			playing = false;
			sendPlaystate(newstate);
			break;
		case STOPPED:
			playing = false;
			if(scene != END)	{
				enterScene(END);
			}
			sendPlaystate(newstate);
			break;
	}
}

void SlideApp::endScene()
{
	switch(scene)	{
		case STARTUP:
			break;
		case LOADING:
			break;
		case PREPAUSE:
			break;
		case PLAY:
			break;
		case END:
			break;
	}
}



void SlideApp::setTimeout(int duration)
{
	if(duration > -1)	{
		nowtime = ofGetElapsedTimeMillis();
		timeout = nowtime + duration;
		doingtime = true;
	} else {
		doingtime = false;
	}
}

void SlideApp::cancelTimeout()
{
	doingtime = false;
}

void SlideApp::updateTimeout()
{
	if(doingtime)	{
		if(ofGetElapsedTimeMillis() >= timeout)
			callTimeout();
	}
}

void SlideApp::callTimeout()
{
	timeout = 0;
	doingtime = false;
	switch(scene)	{
		case STARTUP:
			enterScene(LOADING);
			break;
		case LOADING:
			enterScene(PREPAUSE);
			break;
		case PREPAUSE:
			enterScene(PLAY);
			break;
		case PLAY:
			if(autorun)	{
				nextSlideImage();
			}
			break;
		case END:
			//exit();
			//std::exit(0);
			break;
	}
}


// OSC
void SlideApp::sendPlaystate(int playstate)
{
	if(totalSenders > 0)	{
		char msg[255];
		ofxOscMessage m;
		sprintf(msg, "/%s/playstate", oscname.c_str());
		m.setAddress(msg);
		switch(playstate)	{
			case PLAYING:
				m.addStringArg( "playing" );
				break;
			case PAUSED:
				m.addStringArg( "paused" );
				break;
			case STOPPED:
				m.addStringArg( "stopped" );
				break;
		}
		for(int i = 0; i < totalSenders; i++)	{
			senders[i].sendMessage(m);
		}
	}
}
	
void SlideApp::sendEnterScene(int newscene)
{
	if(totalSenders > 0)	{
		char msg[255];
		ofxOscMessage m;
		sprintf(msg, "/%s/enterscene", oscname.c_str());
		m.setAddress(msg);
		switch(newscene)	{
			case NOSCENE:
				m.addStringArg( "noscene" );
				break;
			case STARTUP:
				m.addStringArg( "startup" );
				break;
			case LOADING:
				m.addStringArg( "loading" );
				break;
			case PREPAUSE:
				m.addStringArg( "prepause" );
				break;
			case PLAY:
				m.addStringArg( "play" );
				break;
			case END:
				m.addStringArg( "end" );
				break;
		}
		for(int i = 0; i < totalSenders; i++)	{
			senders[i].sendMessage(m);
		}
	}
}


void SlideApp::sendNewSlideImage(int imageID)
{
	if(totalSenders > 0)	{
		char msg[255];
		ofxOscMessage m;
		sprintf(msg, "/%s/slideimage", oscname.c_str());
		m.setAddress(msg);
		m.addIntArg(imageID);
		for(int i = 0; i < totalSenders; i++)	{
			senders[i].sendMessage(m);
		}
	}
}

void SlideApp::sendNewStatement(int textID)
{
	if(totalSenders > 0)	{
		char msg[255];
		ofxOscMessage m;
		sprintf(msg, "/%s/statement", oscname.c_str());
		m.setAddress(msg);
		m.addIntArg(textID);
		for(int i = 0; i < totalSenders; i++)	{
			senders[i].sendMessage(m);
		}
	}
}




void SlideApp::updateIncomingOSC()
{
	if(receiveosc)	{
		while(receiver.hasWaitingMessages()) {
			ofxOscMessage m;
			receiver.getNextMessage(&m);
			
			if ( strcmp( m.getAddress(), "/nextslideimage" ) == 0 )	{
				if(scene == PLAY)	{
					nextSlideImage();
				}
			} else if ( strcmp( m.getAddress(), "/nextstatement" ) == 0 )	{
				if(scene == PLAY)	{
					nextStatement();
				}
			} else if ( strcmp( m.getAddress(), "/setslideimage" ) == 0 )	{
				if(scene == PLAY)	{
					setSlideImage(m.getArgAsInt32(0));
				}
			} else if ( strcmp( m.getAddress(), "/setstatement" ) == 0 )	{
				if(scene == PLAY)	{
					setStatement(m.getArgAsInt32(0));
				}
			} else if ( strcmp( m.getAddress(), "/playstate" ) == 0 )	{
				if (strcmp(m.getArgAsString(0), "play" ) == 0)	{
					enterPlaystate(PLAYING);
				} else if (strcmp(m.getArgAsString(0), "pause" ) == 0)	{
					enterPlaystate(PAUSED);
				} else if (strcmp(m.getArgAsString(0), "stop" ) == 0)	{
					enterPlaystate(STOPPED);
				}
			} else if ( strcmp( m.getAddress(), "/enterscene" ) == 0 )	{
				if (strcmp(m.getArgAsString(0), "prepause" ) == 0)	{
					enterScene(PREPAUSE);
				} else if (strcmp(m.getArgAsString(0), "play" ) == 0)	{
					enterScene(PLAY);
				} else if (strcmp(m.getArgAsString(0), "end" ) == 0)	{
					enterScene(END);
				}
			} else if ( strcmp( m.getAddress(), "/autorun" ) == 0 )	{
				if(m.getArgAsInt32(0) == 0)	{
					autorun = false;
				} else {
					autorun = true;
				}
			}
		}
	}
}

// events
void SlideApp::keyPressed  (int key){ 
	// generic
	int mod = glutGetModifiers();
	switch(mod)	
	{
		case GLUT_ACTIVE_CTRL:
			key += 96;
			switch(key)	{
				case 'q':
					enterScene(END); // QUIT
					break;
			}
			break;
	}
	
	
	switch(scene)	{
		case PREPAUSE:
			switch(key)	{
				case ' ':
					callTimeout();
					break;
			}
			break;
		case PLAY:
			switch(key)	{
				case ' ':
					nextSlideImage();
					break;
			}
			break;
	}
	
}


void SlideApp::keyReleased  (int key){ 
}


void SlideApp::mouseMoved(int x, int y ){
	
}


void SlideApp::mouseDragged(int x, int y, int button){
	
}


void SlideApp::mousePressed(int x, int y, int button){
	
}


void SlideApp::mouseReleased(){
	//toolpanel.mouseReleased();
	//colourwash.mouseReleased();
}
