#include "Demo.h"
#include "App.h"
#include <fstream>

Demo::Demo(App* _app, GUIPackage *gpack) : DApplet(_app), app(_app) {
    logo = Texture::fromTwoFiles("dojo-color.png", "dojo-alpha.png", TextureFormat::RGBA8, Texture::CLAMP);
	gameUI = new GameUI(app);

	//get a handle to the UI display to show the score
	scoreHandle = gameUI->addText(app->debugFont,12,Vector2(10,10),"Pull point");
	hitHandle = gameUI->addText(app->debugFont, 12, Vector2(10,25),"Hit object");
	hitObject = -1;

	//reset the lives and score
	dead = false;
	win = false;
	left_foot = true;
	setGameLives(3);
	setGameScore(0);

	guiPackage = gpack;
	playerId = 0;
}


void Demo::onInit()  {
    DApplet::onInit();

    // Called before Demo::run() beings
    app->setApplet(this);

	app->debugCamera.setPosition(Vector3(0, 1.5, 0));
	app->debugCamera.lookAt(player->frame().translation);
    app->debugController.setActive(false);
	
	luigi = static_cast<Marionette*> (&(*enemy));
	mario = static_cast<Marionette*> (&(*player));
	mario->initPullPoints(this->spawnPosition);

	loadPlayers();
	
	Array<char* > moveNames = mario->loadMoves(0);
	luigi->loadMoves(0);

	mario->setOpponent(luigi);
	luigi->setOpponent(mario);

	for(int i = 0; i < moveNames.size(); i++)
	{
		wxString temp(moveNames[i]);
		guiPackage->movesBox->Insert(temp, i);
	}

	strings = mario->getStrings();

	markers.clear();
	relativeCenter.clear();
	for(int i = 0; i < strings.size(); i++)
	{
		char name[256];
		sprintf(name, "Marker%d", i);
		
		markers.append(Ball::create(name));
		World::world()->insert(markers.last(), CoordinateFrame());
		
		selected.append(false);

		relativeCenter.append(Vector3(strings[i]->frame().translation));
	}


	Vector3 epos = Vector3(0, 0, 1);
	luigi->initPullPoints(this->spawnPosition - epos);
	
	// Make players look at each other
	G3D::CoordinateFrame f = mario->frame();
	f.lookAt(enemy->frame().translation);
	mario->turnTo(f);
	f = enemy->frame();
	f.lookAt(player->frame().translation);
	luigi->turnTo(f);

	currentPullPoint = mario->nextPullPoint();
	guiPackage->jointList->SetSelection(0);
	selected[0] = true;
	
	pureDeltaMouse = false;
}

void Demo::reload(void)
{
	hitObject = -1;
	mario->initPullPoints(this->spawnPosition);
	
	Array<char* > moveNames = mario->loadMoves(playerId);
	luigi->loadMoves(playerId);

	guiPackage->movesBox->Clear();
	for(int i = 0; i < moveNames.size(); i++)
	{
		wxString temp(moveNames[i]);
		guiPackage->movesBox->Insert(temp, i);
	}

	Vector3 epos = Vector3(0, 0, 1);
	luigi->initPullPoints(this->spawnPosition - epos);
	
	// Make players look at each other
	G3D::CoordinateFrame f = mario->frame();
	f.lookAt(enemy->frame().translation);
	mario->turnTo(f);
	f = enemy->frame();
	f.lookAt(player->frame().translation);
	luigi->turnTo(f);
	for (int i = 0; i < selected.size() ; i++) {
		selected[i] = false;
	}

	mario->setOpponent(luigi);

	selected[0] = true;
}

void Demo::savePlayers()
{
	std::ofstream playerFile("conf\\players.tomo");
	playerFile << playerMap.size() << std::endl;

	p_iter it = playerMap.begin();
	p_iter end = playerMap.end();
	while(it != end) {
		playerFile << (*it).first << " = " << (*it).second << std::endl;
		++it;
	}

	mario->saveMoves(playerId);
}

