#include "singleBouncer.h"

#include "testApp.h"

//-----------------------------------------------------------------------
singleBouncer::singleBouncer(){
	theChannel		= 2;
	numCards		= 0;
	currentNote		= 0;
	levitationAmnt  = 0;
	playingNumber	= 0;
	hide			= false;
	bComingIn		= false;
	animationType	= 0;
	planePtr		= NULL;
}


//---------------------------------------
void singleBouncer::setup(int camW, int camH, int markerSizeMM, int midiChannel, int setCardMode){
	cameraW = camW;
	cameraH = camH;
		
	cardMode = setCardMode;
	
	fakeMarker.setup(camW, camH, markerSizeMM, true);
	telePortMarker.setup(camW, camH, markerSizeMM, true);
	reflectMarker.setup(camW, camH, markerSizeMM, true);

	theChannel = midiChannel;
	
	Akzidenz.loadFont("fonts/AkzidenzGroteskBE-Super.otf", 32);	
	ballShadow.loadImage("shadowBall.png");
}


//---------------------------------------
bool singleBouncer::findClosestCardMatchingAngle(vector <card> & cards){

	int numFound = cards.size();
	//find the closest matching card that isn't the one we were just at

	float closestAngle		= 999999;
	int closestID			= -1;
	ofxVec2f delta;	
		
	for(int k = 0; k < numFound; k++){		
		if( cards[k].id == dstCard.id ) continue;	//don't want to find ourselves
		delta = cards[k].center - dstCard.center;
		delta.normalize();
		
		float angle = fabs( dstCard.angleVec.angle(delta) );
	
		if(angle < closestAngle){
			closestAngle = angle;
			closestID = k;
		}
	}
	
	//if we have a candidate - then lets update our src and target 
	//and set off on our new journery
	if( closestID > -1){
		shape.begin( cards[closestID].center );
		srcCard = dstCard;
		dstCard = cards[closestID];
		return true;
	}
	return false;
}

//---------------------------------------
bool singleBouncer::findNextNearestCard(vector <card> & cards){

	int numFound = cards.size();
	//find the closest matching card that isn't the one we were just at

	float closestDist		= 999999;
	int closestID			= -1;
	ofxVec2f vec;			
		
	int startPos = ofRandom(0, numFound);
	for(int i = 0; i < numFound; i++){
		int k = i;
		
		if( cards[k].id == dstCard.id ) continue;					//don't want to find ourselves
		if( numFound > 2 && cards[k].id == srcCard.id) continue;	//this stops the card going back to the previous card when there are more than two cards
		//if( !cards[k].cached == false )continue;					//we don't want fresh off the boat cards
		
		vec =  (cards[k].center - shape.pos);
		float dist = vec.length();
		if(dist < closestDist){
			closestDist = dist;
			closestID = k;
			
			//some variation so the pattern doesn't just repeat :)
			//scaled to the number of cards
			if( (int)ofRandom(0, 4 + numFound) == 4 )break;
		}
	}

	//if we are at are target - then lets update our src and target 
	//and set off on our new journery
	if( closestID > -1){
		shape.begin( cards[closestID].center );
		srcCard = dstCard;
		dstCard = cards[closestID];
		return true;
	}
	return false;
}

//-----------------------------------------------------------------------
void singleBouncer::playNoteFromCard(card & theCard){
	
	int cardNumber = theCard.getCard();
	
	float pitch;
	
	//if we want to split octaves and the card is either a spade or diamond then
	//go up an octave
	if( SPLIT_OCTAVES && ( theCard.getSuit() == 1 || theCard.getSuit() == 2)  ){
		cardNumber += 13;
		pitch	 =  (float)cardNumber / 26.1;
	}else{
		pitch	 =  (float)cardNumber / 13.1;	
	}
	
	pitch		*=  NUM_NOTES;
	
	pitch		= MIN(pitch, NUM_NOTES);
	pitch		= MAX(0, pitch);
	
	currentNote		= notes[(int)pitch];
	
	printf("playing note! %i - card number is %i card shift is  %i  \n", currentNote, theCard.getCard(), cardNumber);
	
	currentChannel	= 2; //theChannel + (int)( 3.99 * theCard.center.x / (float)cameraW );
	midi.sendNoteOn(currentChannel, currentNote, 100);
}

//-----------------------------------------------------------------------
void singleBouncer::stopNote(){
	midi.sendNoteOff(currentChannel, currentNote, 100);
}

