#ifndef _DRIP_H
#define _DRIP_H

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

#define MAX_DRIPS 400

class drip{
	
	public: 
		drip(){
			reset();
		}
		
		//-----------------------------------	
		void reset(){
			dripping	= false;
			direction 	= 0;
			dripLength  = 0;
			distance	= 0;
			dripWidth   = 0;
			
			r			= 255;
			g			= 255;
			b			= 255;
			
			vel.set(0,0);
			pos.set(0,0);
			dst.set(0,0);
			pre.set(0,0);

		}
		
		//-----------------------------------	
		void setColor(float red, float green, float blue){
			r = red;
			g = green;
			b = blue;
		}
		
		//-----------------------------------	
		bool setup(float x, float y, int imageW, int imageH, int _direction, float length, float speed, int dWidth){
			
			//set position
			pos.x = x;
			pos.y = y;
			
			//lets make sure it lies in the image
			if(pos.x < 0)pos.x = 0;
			else if(pos.x >= imageW)pos.x = imageW -1;
			if(pos.y < 0)pos.y = 0;
			else if(pos.y >= imageH)pos.y = imageH -1;
			
			//set pre to be the same as position
			pre = pos;
						
			//make sure we have only 4 directions
			direction = _direction;
			
			if(direction < 0) direction = 0;
			if(direction > 3) direction = 3;
			
			
			//figure out target and 
			//acceleration based on direction
			if(direction == 0){	//south
				dst.x = pos.x;
				dst.y = pos.y + length;
				vel.x = 0;
				vel.y = speed;
			}
			else
			if(direction == 1){	//west
				dst.x = pos.x - length;
				dst.y = pos.y;
				vel.x = -speed;
				vel.y = 0;			}
			else
			if(direction == 2){	//north
				dst.x = pos.x;
				dst.y = pos.y - length;
				vel.x = 0;
				vel.y = -speed;				
			}
			else
			if(direction == 3){	//east
				dst.x = pos.x + length;
				dst.y = pos.y;
				vel.x = speed;
				vel.y = 0;
			}
									
			//now lets check we haven't gone over our image border
			if(dst.x >= imageW){
				dst.x = imageW - 1;
			}
			else if(dst.x < 0){
				dst.x = 0;
			}
			
			if(dst.y >= imageH){
				dst.y = imageH - 1;
			}
			else if(dst.y < 0){
				dst.y = 0;
			}
			
			
			//recalculate the dripLength
			dripLength = abs(dst.x - pos.x) + abs(dst.y - pos.y);

			//lets not start dripping until specifically told to do so
			dripping	= false;
			
			//store our drip width
			dripWidth = dWidth;
			
			return true;
		}
		
		//-----------------------------------	
		bool update(){
						
			if(!isDripping())return false;
						
			//lets figure out how far we will have travelled
			distance += abs(vel.x) + abs(vel.y);
			
			//only update pos if doing so would keep us
			//under the dripLength - otherwise we could
			//go outside of the image dimensions
			
			//printf("dist is %f - length is %i \n", distance, dripLength);
			
			if(distance >= dripLength){
				stopDripping();
				return false;
			}else{
				
				//lets see if we have moved less than 1 pixel
				float preDist = abs( (pre - pos).length() );
				
				//if(preDist > 1){
					//update our previous point
					pre = pos;
				//}
				
				//update our current point
				pos.x += vel.x;
				pos.y += vel.y;
								
				//if we have moved less than a pixel
				//then no need to draw 
				//if(preDist < 1){
				//	return false;
				//}
				
			}
		
			return true;		
		}
		
		//-----------------------------------	
		void startDripping(){
			dripping = true;
		}
				
		//-----------------------------------	
		void stopDripping(){
			dripping = false;
		}
		
		//-----------------------------------	
		bool isDripping(){
			return dripping;
		}
		
		ofVec2f pre;	//previous position
		ofVec2f pos;	//our position
		ofVec2f dst;	//our destination (target)
		ofVec2f vel;	//velocity
				
