#ifndef _IMG_PROJECTION_H
#define _IMG_PROJECTION_H

#include "ofMain.h"
#include "ofAddons.h"
#include "guiQuad.h"
#include "graffLetter.h"
#include "vectorBrush.h"
#include "imageBrush.h"
#include "drips.h"

//this class deals with the projection of the lasertag drawing
//modes. it manages textures for the different modes and the distortion
//of the projected texture. 

class imageProjection : public baseGui{


	public:
	
		///////////////////////////////////////////////////////
		//
		//		SETUP STUFF
		//
		///////////////////////////////////////////////////////
	
		//-----------------------------------------------------	
		imageProjection(){
			drawingStyle  = 0;
			numBrushes 		= 0;
			brush			= -1;
			width			= 0;
			height			= 0;
			oldX			= 300;
			oldY			= 200;
			brushSize		= 32;
			scaleX			= 0;
			scaleY			= 0;
			toolX			= 0;
			toolY			= 0;
			mag				= 0;
			count			= 0;
			dCount			= 0;
			
			dripSpeed 	= 3.0/40.0; 	//3 pixels a second at 40fps
			dripFreq		= 3; 			//add a new drip every 3 points
			dripDirection	= 0; 			//down is default;
			doDrip			= false; 		//do we want drips?
		}
						
		//-----------------------------------------------------		
		void setup(int _width, int _height, string _brushDirectory){
			
			//this is the width and height of our  projected texture		
			width  = _width;
			height = _height;
			
			//setup our different drawing style
			GL.setup(width, height);	//graffLetters
			
			VBR.setupBrush(6);			//vectorBrush
			VBR.setupRasterImage(width, height);
			
			IB.setup(width, height);	//the image brush!
			IB.loadBrushes(_brushDirectory);

			DRIPS.setup(width, height); //drips yo
			float dripSpeed = 3.0/40.0; //2 pixels a second at 40fps
						
			//allocate our ofTextures - some are color - some are b&w
			P_PRETEX.allocate(width, height, GL_RGB);
			PTEX.allocate(width, height, GL_RGB);

			D_PRETEX.allocate(width, height, GL_RGB);
			DTEX.allocate(width, height, GL_RGB);
			
			G_PRETEX.allocate(width, height, GL_LUMINANCE);
			GTEX.allocate(width, height, GL_LUMINANCE);
			
			GTEX.setPoints(0,0,640,0,640,480,0,480);			
			PTEX.setPoints(0,0,640,0,640,480,0,480);
			DTEX.setPoints(0,0,640,0,640,480,0,480);
		}
		
				
		///////////////////////////////////////////////////////
		//
		//		PROJECTION DISTORTION TOOLS
		//
		///////////////////////////////////////////////////////
		
		//loads the projection quad settings from xml
		//and updates the quad with the saved values
		//----------------------------------------------------
		void loadSettings(string filePath){
			//CAMERA QUAD SETTINGS;
			//SET DEFAULT VALUES HERE
			//0.0-1.0 range
			warpSrc[0].x = 0;
			warpSrc[0].y = 0;
			
			warpSrc[1].x = 0.9;
			warpSrc[1].y = 0;
			
			warpSrc[2].x = 0.9;
			warpSrc[2].y = 0.9;

			warpSrc[3].x = 0;
			warpSrc[3].y = 0.9;
			
			//Now we update out quad with the default
			//values and if then if the file is found
			//we overwrite with values from xml file
			QUAD.setup("QUAD_PROJ_");
			QUAD.setQuadPoints(warpSrc);
			QUAD.readFromFile(filePath);
			
			setToolDimensions(320, 240);
			
			applyQuad();
		}
		
		
		//sets the dimensions of the mini tool - as it is much smaller
		//than the actual projected texture size
 		//-----------------------------------------------------		
		void setToolDimensions(float desiredW, float desiredH){
						
			scaleX = desiredW / width;
			scaleY = desiredH / height;
			
			scaledWidth  = desiredW;
			scaledHeight = desiredH;
		}
		