void Demo::loadPlayers()
{
	std::ifstream playerFile("conf\\players.tomo");

	playerMap.clear();

	playerFile >> playerCount;

	for (int i = 0; i < playerCount; i++) {
		int id;
		std::string dummy;
		std::string name;

		playerFile >> id >> dummy >> name;
		playerMap[id] = name;

		guiPackage->playerList->Insert(wxString(name.c_str()), id);
	}
	guiPackage->playerList->SetSelection(0);
	playerId = 0;
}

void Demo::selectPlayer(int id)
{
	playerId = id;
	this->reload();
}

void Demo::newPlayer()
{
	playerId = playerMap.size();
	std::string name(guiPackage->playerName->GetValue().c_str());
	playerMap[playerId] = name;
	guiPackage->playerList->Insert(guiPackage->playerName->GetValue(), playerId);

	mario->clearMoves();

	Configuration *c = new Configuration(strings, mario);
	Move *m = new Move("Stand", 0);
	m->addConfiguration(c);
	mario->addMove(m);
	
	Configuration *forward = new Configuration(mario);
	Array<Vector3> poses;
	for (int i = 0; i < strings.size(); i++) {
		Vector3 fuck = strings[i]->frame().translation;
		Vector3 v = strings[i]->frame().translation - Vector3(0, 0, .3);
		poses.append(mario->frame().pointToObjectSpace(v));
	}
	forward->setPositions(poses);
	m = new Move("Forward", 1);
	m->addConfiguration(forward);
	mario->addMove(m);
	
	Array<Vector3>poses2;
	Configuration *back = new Configuration(mario);
	for (int i = 0; i < strings.size(); i++) {
		poses2.append(mario->frame().pointToObjectSpace(strings[i]->frame().translation + Vector3(0, 0, .3)));
	}
	back->setPositions(poses2);
	m = new Move("Back", 2);
	m->addConfiguration(back);
	mario->addMove(m);

	Array<Vector3>poses3;
	Configuration *left = new Configuration(mario);
	for (int i = 0; i < strings.size(); i++) {
		poses3.append(mario->frame().pointToObjectSpace(strings[i]->frame().translation - Vector3(.3, 0, 0)));
	}
	left->setPositions(poses3);
	m = new Move("Left", 3);
	m->addConfiguration(left);
	mario->addMove(m);

	Array<Vector3>poses4;
	Configuration *right = new Configuration(mario);
	for (int i = 0; i < strings.size(); i++) {
		poses4.append(mario->frame().pointToObjectSpace(strings[i]->frame().translation + Vector3(.3, 0, 0)));
	}
	right->setPositions(poses4);
	m = new Move("Right", 4);
	m->addConfiguration(right);
	mario->addMove(m);

	this->savePlayers();

	this->reload();
}

void Demo::onLogic(){

}

void Demo::onCleanup() {
    // Called when Demo::run() exits
	delete gameUI;
}


void Demo::onNetwork() {
	// Poll net messages here
}