		int dripWidth;
		int dripLength;
		int direction;
		
		float distance;
		float r, g, b;
		
		bool dripping;
	
};


class drips{
	
	public: 
		
		//------------------------------------
		drips(){
			currentDrip = 0;
			numDrips	= 0;
			width 		= 0;
			height 		= 0;
			totalPixels = 0;
			red			= 255;
			green		= 255;
			blue		= 255;
			bSetup		= false;
		}
		
		//------------------------------------
		void setup(int w, int h){
			if(bSetup)return;
		
			width  = w;
			height = h;
			
			totalPixels = width * height * 3;
			pixels = new unsigned char[totalPixels];
			
			clear();		
			bSetup = true;
		}
		
		
		//-----------------------------------------------------
		void setColor(unsigned char r, unsigned char g, unsigned char b){
			red		= r;
			green	= g;
			blue 	= b;
		}
		
		//------------------------------------
		void clear(){
			
			for(int i = 0; i < numDrips; i++){
				DRIP[i].reset();
			}
			
			currentDrip = 0;
			numDrips	= 0;
			memset(pixels, 0, totalPixels);
			
			
		}
		
		
		//------------------------------------
		bool addDrip(int x, int y, int direction, float speed, int dripWidth){
			if(!bSetup){
				printf("addDrip: call setup first!!\n");	 
				return false;
			}
			
			if(numDrips >= MAX_DRIPS){
				printf("addDrip: no more drips available till clear - max is %i\n", MAX_DRIPS);
				return false;
			}
			
			if(x > width || y > height || x < 0 || y < 0){
				printf("addDrip: coordinates (%i, %i) outside image [%i x %i]! \n",x, y, width, height); 
				return false;
			}
			
			
			float length = 0;
			
			if(direction == 0){
				length = rand()%(height - y);
			}
			else
			if(direction == 1){
				length = rand()%x;
			}
			else 
			if(direction == 2){
				length = rand()%y;
			}
			else
			if(direction == 3){
				length = rand()%(width - x);
			}
			
			length *= 0.75;
			
			if((int)length == 0){
				printf("addDrip: couldn't add drip: length is %i coord is (%i, %i), image %i by %i and direction %i\n", (int)length,  x, y, width, height,direction);
				return false;
			}			
			
			DRIP[numDrips].reset();
			DRIP[numDrips].setup(x, y, width, height, direction, (float)length, speed, dripWidth);
			DRIP[numDrips].setColor(red, green, blue);
			DRIP[numDrips].startDripping();
			
			numDrips++;
			
			return true;
		}
		
		//same as internal one but takes an array to draw into
		//there is no checking of size etc so be careful!
		//-----------------------------------
		void updateDrips(unsigned char * pix){
			if(!bSetup){
				printf("addDrip: call setup first!!\n");		 
				return;
			}
		
			for(int i = 0; i < numDrips; i++){
				if(DRIP[i].isDripping() && DRIP[i].update() ){
					//then we can try and update our drip
					//into our pixels					
					if(DRIP[i].direction == 0 || DRIP[i].direction == 2){
						fastVerticalLine(pix, DRIP[i].pre.x, DRIP[i].pre.y, DRIP[i].pos.y,  1,  DRIP[i].r, DRIP[i].g, DRIP[i].b);    
					}else{
						fastHorizontalLine(pix, DRIP[i].pre.y, DRIP[i].pre.x, DRIP[i].pos.x,  1,  DRIP[i].r, DRIP[i].g, DRIP[i].b);    
					}
				}
			}
		}
		
		//-----------------------------------
		void updateDrips(){
			updateDrips(pixels);
		}
		
		//----------------------------
		unsigned char * getPixels(){
			return pixels;
		}
		
				
	protected:

		//------------------------------------
		void fastVerticalLine(unsigned char * pix, int xPos, int y0, int y1, int lineWidth,  unsigned char r, unsigned char g, unsigned char b){
			if(xPos < 0 || xPos >= width)return;
			
			int x0 = xPos - lineWidth;
			int x1 = xPos + lineWidth;
			
			if(x0 < 0) x0 = 0;
			if(x1 >= width) x1 = width-1;
	
			int y = y0;
			
			int incr = 1;
			if(y1 < y0)incr = -1;
			
			int pos = 0;
			int x = x0;
			int stride;
			
			while(y != y1){
				stride = y*width;
				for(x = x0; x <x1; x++){

					pos = 3*(stride+x);
					
					pix[    pos] = r;
					pix[1 + pos] = g;
					pix[2 + pos] = b;
				}
				y+=incr;
			}
		}
		
		//------------------------------------
		void fastHorizontalLine(unsigned char * pix, int yPos, int x0, int x1, int lineWidth,  unsigned char r, unsigned char g, unsigned char b){
			if(yPos < 0 || yPos >= height)return;
			
			int y0 = yPos - lineWidth;
			int y1 = yPos + lineWidth;
			
			if(y0 < 0) y0 = 0;
			if(y1 >= height) y1 = height-1;
	
			int x = x0;
			
			int incr = 1;
			if(x1 < x0)incr = -1;
			
			int pos = 0;
			int y = y0;
			int stride;
			
			while(x != x1){
				for(y = y0; y <y1; y++){
					stride = y*width;
					pos = 3*(stride+x);
					pix[    pos] = r;
					pix[1 + pos] = g;
					pix[2 + pos] = b;
				}
				x+=incr;
			}
		}
		

		/*
		//------------------------------------
		//modified from http://www.cs.unc.edu/~mcmillan/comp136/Lecture6/Lines.html
		//----------------------------
		void lineFastRGB(unsigned char * pix, int x0, int y0, int x1, int y1, unsigned char r, unsigned char g, unsigned char b)
   		{
			int dy = y1 - y0;
			int dx = x1 - x0;
			int stepx, stepy;
			int pPos = 0;

			if (dy < 0) { dy = -dy;  stepy = -width; } else { stepy = width; }
			if (dx < 0) { dx = -dx;  stepx = -1; } else { stepx = 1; }
			dy <<= 1;
			dx <<= 1;

			y0 *= width;
			y1 *= width;
			
	        pPos = 3*(x0+y0);
	        if(pPos > 0 && pPos < totalPixels-2 && x0 < width && x0 > 0){
				pix[    pPos] = r;
				pix[1 + pPos] = g;
				pix[2 + pPos] = b;
			}
			
			if (dx > dy) {
			    int fraction = dy - (dx >> 1);
			    while (x0 != x1) {
			        if (fraction >= 0) {
			            y0 += stepy;
			            fraction -= dx;
			        }
			        x0 += stepx;
			        fraction += dy;
			        
			        pPos = 3*(x0+y0);
	       			 if(pPos > 0 && pPos < totalPixels-2 && x0 < width && x0 > 0){
						pix[    pPos] = r;
						pix[1 + pPos] = g;
						pix[2 + pPos] = b;
					}
			    }
			} else {
			    int fraction = dx - (dy >> 1);
			    while (y0 != y1) {
			        if (fraction >= 0) {
			            x0 += stepx;
			            fraction -= dy;
			        }
			        y0 += stepy;
			        fraction += dx;
			        
			        pPos = 3*(x0+y0);
	       			 if(pPos > 0 && pPos < totalPixels-2 && x0 < width && x0 > 0){
						pix[    pPos] = r;
						pix[1 + pPos] = g;
						pix[2 + pPos] = b;
					}
			    }
			}
    	}
    	*/



		bool bSetup;
		int width, height, totalPixels, currentDrip, numDrips;
		unsigned char red, green, blue;

		drip DRIP[MAX_DRIPS];
		unsigned char * pixels;

};

#endif






