		//this sets the the quad coords for our projection textures
		//and does the calculations - we do it here so we don't
		//have to do it in the draw command
		//-----------------------------------------------------		
		void applyQuad(){
	
			ofPoint2f * tmpPts = QUAD.getScaledQuadPoints(width, height);
			
			PTEX.setPoints(  	tmpPts[0].x , tmpPts[0].y , 
								tmpPts[1].x , tmpPts[1].y , 
								tmpPts[2].x , tmpPts[2].y , 
								tmpPts[3].x , tmpPts[3].y );
								
			GTEX.setPoints(  	tmpPts[0].x , tmpPts[0].y , 
								tmpPts[1].x , tmpPts[1].y , 
								tmpPts[2].x , tmpPts[2].y , 
								tmpPts[3].x , tmpPts[3].y );	
								
			DTEX.setPoints(  	tmpPts[0].x , tmpPts[0].y , 
								tmpPts[1].x , tmpPts[1].y , 
								tmpPts[2].x , tmpPts[2].y , 
								tmpPts[3].x , tmpPts[3].y );	
																
		}
		
		//this is for selecting the quad corners of the projection
		//directly - should be hooked up to mouse down
		 //-----------------------------------------------------		
		int selectQuad(int x, int y, int xCheck, int yCheck, int w, int h, int hitArea){
			bool isSelected = QUAD.selectPoint(x, y, xCheck, yCheck, w, h, hitArea);
			
			if(isSelected)whichToolSelected = 1;
			
			return whichToolSelected;
		}
		
		//this is for updating te quad corners of the projection 
		//directly - should be used on mouse drag
		//-----------------------------------------------------		
		int updateQuad(int x, int y, int xCheck, int yCheck,  int w, int h){
			bool isSelected = QUAD.updatePoint(x, y, xCheck, yCheck,w, h);
			applyQuad();

			return whichToolSelected;			
		}
		
		//this is for manipulation the projection quad with the mini tool
		//should be hooked up to mouse down 
		//-----------------------------------------------------		
		int selectMiniQuad(int x, int y, int hitArea){
			bool isSelected = QUAD.selectPoint(x, y, toolX, toolY, scaledWidth, scaledHeight, hitArea);
		
			if(isSelected)whichToolSelected = 2;
			return whichToolSelected;			
		}
				
		//this is for moving one of the quad corners
		//should be hooked up to mouse drag		
		//-----------------------------------------------------		
		int updateMiniQuad(int x, int y){
			QUAD.updatePoint(x, y, toolX, toolY, scaledWidth, scaledHeight);
			applyQuad();
			
			return whichToolSelected;			
		}
		
		//unselects the quad corner
		//should be hooked up to the mouse up event
		//-----------------------------------------------------		
		void releaseAllQuads(){
			QUAD.releaseAllPoints();
			whichToolSelected = 0;
		}
		
		///////////////////////////////////////////////////////
		//
		//		THIS IS WHERE WE PAINT!
		//
		///////////////////////////////////////////////////////
		//--------------------------------------------

		
		//--------------------------------------------
		void paintImageBrush(float currentX, float currentY, int brushWidth, int lineResolution, bool newStroke){
						
			drawingStyle = 0;
			
			if(newStroke){
				int red 	= 160+rand()%95;
				int green 	= 160+rand()%95;
				int blue	= 160+rand()%95;
				
				IB.setColor(red, blue, green);
				DRIPS.setColor(red, blue, green);
			}			
			
			IB.paint(currentX * (float)width, currentY * (float)height, brushWidth, lineResolution, newStroke);
			if(doDrip && count%dripFreq==0){
				
				float accel = dripSpeed * (0.25  + (float)(rand()%750)*0.001);
				DRIPS.addDrip(currentX * (float)width, currentY * (float)height, dripDirection, accel, 3);
			}
			unsigned char * pix = IB.getPixels();
			
			PTEX.loadData(pix, width, height, GL_RGB);
			P_PRETEX.loadData(pix, width, height, GL_RGB);

			dCount++;
		}
		