void Demo::onSimulation(RealTime rdt, SimTime sdt, SimTime idt) {
    DApplet::onSimulation(rdt, sdt, idt);

	Vector2 mouseXY;
	//pureDeltaMouse is a bool signifying if the camera is in mouse look mode
	if(pureDeltaMouse == false){
		mouseXY = Vector2::ZERO;
	}
	else{
		mouseXY = app->userInput->mouseDXY();
	}


	Vector3 pos;
	if(mouseXY == Vector2::ZERO){
		//if there is no change in the mouse position, place the camera behind the ball
		CoordinateFrame orig = app->debugCamera.getCoordinateFrame();
		orig.translation += (player->frame().translation - previousPlayerPosition);
		pos = orig.translation;
	}
	else{
		//if there is a change ion mouse position, rotate the camera accordingly
		double c = -.02;

		Vector3 up = Vector3::UNIT_Y;

		//create a roation matrix about the Y-axis based on horizontal mouse movement
		Matrix3 lrMat = Matrix3::fromAxisAngle(up, c*mouseXY.x);
		CoordinateFrame orig = app->debugCamera.getCoordinateFrame();

		orig.translation += (player->frame().translation - previousPlayerPosition);

		//update the positon of the camera based on the rotation matrix
		pos = (lrMat*(orig.translation-player->frame().translation)).unit()*3 + player->frame().translation;
		
		//create a rotation matrix about the X-axis of the ball based on vertical mouse movement
		Vector3 right = cross(up,(orig.translation-player->frame().translation).unit());
		
		Matrix3 udMat = Matrix3::fromAxisAngle(right, .25*c*mouseXY.y);
		
		//update the positon of the camera based on the rotation matrix
		pos = (udMat*(pos-player->frame().translation)).unit()*3 + player->frame().translation;

	}

	//Hack
	for(int i = 0; i < markers.size(); i++)
	{
		if(selected[i])
		{
			markers[i]->setFrame(strings[i]->frame());
		}
		else
		{
			markers[i]->setFrame(CoordinateFrame(Vector3(0, 5000, 0)));
		}
	}


	// Track the player with the camera
	if (true == pureDeltaMouse) {
		app->debugCamera.setPosition(pos);
	}
	app->debugCamera.lookAt(player->frame().translation);
	previousPlayerPosition = player->frame().translation;
}


void Demo::onUserInput(UserInput* ui) {
    DApplet::onUserInput(ui);

		//Hold down the RIGHT mouse button to manipulate the camera
	if(ui->keyPressed(SDL_RIGHT_MOUSE_KEY))
	{
		//guiPackage->g3dPanel->SetFocus();
		pureDeltaMouse = true;
	}

	if(ui->keyReleased(SDL_RIGHT_MOUSE_KEY))
	{
		pureDeltaMouse = false;
		//pureDeltaMouse = !pureDeltaMouse;
	}

	if(ui->keyPressed(SDL_LEFT_MOUSE_KEY))
	{
		relativeCenter.clear();
		for(int i = 0; i < strings.size(); i++)
		{
			relativeCenter.append(Vector3(strings[i]->frame().translation));
		}

		Vector2 mouseXY;
		mouseXY = app->userInput->mouseXY();

		Ray myRay = app->debugCamera.worldRay(mouseXY.x, mouseXY.y, Rect2D(Vector2(800, 600)));
		
		if (!app->userInput->keyDown(SDLK_q)) {
			
			for(int i = 0; i < selected.size(); i++)
			{
				selected[i] = false;
			}
		}

		hitObject = -1;
	
		for(int i = 0; i < strings.size(); i++)
		{
			Vector3 translate = strings[i]->frame().translation;
			Sphere tempSphere(translate, .1f);

			
			if(myRay.intersectionTime(tempSphere) < G3D::inf())
			{
				hitObject = i;

				currentPullPoint = strings[i];
				mario->setPullPoint(i);
				guiPackage->jointList->SetSelection(i);
				guiPackage->xaxis->SetValue(50);
				guiPackage->yaxis->SetValue(50);
				guiPackage->zaxis->SetValue(50);

				//Allow toggling of selection
				selected[i] = !selected[i];
			}
		
		}
	}
	//app->debugCamera.

	//pureDeltaMouse is a bool signifying if the camera is in mouse look mode
	app->userInput->setPureDeltaMouse(pureDeltaMouse);
	app->debugTabSwitchCamera = false;

	if(app->userInput->keyPressed(SDLK_s))
	{
		mario->saveMoves(0);
	}
}


