/*
 * trajectory.js
 * Builds on the example in arrow.js. This example shows how to
 * calculate and draw the trajectory path of the arrow.
 */
  
var tmp = null;

// Reference to canvas context
var ctx = false;

// Update frequency
var fHz = 1000/60;

// Canvas size
var canvas_size = {x: 10, y: 10};

// List of on-going explosions
var entities = [];

// Reference to the interval, used to
// clear it at reset.
var interval = null;

// Box2D World
var world = null;

// Pixel-to-Meter ratio
var PTM_RATIO = 30.0;

var initialised = false;


var move = false;
var v0x = null;
var v0y = null;
var golfPlayer = null;
var end_simulation = false;
var show_response = false;
var tryNumber = 0;
var pointMarker=0;

var swingSoung=document.getElementById('swing-sound');
var dropBall=document.getElementById('drop-ball');
var winSound =document.getElementById('win');
var loseSound =document.getElementById('game-over');

/*
 * ArrowClass
 * Class to create and manage arrows.
 */
ArrowClass = Class.extend({
	// Reference to the physics body
    physBody: null,
    
    // How long to show the arrow once it has hit a surface (ms)
    lifetime: 300,
        
    // Max amount of time the arrow can be in flight (ms)
    max_existence_time: 10000,
    
    // Flag indicating if the arrow should be destroyed
    _killed: false,
	
	//--------------------------------------
    init: function (pos, angle, force) {
		var bodyDef = new b2BodyDef();
		
		bodyDef.position.Set(p2b(pos.x)
						   , p2b(pos.y));
						   
		bodyDef.type = b2Body.b2_dynamicBody;
		
		bodyDef.userData = {
							ent: this,
							description: 'arrow'
					 	   };
		
		// Define the vertices of the arrow
		var vertices = [];
		vertices.push(new b2Vec2(0,0));
		vertices.push(new b2Vec2(0.8,-0.06));
		vertices.push(new b2Vec2(1.1,0));
		vertices.push(new b2Vec2(0.8,0.06));
		
		var polygonShape = new b2PolygonShape();
		polygonShape.SetAsArray(vertices);
	
		var fixtureDef = new b2FixtureDef();
		fixtureDef.shape = polygonShape;
		fixtureDef.density = 2;
		fixtureDef.friction = 0.5;
		fixtureDef.restitution = 0.1;
		// arrows should not collide with each other
		fixtureDef.filter.groupIndex = -1;	
	
		var body = world.CreateBody(bodyDef);
	
		body.CreateFixture(fixtureDef);
		body.SetLinearVelocity(new b2Vec2(force * Math.cos(angle),
										force * Math.sin(angle))
							   );
		body.SetAngle(angle);
		body.SetAngularDamping(1.5);
	
		this.physBody =  body;
		
		return this;
    },
    
    //--------------------------------------
    update: function () {
    	// Don't do anything if dead
    	if ( this._killed ) return;
    	
    	// Decrease the existence timer
    	this.max_existence_time -= fHz;
    	if( this.max_existence_time <= 0 ) {
        	this.kill();
        	return;
        }
        
        // Check if the arrow has hit the ground and if the body
        // still active. The body is set to inactive if it has
        // hit the ground or is no longer moving.
        if ( this.physBody.IsActive() ) {
			var body = this.physBody;
			
			// Calculate the flight speed based on the linear velocity
			// of the body.
			var flightSpeed = Normalize2(body.GetLinearVelocity());
			
			// Adjust the arrow to point in the flight direction.
			if ( flightSpeed > 2 ) {  
				var bodyAngle = body.GetAngle();
				var flyingAngle = Math.atan2( body.GetLinearVelocity().y,
											  body.GetLinearVelocity().x );
				body.SetAngle(flyingAngle);
			}
		}
		else {
			// This section controls how long the
			// arrow is visible when it has stuck
			// to a static body, e.g. the ground.
			this.lifetime -= fHz;
        	if( this.lifetime <= 0 ) {
            	this.kill();
            	return;
       	 	}
		}
    },
    
    //--------------------------------------    
    onTouch: function(body, point, impulse){
    	tmp = body;
    	
    	// The arrow has hit a static object, e.g. the ground
    	// or a platform
    	if ( body.GetType() === b2Body.b2_staticBody ) {
    		this.physBody.SetActive(false);
    		return;
    	}
    	
    	// The arrow has hit a dynamic object, e.g. a
    	// block
    	if ( body.GetType() === b2Body.b2_dynamicBody ) {
    		this.physBody.SetActive(false);
    		this.kill();
    		return;
    	} 
    },
    
    //--------------------------------------
    kill: function() {
    	this._killed = true;
    }
});


