#include "Salamander.h"
#include "Character.h"

#include "Body.h"
#include "ButtonEvent.h"
#include "ButtonDownEvent.h"
#include "ButtonUpEvent.h"
#include "Polygon.h"
#include "SDLHack.h"
#include "World.h"

Character::Character() : Body(),
	lkey_(false),
	rkey_(false),
	bodysize_(15.0, 25.0),
	maxspeed_(500.0),
	autopercent_(0.6),
	touchingland_(false),
	animationframe_(0.0),
	currentframe_(0),
	runvelocity_(100.0),
	autoaccelerate_(false)
{
	generate();

	events().receive<Character, ButtonEvent>(this, &Character::onButtonEvent);
}

Character::~Character()
{
	
}

void Character::frame()
{
	// update the touchingland member
	updateTouchingLand();

	// move!
	movement();

	// animate!
	animate();

	// camera
	SDLHack sdlhack; sdlhack.setCamera(getTranslation());
}

void Character::movement()
{
	// sideways
	double direction = 0.0;
	if(lkey_ && !rkey_) direction = -1.0;
	if(rkey_ && !lkey_) direction =  1.0;

	//auto accelerate to 60% of max speed
	if(velocity_.x_ < maxspeed_ * autopercent_ && autoaccelerate_)
		applyForce(Vec2(50.0, 0.0));

	// user control for finer navigation
	applyForce(Vec2(direction * 100.0, 0.0));

	// clamp speed
	if(velocity_.x_ > maxspeed_) velocity_.x_ = maxspeed_;
	if(velocity_.x_ < 0.0) velocity_.x_ = 0.0;

	// fake gravity
	if(!touchingland_)
		applyForce(Vec2(0.0, 60.0));

	// start accelerating
	if(rkey_) autoaccelerate_ = true;

	// end of level
	int levels = 21;
	if(getTranslation().x_ > 500.0 * levels - 200.0)
	{
		autoaccelerate_ = false;
		setTranslation(Vec2(500.0 * levels - 200.0, getTranslation().y_));
		velocity_.x_ = 0.0;
	}

}

void Character::jump()
{
	if(touchingland_)
	{
		// jump
		force_.y_ = 0.0;
		velocity_.y_ = -300.0;
		biasedVelocity_.y_ = 0.0;

		// keep the player moving sideways, too
		if(velocity_.x_ < maxspeed_ * autopercent_) 
			velocity_.x_ = maxspeed_ * autopercent_;
	}
}

void Character::updateTouchingLand()
{
	double xout = 10.0;
	double yout = 20.0;
	touchingland_ = world_->pickBody(getTranslation() + Vec2(-bodysize_.x_ * 0.5, bodysize_.y_ * 0.5) + Vec2(-xout, yout)).body == NULL &&
		   world_->pickBody(getTranslation() + Vec2( bodysize_.x_ * 0.5, bodysize_.y_ * 0.5) + Vec2( xout, yout)).body == NULL ? 
		false : true;
}

void Character::animate()
{
	// move things along
	animationframe_ -= application().delta();

	// only update after a certain time
	if(animationframe_ > 0.0)
		return;

	// default frame time
	animationframe_ = 0.1;

	// if we're touching something (or moving upwards)
	if(touchingland_)
	{
		// then we are landing
		if(currentframe_ == FALL1 || currentframe_ == FALL2)
		{
			currentframe_ = LAND;
		}
		// or running
		else if(velocity_.x_ > runvelocity_)
		{
			currentframe_++;
			if(currentframe_ > RUN3)
				currentframe_ = RUN1;
		}
		// or standing still!
		else
		{
			currentframe_ = STAND;
		}
	}
	// if we're not touching anything...
	else
	{
		// then we're jumping
		if(velocity_.y_ < 0.0)
		{
			currentframe_ = JUMP;
		}
		// or we're falling
		else
		{
			animationframe_ = 0.3;
			if(currentframe_ == FALL1)
				currentframe_ = FALL2;
			else
				currentframe_ = FALL1;
		}
	}

	// set the image
	sprite_->setTexture(textures_[currentframe_]);
	
}

void Character::generate()
{
	// The physics bounds
	Polygon* polygon = new Polygon();
	polygon->addVertex(-bodysize_ * 0.5);
	polygon->addVertex(Vec2(bodysize_.x_ * 0.5, -bodysize_.y_ * 0.5));
	polygon->addVertex(bodysize_ * 0.5);
	polygon->addVertex(Vec2(-bodysize_.x_ * 0.5, bodysize_.y_ * 0.5));

	setShape(polygon);
	setMass(0.1);
	setFriction(0.0);

	// The sprite
	sprite_ = new Sprite();
	
	Stream& vcd = *(new Stream(StreamCoordinates::TypeId));
	vcd.add(*(new StreamCoordinates(0.0f, 0.0f)));
	vcd.add(*(new StreamCoordinates(0.0f, 1.0f)));
	vcd.add(*(new StreamCoordinates(1.0f, 0.0f)));
	vcd.add(*(new StreamCoordinates(1.0f, 1.0f)));
	sprite_->addStream(vcd);

	sprite_->setReferenceFrame(Entity::WORLD);
	sprite_->setColour(Colour(1.0, 1.0, 1.0, 1.0));
	sprite_->setScale(Vec2(64.0, 64.0));
	scene().addChild(*sprite_);

	// Some textures!
	textures_.push_back(asset().load<Texture>("..\\data\\frame_stand1.png"));
	textures_.push_back(asset().load<Texture>("..\\data\\frame_run1.png"));
	textures_.push_back(asset().load<Texture>("..\\data\\frame_run2.png"));
	textures_.push_back(asset().load<Texture>("..\\data\\frame_run3.png"));
	textures_.push_back(asset().load<Texture>("..\\data\\frame_fall1.png"));
	textures_.push_back(asset().load<Texture>("..\\data\\frame_fall2.png"));
	textures_.push_back(asset().load<Texture>("..\\data\\frame_land1.png"));
	
	sprite_->setTexture(textures_[0]);

}

void Character::propogate()
{
	// First update the base class properties.
	Body::propogate();

	// Then update our geometry transform.
	sprite_->setTranslation(Body::getTranslation());
	sprite_->setRotation(Body::getRotation());
}

void Character::updateMatrix()
{
	// first we hitch a ride on this with the frame
	frame();

	// keep the character upright
	setRotation(0.0);

	// then update
	Body::updateMatrix();
}

void Character::onButtonEvent(const ButtonEvent& event)
{
	// down?
	bool down = event.typeId() == ButtonDownEvent::TypeId ? true : false;

	// left and right
	if(event.getCode() == ButtonEvent::keyCode::Left)
		lkey_ = down;
	if(event.getCode() == ButtonEvent::keyCode::Right)
		rkey_ = down;

	// jump!
	if(down && event.getCode() == ButtonEvent::keyCode::Up)
		jump();
}