		//-------------------------
		void idleDrips(){
			if(!doDrip)return;
			
			unsigned char * pix = IB.getPixels();
			DRIPS.updateDrips(pix);
			
			PTEX.loadData(pix, width, height, GL_RGB);
			P_PRETEX.loadData(pix, width, height, GL_RGB);
		}
		
		//--------------------------------------------
		void dripSettings( bool _doDrip, int _dripDirection, int _dripFreq, float _dripSpeed){
			doDrip 			= _doDrip;
			dripFreq		= _dripFreq;
			dripDirection	= _dripDirection;
			dripSpeed		= _dripSpeed;
		}
				
		//new vector brush style - super dope!
		//--------------------------------------------
		void paintVectorBrush(float currentX, float currentY, int brushWidth, bool newStroke){
						
			drawingStyle = 2;
			
			VBR.addStroke(currentX * (float)width, currentY * (float)height, brushWidth, newStroke);
			VBR.drawIntoPixels();
						
			unsigned char * pix = VBR.getPixels();
			
			GTEX.loadData(pix, width, height, GL_LUMINANCE);
			G_PRETEX.loadData(pix, width, height, GL_LUMINANCE);
		}
		
		
		//dope graff style letters made by Zach Lieberman <-- (nerd)
		//-----------------------------------------------------
		void paintGraffLetters(float currentX, float currentY, float prevX, float prevY, int brushWidth, bool newStroke){
			drawingStyle = 1;
			
			int dstX = (int)(currentX * (float)width);
			int dstY = (int)(currentY * (float)height);
			
			int srcX = (int)(prevX * (float)width);
			int srcY = (int)(prevY * (float)height);
			
			if(newStroke){
				GL.newLetter();
				srcX = dstX;
				srcY = dstY;
			}
			
			if(brushWidth != GL.getBrushSize())GL.setBrushSize(brushWidth+12); //this is so the size is equivilant
			GL.addLine(dstX, dstY, srcX, srcY);	
			
			unsigned char * glPixels = GL.getImageAsPixels();
			GTEX.loadData(glPixels, width, height, GL_LUMINANCE);
			G_PRETEX.loadData(glPixels, width, height, GL_LUMINANCE);
		}
		
		//they have to be updated too!
		//-----------------------------------------------------
		void idleGraffLetters(){
			GL.update();
		}

		//if we want to clear the screen
		//-----------------------------------------------------
		void clear(){
			
			//clear drips
			dCount = 0;
			DRIPS.clear();
			unsigned char * dripPix = DRIPS.getPixels();
			DTEX.loadData(dripPix, width, height, GL_RGB);
			D_PRETEX.loadData(dripPix, width, height, GL_RGB);
			
			//clear vector brush
			VBR.clear();

			//clear the image brush and update the texture			
			IB.clear();
			
			unsigned char * pix = IB.getPixels();

			PTEX.loadData(pix, width, height, GL_RGB);
			P_PRETEX.loadData(pix, width, height, GL_RGB);
			
					
			//clear graff letters
			GL.clear();		
			GL.update();
			unsigned char * glPixels = GL.getImageAsPixels();
			GTEX.loadData(glPixels, width, height, GL_LUMINANCE);
			G_PRETEX.loadData(glPixels, width, height, GL_LUMINANCE);
			
		}
		
		///////////////////////////////////////////////////////
		//
		//		THIS IS WHERE WE DRAW TO THE SCREEN
		//
		///////////////////////////////////////////////////////
		
		//Draws our mini quad tool with the projected texture at 
		//the precomputed scale set by setToolDimensions
		//-----------------------------------------------------
		void drawMiniProjectionTool(float x, float y, bool showOutline){
			
			toolX = x;
			toolY = y;
		
			glPushMatrix();
				glTranslatef(x, y, 0);
				glScalef(scaleX, scaleY, 1.0);
				ofSetColor(0xFFFFFF);
				if(showOutline){
					ofNoFill();
					ofRect(0,0,width, height);
					ofFill();
				}
				ofEnableAlphaBlending();
				if(drawingStyle) GTEX.draw();
				else PTEX.draw();
				ofDisableAlphaBlending();
			glPopMatrix();
			
			QUAD.draw(x, y, scaledWidth, scaledHeight);
		}
		