/*
 * PlayerClass
 * Class to create and manage players.
 */
PlayerClass = Class.extend({
	// Reference to the physics body
    physBody: null,
    
	
	//--------------------------------------
    init: function (pos) {
		var bodyDef = new b2BodyDef();

		
		bodyDef.position.Set(p2b(pos.x), p2b(pos.y));
						   
		bodyDef.type = b2Body.b2_staticBody;

		player = sprite({
			width: 333,
			height: 79,
			image: document.getElementById('playerSprite'),
			numberOfFrames: 3,
			ticksPerFrame: 1,
			tickCount: 3,
			frameIndex: 0,
			stop: false
		});
	
		bodyDef.userData = player;
		
		var polygonShape = new b2PolygonShape();
		polygonShape.SetAsBox((111/2)/PTM_RATIO,(79/2)/PTM_RATIO);	
		var fixtureDef = new b2FixtureDef;
		fixtureDef.shape = polygonShape;
		fixtureDef.density = 1;
		fixtureDef.friction = 0.5;
		fixtureDef.restitution = 0.1;

	
		var body = world.CreateBody(bodyDef);
	
		body.CreateFixture(fixtureDef);

		
		this.physBody =  body;
		
		return this;
    },
    
    //--------------------------------------
    update: function () {
    	var body = this.physBody;
    	spritePlayer = body.GetUserData();
 
        spritePlayer.tickCount += 1;

        if (spritePlayer.tickCount > spritePlayer.ticksPerFrame) {

						spritePlayer.tickCount = 0;
			
            // If the current frame index is in range
            if (spritePlayer.frameIndex < spritePlayer.numberOfFrames && move && !spritePlayer.stop) {	
                // Go to the next frame
                spritePlayer.frameIndex += 1;
                console.log(swingSoung);
                swingSoung.play();
            } else if (spritePlayer.stop || spritePlayer.frameIndex >= spritePlayer.numberOfFrames){
            	spritePlayer.stop = true;
            	spritePlayer.frameIndex = 0;
            }
        }

   
				ctx.save();
        ctx.translate( body.GetPosition().x*PTM_RATIO, body.GetPosition().y*PTM_RATIO);
        ctx.rotate(body.GetAngle());
				ctx.drawImage(spritePlayer.image,spritePlayer.frameIndex*111,0,111,79,-66,-40,111,79);        
        ctx.restore();
    },
    
 
});



/*
 * createMap
 * Draws the static and dynamic bodies used.
 */
function createMap(){
	// Wall thickness
	var wt = 10;
	// Platform thickness
	var pt = 7;
	// Platform width
	var pw = 40;

	// Left wall
	tmp = createBox(p2b(wt), 
			  p2b(canvas_size.y/2), 
			  p2b(wt), 
			  p2b(canvas_size.y/2), 
			  b2Body.b2_staticBody);
	tmp.SetUserData("Left wall");
			  
	// Right wall
	tmp = createBox(p2b(wt), 
			  p2b(canvas_size.y/2), 
			  p2b(canvas_size.x - wt), 
			  p2b(canvas_size.y/2), 
			  b2Body.b2_staticBody);
	tmp.SetUserData("Right wall");
			  
	// Ground
	tmp = createBox(p2b(canvas_size.x / 2), 
			  p2b(wt), 
			  p2b(canvas_size.x / 2), 
			  p2b(canvas_size.y - wt), 
			  b2Body.b2_staticBody);
	tmp.SetUserData("Ground");
	
	// Ceiling
	tmp = createBox(p2b(canvas_size.x / 2), 
			  p2b(wt), 
			  p2b(canvas_size.x / 2), 
			  p2b(wt), 
			  b2Body.b2_staticBody);
			  
	tmp.SetUserData("Ceiling");
			  
	// Platform on right + stack
	tmp = createBox(p2b(pw), 
			  p2b(pt), 
			  p2b(535), 
			  p2b(373), 
			  b2Body.b2_staticBody);
	tmp.SetUserData("Right platform");

}

/*
 * getTrajectoryPoint
 * This functions returns the coordinates of step n 
 * in a trajectory based on the starting position and
 * starting velocity.
 */