//-----------------------------------------------------------------------
void singleBouncer::stopAllNotes(){
	printf("\nSTOPPING ALL NOTES!!\n\n");
	for(int j = 0; j < 4; j++){
		for(int i = 0; i < NUM_NOTES; i++) midi.sendNoteOff(theChannel + j, notes[i], 100);
	}
}

//-----------------------------------------------------------------------
ofxPoint2f singleBouncer::randomOutofFrame(){
	//lets start some where outside of the frame
	bool aboveBellow = ofRandom(0, 2);
	bool leftRight	 = ofRandom(0, 2);
	ofxPoint2f pos;

	if(aboveBellow)pos.y = ofRandom(-40 , -200);
	else pos.y = ofRandom(cameraH + 40 , cameraH + 200);
	
	if(leftRight)pos.x = ofRandom(-40 , -200);
	else pos.x = ofRandom(cameraW + 40 , cameraW + 200);
	
	return pos;
}

//-----------------------------------------------------------------------
void singleBouncer::update(vector <card> & cards, float bounceSpeed, float levitationSpeed){
	numCards = cards.size();

	//NO CARDS FOUND RESET STUFF
	if( numCards == 0){
		shape.reset();
		stopAllNotes();
		animationType	= 0;				
		levitationAmnt	= 0;
		hide			= true;
		bComingIn		= true;
		incomingReflect	= false;
		incomingTeleport = false;
	}
	
	//WE ARE COMING IN FROM OFF CAMERA
	if( shape.getState() == 0 && numCards >= 2){
		
		ofxPoint2f startPos = randomOutofFrame();
		dstCard = cards[ofRandom(0, numCards-1)];
		shape.begin(startPos, dstCard.center);
		
		animationType		= 0;				
		levitationAmnt		= 0.0;
		hide				= false;
		bComingIn			= true;
		incomingReflect		= false;
		incomingTeleport	= false;		
	}
	
	//WE HAVE HIT OUR TARGET
	//--PLAY A NOTE
	//--THEN FIND OUR NEXT TARGET
	if( shape.getState() == 2 ){
		
		bool bTeleport = false;
		bool bReflectFaces = false;

		if( ACES_TELEPORT ){
			//Ace is Wild! :)
			//if we landed on an ace - lets teleport to another card
			//we need to make people feel something special happened
			//so we should make sure to either have a sound effect or
			//some special visual or something - otherwise it looks like a bug
			//have the card it teleports out of be silent works - but breaks the rythum

			int currentNumber = dstCard.getCard();
			if( currentNumber == 0 && dstCard.bFaceCard != true){
				int aceID = dstCard.id;
				for(int i = 0; i < numCards; i++){
					int cardNum = cards[i].getCard();
					if( cards[i].id != aceID){
						bTeleport	= true;
						dstCard		= cards[i];
						shape.setPosition(dstCard.center);
						if( (int)ofRandom(0, 3 + numCards) == 1)break; //choose a random card to jump out of 
					}
				}
			}
		}else if( REFLECT_FACES ){
			if( dstCard.bFaceCard && dstCard.getCard() + 1 > 10 ){	
				bReflectFaces = true;
				bTeleport = false;
			}
		}				

		bool wasCardFound = false;
		//if we are reflecting faces then match the next card by angle - otherwise match by distance
		if( bReflectFaces )wasCardFound = findClosestCardMatchingAngle(cards);
		else wasCardFound = findNextNearestCard(cards);

		//AFTER THIS POINT SRC CARD HAS NOW BECOME DST CARD
		//AND DST CARD IS THE NEW CARD WE HAVE FOUND.
		//THE TWO ABOVE FUNCTIONS HANDLE THE SWITCHING - A LITTLE OPAQUE I KNOW - SORRY!
		
		if(!wasCardFound){
			printf("\n\n COULDN'T FIND CARD - STOPPING AND HIDING :) \n\n");
			shape.reset();
			
			animationType	= 0;
			levitationAmnt	= 0.0;
			bComingIn		= false;
			hide			= true;
			incomingReflect	= false;
			incomingTeleport = false;
					
		}else{
			animationType		= 1;
			levitationAmnt		= 0.0;
			hide				= false;
			bComingIn			= false;
			incomingReflect		= false;
			incomingTeleport	= false;
			
			playingCard		= srcCard;
			playingNumber	= playingCard.getCard() + 1;

			//turn off the note and maybe play a new one?
			stopNote();			
			
			hitTime = ofGetElapsedTimef();
			hitLevel = 0;
			hitTarget = 1;	
			
			for (int i = 0; i < 10; i++){
				target[i].set(ofRandom(-20,20), ofRandom(-20, 20), ofRandom(100,150));
			}
				
			if(bTeleport){
				animationType = 2; //we want this to be a teleport animation					
			}else if( bReflectFaces ){
				animationType = 3; //reflecting animation - or faces animation???? - skips note?
				playNoteFromCard(playingCard); //maybe play something else?
			}else{
				playNoteFromCard(playingCard);
			}
			
			fakeMarker.setMarkerPoints(playingCard.sqPts[0], playingCard.sqPts[1], playingCard.sqPts[2], playingCard.sqPts[3]);
		}
	}
	
	
	//THIS STUFF HAPPENS AS THE BOUNCER IS BETWEEN POINTS
	
	//only update our leviation amount if we are animating
	//once it is finished then we turn off the animation
	if(  shape.getState() == 1 ){
		if(animationType != 0){
			levitationAmnt += levitationSpeed;
			if( levitationAmnt >= 1.0 ){
				levitationAmnt = 0.0;
				animationType = 0;
			}
		}
		
		//for teleport cards and reflect cards we need to animate before we reach the card
		
		if( ACES_TELEPORT ){
			//we have an ace!
			if(dstCard.getCard() == 0 && dstCard.bFaceCard != true){
				incomingReflect  = false;		
				incomingTeleport = true;
				telePortOutCard = dstCard;
				telePortMarker.setMarkerPoints(telePortOutCard.sqPts[0], telePortOutCard.sqPts[1], telePortOutCard.sqPts[2], telePortOutCard.sqPts[3]);
			}else{
				incomingTeleport = false;
			}
		}
		
		if( REFLECT_FACES ){
			if( dstCard.bFaceCard && dstCard.getCard() + 1 > 10 ){
				incomingTeleport = false;
				incomingReflect  = true;
				reflectCard = dstCard;
				reflectMarker.setMarkerPoints(reflectCard.sqPts[0], reflectCard.sqPts[1], reflectCard.sqPts[2], reflectCard.sqPts[3]);
			}else{
				incomingReflect  = false;
			}
		}
	}	
	
	shape.update(bounceSpeed, true);
}