		//Draws the tool at any size
		//-----------------------------------------------------
		void drawProjectionToolHandles(float x, float y, float w, float h, bool showOutline, bool highlyVisible){
			
			float sX = w/width;
			float sY = h/height;
		
			glPushMatrix();
				glTranslatef(x, y, 0);
				glScalef(sX, sY, 1.0);
				ofSetColor(0xFFFFFF);
				if(showOutline){
					ofNoFill();
					ofRect(0,0,width, height);
					ofFill();
				}
			glPopMatrix();
			
			if(highlyVisible){
				QUAD.draw(x, y, w, h, 255, 255, 255, 3);
			}else{
				QUAD.draw(x, y, w, h);
			}
		}
		
		//the texture that is projected
		//-----------------------------------------------------
		void drawProjectionTex(float x, float y, float w, float h){
			
			float sX = w/width;
			float sY = h/height;
			
			glPushMatrix();
			glTranslatef(x,y,0);
			glScalef(sX, sY, 1);

			glColor4f(1.0, 1.0, 1.0, 1.0);
			ofEnableAlphaBlending();
			
			if(drawingStyle >= 1) GTEX.draw();
			else PTEX.draw();
			
			/*
			if(drawingStyle >= 1) GTEX.draw();
			else PTEX.draw();
			
			//antialising for vector mode!
			glBlendFunc(GL_SRC_ALPHA, GL_ONE);
			
			if(drawingStyle >= 1) GTEX.draw();
			else PTEX.draw();
			
			glColor4f(1.0, 1.0, 1.0, 0.3);
			
			glTranslatef(1, 0, 0);
			if(drawingStyle >= 1) GTEX.draw();
			else PTEX.draw();
			
			glTranslatef(-2, 0, 0);
			if(drawingStyle >= 1) GTEX.draw();
			else PTEX.draw();
			
			glTranslatef(1, 1, 0);
			if(drawingStyle >= 1) GTEX.draw();
			else PTEX.draw();
			
			glTranslatef(-1, -2, 0);
			if(drawingStyle >= 1) GTEX.draw();
			else PTEX.draw();
			*/
							
			ofDisableAlphaBlending();
			glPopMatrix();
		}
		
		//draws an uwarped texture of what is projected
		//-----------------------------------------------------
		void drawPreviewTex(float x, float y, float w, float h){
			ofEnableAlphaBlending();
			if(drawingStyle)G_PRETEX.draw(x,y,w,h);
			else P_PRETEX.draw(x,y,w,h);
			ofDisableAlphaBlending();
		}		
		
		//-----------------------------------------------------
		void drawBrush(float x, float y, float w, float h, bool withName){
			
			ofSetColor(0xFFFFFF);
			
			IB.drawBrush(x, y, w, h);
			ofNoFill();
			ofRect(x, y, w, h);
			ofFill();
			
			if(withName){
				drawText(IB.getBrushName(), x, y + h + 12);
			}
			
		}
		
			
		ofImage 	 		IMG;
		ofImage		 		TMP;
		ofDirList 	 		DL;
		vectorBrushRaster 	VBR;
		graffLetter	 		GL;
		imageBrush			IB; //back with a vengance
		drips				DRIPS;
		
		//our paint texture and its preview texture
		ofTextureAdv PTEX;
		ofTexture 	 P_PRETEX;
		
		//our drips texture and its preview texture
		ofTextureAdv DTEX;
		ofTexture 	 D_PRETEX;	
		
		//our graff letters / vector brush texture and its preview texture
		ofTextureAdv GTEX;
		ofTexture 	 G_PRETEX;
		
		guiQuad		 QUAD;
		ofPoint2f 	 warpSrc[4];
		
		int drawingStyle;

		int toolX, toolY, count, dCount,  whichToolSelected, dripFreq, dripDirection;
		float scaleX, scaleY, mag, dripSpeed;
		bool doDrip;
				
		int width, height, scaledWidth, scaledHeight, oldX, oldY, brush, numBrushes, brushSize;

};

#endif