function getTrajectoryPoint( startingPosition, startingVelocity, n ){
	var sP = new b2Vec2(startingPosition.x, startingPosition.y);
	var sV = new b2Vec2(startingVelocity.x, startingVelocity.y);
	var t = 1 / 30.0;  // seconds per time step (at 60 fps)


	sV.Multiply(t); // m/s
	sV.Multiply(n);

	var gravity = new b2Vec2(world.GetGravity().x, world.GetGravity().y);
	
	gravity.Multiply(t*t); // m/s/s
	
	gravity.Multiply((Math.pow(n,2) + n) * 0.5);
	
	sV.Add(gravity);
	
	sP.Add(sV);
	
	// Return the coordinates of the trajectory point.
	return new b2Vec2(sP.x, sP.y);
}

/*
 * drawTrajectoryPath
 * Draw the trajectory path on the canvas based
 * on the inputs: starting position, angle, and force.
 */
 
function drawTrajectoryPath( startPosition, angle, force ) {
	
	// Create a vector that represents the starting position
	// in meters instead of pixels.
	var startPositionMeters = startPosition.Copy();
	startPositionMeters.Multiply(1/PTM_RATIO);
	
	// Vector value of velocity based on force and
	// trajectory angle.
	var startingVelocity = new b2Vec2(v0x,v0y);
	  
	// Starting position when calculating the trajectory
	// position at a given step
	var trajectoryStartingPos = new b2Vec2(0,0);

	// Determines the number of trajectory points to calculate.
	var numTrajectoryPoints = 180;

	// Set the canvas context drawing parameters
	// for the trajectory markers. 
	ctx.save();
	ctx.fillStyle = 'green';
	ctx.lineWidth = 0.1;
	ctx.strokeStyle = '#003300';

	// Size of the trajectory marker
	var radius = 2;

	// Initialise the starting position of the ray.
	// This value will be set to the value of the end
	// of the ray at the end of every loop.
	var startOfRay = startPositionMeters.Copy();

	// Flag indicating whether to draw the trajectory.
	// Set to false when the raycast hits a physics body
	// to prevent the trajectory from passing through
	// objects.
	var show_trajectory = false;

	// Loop to determine the trajectory points
	for ( var i = 0; i < numTrajectoryPoints; i++) {
	
		// Stop drawing trajectory markers.
		if ( !show_trajectory ){
			//end_simulation = true;
			break;
		};

		// Current trajectory coordinates for step i.
		var cur_pos = getTrajectoryPoint(startPositionMeters,
										 startingVelocity, 
										 i);
		// Calculate the coordinates of the trajectory marker
		// on the canvas.
		var trajectoryMarkerPos = cur_pos.Copy();
		trajectoryMarkerPos.Multiply(PTM_RATIO);

		// The end of the ray is the current position in the
		// trajectory. The ray will therefore be the line 
		// from the previous trajectory position to the current
		// trajectory position.
		var endOfRay = cur_pos.Copy();

		// Cast the ray to see if it hits some physics body. If it
		// does then set show_trajectory to false and change the fill
		// style to indicate the expected collision point.
		world.RayCast(function(raycast){
							   var b = raycast.GetBody();
							   var stop = false;
							   if(b){
								  ctx.fillStyle = 'red';
								  radius = 2;
								  show_trajectory = false;
							   }
						   }, startOfRay, endOfRay);


		// Draw the trajectory marker
		// ctx.beginPath();
		// ctx.arc(trajectoryMarkerPos.x, 
		// 	    trajectoryMarkerPos.y, 
		// 	    radius, 
		// 	    0, 
		// 	    2 * Math.PI, 
		// 	    false);
		// ctx.fill();
		// ctx.stroke();

		startOfRay = endOfRay;
	}
	ctx.restore();
}

/*
 * preventDefault
 * Used to prevent the default behavior of
 * double clicks and select start events.
 */
function preventDefault(event){
	if (event.preventDefault)
		event.preventDefault();
	else
		event.returnValue= false;
	return false;
}

/*
 * reset
 * Resets the demo.
 */