//------------------------------------
void singleBouncer::draw(arPlane *planePtrIn){
	planePtr = planePtrIn;
	if( planePtr == NULL)return;
	if( hide )return;
//	
//	printf("\n\nshapeState is %i \n", shape.getState());
//	printf("bComingIn is %i \n", bComingIn);
//	printf("pos is %f %f \n", shape.pos.x, shape.pos.y);
//	printf("src is %f %f \n",  shape.src.x, shape.src.y);
//	printf("dst is %f %f \n",  shape.dst.x, shape.dst.y);
//	printf("src card is %f %f \n", srcCard.center.x, srcCard.center.y);
//	printf("dst card is %f %f \n", dstCard.center.x, dstCard.center.y);
//	printf("pct is %f \n\n", shape.pct);
	
	ofEnableAlphaBlending();
	
		if( animationType == 1 ){
			drawNumberShadow();
		}else if(animationType == 2){
			drawTeleportOutShadow(); //the animation from the card that is teleported to
		}else if(animationType == 3){
			drawReflectOutShadow();	//same card as drawReflectIn - but gives you an option for a different animation
		}
		
		//this is a special animation 
		//this is for the animation as the bouncer approaches the ace/teleporter
		if( incomingTeleport ){
			drawTeleportInShadow();
			drawTeleportIn(); //currently it is just a circle opening up on the ace
		}
		
		//this one is for the reflect animation 
		//as the card approaches 
		if( incomingReflect ){
			drawReflectInShadow();
			drawReflectIn(); // as the bouncer approaches the reflect card - do something cool?
		}

		//the bouncer
		drawBounce();
		
		if( animationType == 1 ){
			drawNumber();
		}else if(animationType == 2){
			drawTeleportOut(); //the animation from the card that is teleported to
		}else if(animationType == 3){
			drawReflectOut(); //same card as drawReflectIn - but gives you an option for a different animation
		}
			
	ofDisableAlphaBlending();
}