void Demo::onGraphics(RenderDevice* rd) {

    World::world()->activeCamera = &app->debugCamera;
    rd->setProjectionAndCameraMatrix(*World::world()->activeCamera);
    
    World::world()->onGraphics(rd);

	/*
		for(int i=0; i < strings.size(); i++)
		{
			Vector3 trans = strings[i]->frame().translation;
			Draw::sphere(Sphere(trans, .1), rd, Color4(1, 1, 1, 1));
		}
*/   
   // Draw the Dojo logo

    rd->push2D();
        app->renderDevice->setAlphaTest(RenderDevice::ALPHA_GREATER, 0.05);
        app->renderDevice->setBlendFunc(RenderDevice::BLEND_SRC_ALPHA, RenderDevice::BLEND_ONE_MINUS_SRC_ALPHA);
        app->renderDevice->setTexture(0, logo);
		Draw::rect2D(Rect2D::xywh(735, 475, 60, 120), app->renderDevice);
		
		//draw the 2D UI graphics.  Make sure this is within a 2D push-pop section.
		
		gameUI->setText(scoreHandle, G3D::format("Selected Limb Mass = %f Score = %i", currentPullPoint->limb->mass() * 100000, gameScore));
		
		gameUI->setText(hitHandle, G3D::format("Hit: %d", hitObject));

		gameUI->doUIGraphics(rd);
    rd->pop2D();

    DApplet::onGraphics(rd);

}

void Demo::playerDeath() {
	this->gameLives--;
	if (gameLives < 1 && !win) {
		World::world()->remove("Player");
		dead = true;
		deathHandle = gameUI->addText(app->debugFont,48,Vector2(200, 250), "You're DEAD...");
	}
	else {
		player->setLinearVelocity("Pelvis", Vector3::ZERO);
		player->setAngularVelocity("Pelvis", Vector3::ZERO);
		player->setFrame("Pelvis", CoordinateFrame(Matrix3::IDENTITY, this->spawnPosition));
		//hitTarget->setFrame("Root", player->frame());
	}
}

void Demo::endGameWin() {
	UIHandle winHandle = gameUI->addText(app->debugFont, 48, Vector2(250, 250), "You WIN!");
	win = true;
}

void Demo::setMove(int selection)
{
	int numPoses = mario->selectMove(selection)->numPoses();

	guiPackage->posesBox->Clear();

	for(int i = 0; i < numPoses; i++)
	{
		char currNum[2];
		sprintf(currNum, "%d", i+1);
		guiPackage->posesBox->Insert(wxString(currNum), i);
	}
}

void Demo::selectPose(int selection)
{
	mario->selectConfiguration(selection);
}

void Demo::selectJoint(int sel)
{
	hitObject = sel;
	currentPullPoint = strings[sel];
	mario->setPullPoint(sel);
}

void Demo::addMove(char* name)
{
	wxString temp(name);
	guiPackage->movesBox->Insert(temp, guiPackage->movesBox->GetCount());
	guiPackage->movesBox->SetSelection(guiPackage->movesBox->GetCount()-1);

	mario->addMove(name);
}

void Demo::addPose()
{
	int numPoses = mario->addConfiguration();

	guiPackage->posesBox->Clear();

	for(int i = 0; i < numPoses; i++)
	{
		char currNum[2];
		sprintf(currNum, "%d", i+1);
		guiPackage->posesBox->Insert(wxString(currNum), i);
	}

	guiPackage->posesBox->SetSelection(guiPackage->posesBox->GetCount()-1);
}

void Demo::setPose()
{
	mario->setConfiguration();
}

void Demo::deleteMove(int sel)
{
	hitObject = sel;
	mario->deleteMove(sel);

	guiPackage->posesBox->Clear();
}

void Demo::deletePose()
{
	int numPoses = mario->deleteConfiguration();

	guiPackage->posesBox->Clear();

	for(int i = 0; i < numPoses; i++)
	{
		char currNum[2];
		sprintf(currNum, "%d", i+1);
		guiPackage->posesBox->Insert(wxString(currNum), i);
	}
}

void Demo::playMove()
{
	G3D::CoordinateFrame f = mario->frame();
	f.lookAt(enemy->frame().translation);
	mario->setFrame(f);
	mario->playMove();
}

void Demo::setAttackLimb(int selection)
{
	mario->setAttackLimb(strings[selection]);
}

void Demo::setTarget(std::string name)
{
	mario->setMoveTarget(luigi->part(luigi->name() + name));
}