function reset() {
	clean_try();
	move = false;
	v0x =  null;
	v0y = null;
	world.DestroyBody(golfPlayer.physBody);
	end_simulation = false;
	show_response = false;
	golfPlayer = null;
	ctx.clearRect(0, 0, canvas_size.x, canvas_size.y);
	world = null;
	entities = [];
	deferred_kills = [];
	window.clearInterval(interval);
	interval = null;
	initialised = false;
	pointMarker=0;
	document.getElementById('v0x').value = "";
	document.getElementById('v0y').value = "";
	document.getElementById('ganaste').style.display = 'none';
	document.getElementById('perdiste').style.display = 'none';  
	init();
}


/*
 * removeEntity
 * Remove an entity object from the entity list.
 */
function removeEntity(entity) {
	world.DestroyBody(entity.physBody);
	return entities.erase(entity);
}

/*
 * update
 * The main update function
 */
function update(){

	var deferred_kills = [];
	// Update the world simulation
	world.Step(
		  1/60,      //frame-rate
		  10,       //velocity iterations
		  10       //position iterations
	);

	// First update all entities
	for ( var i = 0; i < entities.length; i++ ) {
		entities[i].update();
		
		if(entities[i]._killed)
			deferred_kills.push(entities[i]);
	}
	
	// Remove any entities that are dead
	for ( var i = 0; i < deferred_kills.length; i++ ) {
		removeEntity(deferred_kills[i]);
	}
  	
  	// Draw debug data on the canvas
	world.DrawDebugData();
	
	if ( v0x && v0y ) {
		// Hard coded staring position on the canvas.
		// In a game, this would be the position of the
		// player.
		var startPos = new b2Vec2(35, 378);
		
		// Force to apply to the arrow.
		var force = 5;
		
		// Calculate trajectory angle			
		var angle = Math.atan(v0y/v0x);

		var startPositionMeters = startPos.Copy();

		startPositionMeters.Multiply(1/PTM_RATIO);
		
		// Vector value of velocity based on force and
		// trajectory angle.
		var startingVelocity = new b2Vec2(v0x,v0y);
		  
		// Starting position when calculating the trajectory
		// position at a given step
		var trajectoryStartingPos = new b2Vec2(0,0);

		// Determines the number of trajectory points to calculate.
		var numTrajectoryPoints = 180;

		// Set the canvas context drawing parameters
		// for the trajectory markers. 
		ctx.save();
		ctx.fillStyle = 'green';
		ctx.lineWidth = 0.1;
		ctx.strokeStyle = '#003300';

		// Size of the trajectory marker
		var radius = 2;

		// Initialise the starting position of the ray.
		// This value will be set to the value of the end
		// of the ray at the end of every loop.

		var show_trajectory=true;

		for (i=0;i<=pointMarker;i++) {

			if ( !show_trajectory ){
				end_simulation = true;
				break;
			};

			var startOfRay = startPositionMeters.Copy();

			// Flag indicating whether to draw the trajectory.
			// Set to false when the raycast hits a physics body
			// to prevent the trajectory from passing through
			// objects.
			var show_trajectory = true;

			// Loop to determine the trajectory points
			
			// Stop drawing trajectory markers.

			// Current trajectory coordinates for step i.
			var cur_pos = getTrajectoryPoint(startPositionMeters,
											 startingVelocity, 
											 i);

			// Calculate the coordinates of the trajectory marker
			// on the canvas.
			var trajectoryMarkerPos = cur_pos.Copy();
			trajectoryMarkerPos.Multiply(PTM_RATIO);

			// The end of the ray is the current position in the
			// trajectory. The ray will therefore be the line 
			// from the previous trajectory position to the current
			// trajectory position.
			var endOfRay = cur_pos.Copy();

			// Cast the ray to see if it hits some physics body. If it
			// does then set show_trajectory to false and change the fill
			// style to indicate the expected collision point.
			world.RayCast(function(raycast){
								   var b = raycast.GetBody();
								   var stop = false;
								   if(b){
									  ctx.fillStyle = 'red';
									  radius = 2;
									  show_trajectory = false;
									  if(dropBall){dropBall.play();}
									  dropBall = null;
								   }
							   }, startOfRay, endOfRay);


			// Draw the trajectory marker
			ctx.beginPath();
			ctx.arc(trajectoryMarkerPos.x, 
				    trajectoryMarkerPos.y, 
				    radius, 
				    0, 
				    2 * Math.PI, 
				    false);
			ctx.fill();
			ctx.stroke();

			startOfRay = endOfRay;

		}

		pointMarker++;
		
		drawTrajectoryPath(startPos, angle, force);
	}

	ctx.restore();

	if(end_simulation){
		alert_result();
	}

 	golfPlayer.update();
	world.ClearForces();
}