//clean me :)
//-------------------------------
void singleBouncer::drawBounce(){
	if( planePtr == NULL)return;

	//drawing the bouncer
	float aniPct = shape.pct;
	
	ofxVec2f delta;
	ofxPoint2f pt, pt1, pt2, mid;
	pt  = planePtr->screenToMarker( shape.pos.x, shape.pos.y );
	pt1 = planePtr->screenToMarker( shape.src.x, shape.src.y );	
	pt2 = planePtr->screenToMarker( shape.dst.x, shape.dst.y );	

	mid			= pt1 * 0.5 + pt2 * 0.5;	
	delta		= pt2 - pt1;
	float dist	= delta.length();
	delta.normalize();	
				
	//THIS IS THE NUMBER OF CIRCLES / SPHERES 
	//FOR THE BOUNCER TRAIL
	//FOR GLUT SPHERE HALF THE DIST AMNT
	int numTrail = 30 + dist;
	numTrail = MIN(300, numTrail);	
	
	//THE SIZE OF THE BOUNCER
	int bouncerSize = 12;
	
	//draw the shadow of the bouncer - this could be cleaned up like crazy
	if( planePtr->isMarkerSet() ){
		planePtr->loadARMatrix(drawWidth, drawHeight);
			
			float pctMod, posMod, shadowOpacity, shadowScale, posX, posY;
		glTranslatef(0, 0, 2);
			ofSetRectMode(OF_RECTMODE_CENTER);
			int numShadowTrail = numTrail / 2;
			for(int i = 0; i < numShadowTrail; i++){
				pctMod = (float) i / (float)numShadowTrail;
				posMod = 0.5 + pctMod * 0.5;
				
				shadowOpacity = shape.cosPct * 50;
				shadowScale	= sin(pctMod * HALF_PI) * dist * 0.05;
				shadowScale += ((float)bouncerSize * 2.5) * pctMod;
				
				posX = pt.x * posMod + pt1.x * (1.0 - posMod);
				posY = pt.y * posMod + pt1.y * (1.0 - posMod);
				
				ofSetColor(0, 0, 0,  0.08 * ( 130 + shadowOpacity) );
				ballShadow.draw(posX, posY,  shadowScale, shadowScale);
			}
			ofSetRectMode(OF_RECTMODE_CORNER);
		
		planePtr->unloadARMatrix();
	}				
	
	//draw the bouncer
	if( planePtr->isMarkerSet() ){
		planePtr->loadARMatrix(drawWidth, drawHeight);

			for(int i = 0; i < numTrail; i++){
				glPushMatrix();
					float pctMod = (float) i / (float)numTrail;

					glTranslatef(mid.x, mid.y, 0);
					glRotatef( ( 0.5 + 0.5 * pctMod ) * aniPct * 180, -delta.y, delta.x, 0);
				
					if( cardMode == 0 ){
						if( i != numTrail -1)ofSetColor(255, 255, 0, 20);
						else ofSetColor(00, 55, 230, 160);
					}else if(cardMode == 1){
						if( i != numTrail -1)ofSetColor(170, 0, 0, 20);
						else ofSetColor(200, 20, 60, 160);
					}else if(cardMode == 2){
						if( i != numTrail -1)ofSetColor(180, 209, 0, 20);
						else ofSetColor(180, 180, 0, 100);
					}
					
					//glTranslatef(pt1.x - mid.x, pt1.y - mid.y, 0);
					//glutSolidSphere(11  * pctMod, 16, 16);
					
					ofCircle(pt1.x - mid.x, pt1.y - mid.y, 11  * pctMod);		
														
				glPopMatrix();
			}
		planePtr->unloadARMatrix();
	}
}

//------------------------------------
void singleBouncer::drawNumberShadow(){
	
	if (playingCard.bFaceCard == true){
		return;
	}
	
	hitLevel = 0.93 * hitLevel + 0.07 * hitTarget;
	float elapsedTime =  ofGetElapsedTimef() - hitTime;
	
	
	fakeMarker.setMarkerPoints(playingCard.sqPts[0], playingCard.sqPts[1], playingCard.sqPts[2], playingCard.sqPts[3]);
	if( fakeMarker.isMarkerSet()){
		fakeMarker.loadARMatrix();
		
		
		ofEnableAlphaBlending();
		ofSetColor(255, 255,255);
		
		glPushMatrix();
		int pictureId = playingCard.pictureId;
		if (pictureId != -1){
			ofSetRectMode(OF_RECTMODE_CENTER);
			//glScalef(1, -1, 1);
			ofSetColor(255, 255, 255, powf((1-hitLevel),0.53f)*240);
			ofRect(0,0, 45, 84);
			
			for (int j = 0; j < theApp->cardMan.currentlyTrackedImages[pictureId].nPlanes; j++){
				glPushMatrix();
				glTranslatef(target[j].x * hitLevel, target[j].y * hitLevel, target[j].z * hitLevel);
				glBlendFunc(GL_DST_COLOR, GL_ZERO);
				theApp->cardMan.currentlyTrackedImages[pictureId].colorImagesPlanes[j].draw(0,-2, 60, 83);
				glPopMatrix();
			}
			ofSetRectMode(OF_RECTMODE_CORNER);
		}
		glPopMatrix();
		ofDisableAlphaBlending();
		ofEnableAlphaBlending();
		
		fakeMarker.unloadARMatrix();
	}
	
}

//------------------------------------
void singleBouncer::drawNumber(){

	/*return;
	//draw the number
	if( fakeMarker.isMarkerSet() ){
		fakeMarker.loadARMatrix();
			glPushMatrix();
				glTranslatef(0, 0, levitationAmnt * 110);
				ofSetColor(200, 244, 10, 220);
				Akzidenz.drawString( ofToString(playingNumber), -Akzidenz.stringWidth(ofToString(playingNumber))/2, 10);
			glPopMatrix();
		fakeMarker.unloadARMatrix();
	}		*/
}

//------------------------------------
void singleBouncer::drawTeleportOutShadow(){

}

//------------------------------------
void singleBouncer::drawTeleportOut(){
	//draw the teleport
	if( fakeMarker.isMarkerSet() ){
		fakeMarker.loadARMatrix();
			glPushMatrix();
			
				
				ofNoFill();
				for(int i = 0; i < 10; i++){
					int flash = ofRandom(0, 80);
					ofSetColor(170 +  flash, 170 + flash, 0, 255);
					float scale = (float)i/10.0;
					ofCircle(0, 0, scale * 10 + levitationAmnt * 20.0);
				}
				ofFill();
				
												
			glPopMatrix();
		fakeMarker.unloadARMatrix();
	}		
}

//------------------------------------
void singleBouncer::drawTeleportInShadow(){

}

//------------------------------------
void singleBouncer::drawTeleportIn(){
	//draw the teleport in - this happens on the ace
	
	if( shape.pct < 0.75 )return;
	
	if( telePortMarker.isMarkerSet() ){
		telePortMarker.loadARMatrix(drawWidth, drawHeight);
			glPushMatrix();
			
				ofNoFill();
				for(int i = 0; i < 10; i++){
					int flash = ofRandom(0, 80);
					ofSetColor(170 +  flash, 170 + flash, 0, 255);
					float scale = (float)i/10.0;
					ofCircle(0, 0, ( scale * 14 + shape.sinPct * 20) );
				}
				ofFill();
				
			glPopMatrix();
		telePortMarker.unloadARMatrix();
	}		
}

//------------------------------------
void singleBouncer::drawReflectInShadow(){

}

//------------------------------------
void singleBouncer::drawReflectIn(){
	//draw the reflect animation
	//if( shape.pct < 0.75 )return;
	
	if( reflectMarker.isMarkerSet() ){
		reflectMarker.loadARMatrix();
			glPushMatrix();
				ofEnableAlphaBlending();
				
				ofFill();			
					if( cardMode == 1 ) ofSetColor(255, 0, 0, 90);
					else if( cardMode == 2 ) ofSetColor(0, 0, 0, 90);
					else  ofSetColor(0, 0, 255, 90);
					ofRect(-10,30,20,20);
					ofRect(-10,-60,20,20);
					//glRotatef(180, 1, 0, 0);
					//ofCircle(0, 20, 20);
				ofFill();			
			glPopMatrix();
		reflectMarker.unloadARMatrix();
	}		
}


//------------------------------------
void singleBouncer::drawReflectOutShadow(){

}

//------------------------------------
void singleBouncer::drawReflectOut(){
	//draw the reflect animation
	
	if( fakeMarker.isMarkerSet() ){
		fakeMarker.loadARMatrix();
			glPushMatrix();
				
				ofNoFill();
					if( cardMode == 1 ) ofSetColor(255, 0, 255, 255);
					else if( cardMode == 2 ) ofSetColor(50, 0, 50, 255);
					else  ofSetColor(0, 0, 255, 255);
					ofRect(-22.5,-15, 55, 30);
				ofFill();
				
			glPopMatrix();
		fakeMarker.unloadARMatrix();
	}		
}