/*
 * 
 *
 */
function alert_result(){
	if(v0x >= 6 && v0x < 7 &&  v0y< -14 && v0y>= -15){
		clean_try();
		document.getElementById('perdiste').style.display = 'none';  
		document.getElementById('ganaste').style.display = 'block';
		if(winSound) { winSound.play(); }
		winSound = null;
		tryNumber = 0;		 
	}else{ 
		document.getElementById('perdiste').style.display = 'block';
		document.getElementById('ganaste').style.display = 'none';  
		if(loseSound) { loseSound.play(); }
		loseSound = null;
		if(tryNumber>0){alert_try();}
	}

}

/*
 * 
 *
 */
function alert_try(){
	console.log({try: tryNumber})
	if(tryNumber == 1){
		document.getElementById('intento1').style.display = 'block';  
	}else if(tryNumber > 1){
		document.getElementById('intento1').style.display = 'none';  
		document.getElementById('intento2').style.display = 'block';
	}
}

/*
 * 
 *
 */
function clean_try(){
	document.getElementById('intento1').style.display = 'none';  
	document.getElementById('intento2').style.display = 'none';
}

/*
 * init
 * Initialises the demo.
 */
function init(){	
	//
	// Create the box2d world
	//

	
	world = new b2World(
						new b2Vec2(0, 10),   //gravity
						true                 //allow sleep
					   );
					   
	
	var listener = new b2ContactListener();

	listener.PostSolve = 

		function(contact, impulse){
			 var bodyA = contact.GetFixtureA().GetBody();
			 var bodyB = contact.GetFixtureB().GetBody();
			 var uA = bodyA ? bodyA.GetUserData() : null;
			 var uB = bodyB ? bodyB.GetUserData() : null;
			 if (uA !== null) {
				if (uA.ent !== undefined && uA.ent !== null && uA.ent.onTouch) {
					uA.ent.onTouch(bodyB, null, impulse);
				}
			 }
		 
			 if (uB !== null) {
				if (uB.ent !== undefined && uB.ent !== null && uB.ent.onTouch) {
					uB.ent.onTouch(bodyA, null, impulse)
				}
			 }
			 //move = false;
		} // End PostSolve function
	
	world.SetContactListener(listener)
	
	//
	// Configure debug draw
	//
	setDebugDraw(world);
	
	var canvas = document.getElementById('canvas');
	// Hard coded staring position on the canvas.
	// In a game, this would be the position of the
	// player.
	var startPos = {x: 35, y: 368};
				
	ctx = canvas.getContext('2d');
	
	canvas_size.x = canvas.width;
	canvas_size.y = canvas.height;
	
	if ( !initialised ) {
		var start = document.getElementById('start_btn');
		start.addEventListener('click', function(e) {
			tryNumber++;
			// The mouse position of the event
		    v0x = parseFloat(document.getElementById('v0x').value);
		    v0y = -(parseFloat(document.getElementById('v0y').value));
			if( v0x != "" && v0y != ""){       		    
				// Calculate trajectory angle	
				var angle = Math.atan(v0y/v0x);

				pointMarker=0;

				end_simulation=false;

				document.getElementById('ganaste').style.display = 'none';
				document.getElementById('perdiste').style.display = 'none';  
				swingSoung=document.getElementById('swing-sound');
				dropBall=document.getElementById('drop-ball');

				// Force to apply to the arrow.
				var force = 5;
				
				// Create a new arrow object
				var arrow  = new ArrowClass(startPos, angle, force);

				// Add to the entities list.
				entities.push(arrow);
				golfPlayer = new PlayerClass({x: 48, y: 345});
				move = true;
				winSound =document.getElementById('win');
				loseSound =document.getElementById('game-over');
			} else {

			}
		}, false);
		
		// Create event listeners
		canvas.addEventListener('selectstart', 
								function(e){preventDefault(e);}, 
								false);
		canvas.addEventListener('dblclick', 
								function(e){preventDefault(e);}, 
								false);
		
		// The reset button.
		var reset_btn = document.getElementById('reset_btn');
		reset_btn.addEventListener('click', 
								function(e) { reset(); }, 
								false);
		
		// Set the first time the init function is called.
		initialised = true;
	}
	
	
	createMap();
	golfPlayer = new PlayerClass({x: 48, y: 345});
	interval = window.setInterval(update, fHz);
}

