var   b2Vec2 = Box2D.Common.Math.b2Vec2
    ,   b2BodyDef = Box2D.Dynamics.b2BodyDef
    ,   b2Body = Box2D.Dynamics.b2Body
    ,   b2RevoluteJointDef = Box2D.Dynamics.Joints.b2RevoluteJointDef
    ,   b2PrismaticJointDef = Box2D.Dynamics.Joints.b2PrismaticJointDef
    ,   b2FixtureDef = Box2D.Dynamics.b2FixtureDef
    ,   b2Fixture = Box2D.Dynamics.b2Fixture
    ,   b2World = Box2D.Dynamics.b2World
    ,   b2MassData = Box2D.Collision.Shapes.b2MassData
    ,   b2PolygonShape = Box2D.Collision.Shapes.b2PolygonShape
    ,   b2CircleShape = Box2D.Collision.Shapes.b2CircleShape
    ,   b2DebugDraw = Box2D.Dynamics.b2DebugDraw

var DEBUG = false;

var FRAMERATE           = 60;
var MILLISECONSPERFRAME = 1000/FRAMERATE;

var STEER_NONE  = 0;
var STEER_RIGHT = 1;
var STEER_LEFT  = 2;

var ACC_NONE       = 0;
var ACC_ACCELERATE = 1;
var ACC_BRAKE      = 2;

var ON_GRASS = false;

var CAR_LINEAR_DAMPING         = 0.2; //gradually reduces velocity, makes the car reduce speed slowly if neither accelerator nor brake is pressed
var CAR_ANGULAR_DAMPING        = 0.3; 
var CAR_DENSITY                = 1.25;
var CAR_FRICTION               = 0.3; //friction when rubbing agaisnt other shapes
var CAR_RESTITUTION            = 0.4; //amount of force feedback when hitting something. >0 makes the car bounce off, it's fun!
var CAR_MAX_SPEED              = 300;
var CAR_SPEED_ON_GRASS         = 40;
var CAR_MAX_STEER_ANGLE        = 30;
var CAR_POWER                  = 190;
var CAR_LENGTH                 = 6.3;
var CAR_WIDTH                  = 2.5;
var CAR_FRONT_WHEEL_WIDTH      = 0.4;
var CAR_BACK_WHEEL_WIDTH       = 0.4;
var CAR_FRONT_WHEEL_LENGTH     = 0.8;
var CAR_BACK_WHEEL_LENGTH      = 0.8;
var CAR_FRONT_WHEELS_POWER     = true;
var CAR_BACK_WHEELS_POWER      = false;
var CAR_FRONT_WHEELS_REVOLVING = true;
var CAR_BACK_WHEELS_REVOLVING  = false;

var WHEEL_DENSITY              = 0.5;

var CANVAS_NAME = 'gjs-canvas';
var SCALE       = 6;  //how many pixels in a meter
var WIDTH_PX;         //screen width in pixels
var HEIGHT_PX;        //screen height in pixels
var WIDTH_M;          //world width in meters. for this example, world is as large as the screen
var HEIGHT_M;         //world height in meters
var KEYS_DOWN   = {}; //keep track of what keys are held down by the player

var b2world;
var stats;
var car;

var canvas;
var context;
var edge;
var listener;

var TRACK = 1;
var CAR   = 2;

var edges = [];

var vuelta   = 0;
var pasos    = 0;
var lastpaso = 0;
var barreras = {};

var hKeys = {
  'up'  : 38,
  'down'  : 40,
  'left'  : 37,
  'right' : 39
}

function Wheel(pars){
  /*
  wheel object 
        
  pars:
  
  car - car this wheel belongs to
  x - horizontal position in meters relative to car's center
  y - vertical position in meters relative to car's center
  width - width in meters
  length - length in meters
  revolving - does this wheel revolve when steering?
  powered - is this wheel powered?
  */

  this.position=[pars.x, pars.y];
  this.car=pars.car;
  this.revolving=pars.revolving;
  this.powered=pars.powered;

  //initialize body
  var def=new b2BodyDef();
  def.type = b2Body.b2_dynamicBody;
  def.position=this.car.body.GetWorldPoint(new b2Vec2(this.position[0], this.position[1]));
  def.angle=this.car.body.GetAngle();
  this.body=b2world.CreateBody(def);
  
  //initialize shape
  var fixdef = new b2FixtureDef;
  fixdef.density = WHEEL_DENSITY;
  fixdef.isSensor=true; //wheel does not participate in collision calculations: resulting complications are unnecessary
  fixdef.shape=new b2PolygonShape();
  fixdef.shape.SetAsBox(pars.width/2, pars.length/2);
  this.body.CreateFixture(fixdef);

  //create joint to connect wheel to body
  if (this.revolving) {
    var jointdef=new b2RevoluteJointDef();
    jointdef.Initialize(this.car.body, this.body, this.body.GetWorldCenter());
    jointdef.enableMotor=false; //we'll be controlling the wheel's angle manually
  } else {
    var jointdef=new b2PrismaticJointDef();
    jointdef.Initialize(this.car.body, this.body, this.body.GetWorldCenter(), new b2Vec2(1, 0));
    jointdef.enableLimit=true;
    jointdef.lowerTranslation=jointdef.upperTranslation=0;
  }
  b2world.CreateJoint(jointdef);
}

Wheel.prototype.setAngle=function(angle){
  /*
  angle - wheel angle relative to car, in degrees
  */
  this.body.SetAngle(this.car.body.GetAngle()+math.radians(angle));
};

Wheel.prototype.getLocalVelocity=function(){
  /*
  returns get velocity vector relative to car
  */
  var res=this.car.body.GetLocalVector(this.car.body.GetLinearVelocityFromLocalPoint(new b2Vec2(this.position[0], this.position[1])));

  return [res.x, res.y];
};

Wheel.prototype.getDirectionVector=function(){
  /*
  returns a world unit vector pointing in the direction this wheel is moving
  */
  return vectors.rotate((this.getLocalVelocity()[1]>0) ? [0, 1]:[0, -1] , this.body.GetAngle()) ;
};


Wheel.prototype.getKillVelocityVector=function(){
  /*
  substracts sideways velocity from this wheel's velocity vector and returns the remaining front-facing velocity vector
  */
  var velocity=this.body.GetLinearVelocity();
  var sideways_axis=this.getDirectionVector();
  var dotprod=vectors.dot([velocity.x, velocity.y], sideways_axis);

  return [sideways_axis[0]*dotprod, sideways_axis[1]*dotprod];
};

Wheel.prototype.killSidewaysVelocity=function(){
  /*
  removes all sideways velocity from this wheels velocity
  */
  var kv=this.getKillVelocityVector();
  this.body.SetLinearVelocity(new b2Vec2(kv[0], kv[1]));
};

function Car(pars){
  /*
  pars is an object with possible attributes:
  
  width - width of the car in meters
  length - length of the car in meters
  position - starting position of the car, array [x, y] in meters
  angle - starting angle of the car, degrees
  max_steer_angle - maximum angle the wheels turn when steering, degrees
  max_speed       - maximum speed of the car, km/h
  power - engine force, in newtons, that is applied to EACH powered wheel
  wheels - wheel definitions: [{x, y, rotatable, powered}}, ...] where
           x is wheel position in meters relative to car body center
           y is wheel position in meters relative to car body center
           revolving - boolean, does this turn rotate when steering?
           powered - is force applied to this wheel when accelerating/braking?
  */

  //state of car controls
  this.steer=STEER_NONE;
  this.accelerate=ACC_NONE;
  
  this.max_steer_angle=pars.max_steer_angle;
  this.max_speed=pars.max_speed;
  this.power=pars.power;
  this.wheel_angle=0;//keep track of current wheel angle relative to car.
                     //when steering left/right, angle will be decreased/increased gradually over 200ms to prevent jerkyness.
  
  //initialize body
  var def            = new b2BodyDef();
  def.type           = b2Body.b2_dynamicBody;
  def.position       = new b2Vec2(pars.position[0], pars.position[1]);
  def.angle          = math.radians(pars.angle); 
  def.linearDamping  = CAR_LINEAR_DAMPING;  
  def.bullet         = true; //dedicates more time to collision detection - car travelling at high speeds at low framerates otherwise might teleport through obstacles.
  def.angularDamping = CAR_ANGULAR_DAMPING;
  def.userData       = { t:'car' };
  this.body          = b2world.CreateBody(def);
  
  //initialize shape
  var fixdef         = new b2FixtureDef();
  fixdef.density     = CAR_DENSITY;
  fixdef.friction    = CAR_FRICTION;
  fixdef.restitution = CAR_RESTITUTION;  
  fixdef.shape       = new b2PolygonShape;

  fixdef.shape.SetAsBox(pars.width/2, pars.length/2);
  this.body.CreateFixture(fixdef);

  this.image        = new Image();
  this.image.src    = cars[CAR].img;
  this.image_width  = 30;
  this.image_height = this.image_width * cars[CAR].h / cars[CAR].w;
  
  //initialize wheels
  this.wheels=[]
  var wheeldef, i;
  for (i=0;i<pars.wheels.length;i++) {
    wheeldef=pars.wheels[i];
    wheeldef.car=this;
    this.wheels.push(new Wheel(wheeldef));
  }
}

Car.prototype.getPoweredWheels=function(){
  //return array of powered wheels
  var retv=[];
  for(var i=0;i<this.wheels.length;i++){
    if(this.wheels[i].powered){
      retv.push(this.wheels[i]);
    }
  }
  return retv;
};

Car.prototype.getLocalVelocity=function(){
  /*
  returns car's velocity vector relative to the car
  */
  var retv=this.body.GetLocalVector(this.body.GetLinearVelocityFromLocalPoint(new b2Vec2(0, 0)));
  return [retv.x, retv.y];
};

Car.prototype.getRevolvingWheels=function(){
  //return array of wheels that turn when steering
  var retv=[];
  for(var i=0;i<this.wheels.length;i++){
    if(this.wheels[i].revolving){
      retv.push(this.wheels[i]);
    }
  }
  return retv;
};

Car.prototype.getSpeedKMH=function(){
  var velocity=this.body.GetLinearVelocity();
  var len=vectors.len([velocity.x, velocity.y]);
  return (len/1000)*3600;
};

Car.prototype.setSpeed=function(speed){
  /*
  speed - speed in kilometers per hour
  */
  var velocity=this.body.GetLinearVelocity();
  velocity=vectors.unit([velocity.x, velocity.y]);
  velocity=new b2Vec2(velocity[0]*((speed*1000.0)/3600.0),
                      velocity[1]*((speed*1000.0)/3600.0));
  this.body.SetLinearVelocity(velocity);
};

Car.prototype.update=function(){
    
  //1. KILL SIDEWAYS VELOCITY
  
  //kill sideways velocity for all wheels
  var i;
  for(i=0;i<this.wheels.length;i++){
      this.wheels[i].killSidewaysVelocity();
  }

  //2. SET WHEEL ANGLE

  //calculate the change in wheel's angle for this update, assuming the wheel will reach is maximum angle from zero in 200 ms
  var incr=(this.max_steer_angle/400) * MILLISECONSPERFRAME;
  
  if (this.steer==STEER_RIGHT){
    this.wheel_angle=Math.min(Math.max(this.wheel_angle, 0)+incr, this.max_steer_angle) //increment angle without going over max steer
  } else if(this.steer==STEER_LEFT) {
    this.wheel_angle=Math.max(Math.min(this.wheel_angle, 0)-incr, -this.max_steer_angle) //decrement angle without going over max steer
  } else {
    this.wheel_angle=0;        
  }

  //update revolving wheels
  var wheels=this.getRevolvingWheels();
  for (i=0;i<wheels.length;i++) {
    wheels[i].setAngle(this.wheel_angle);
  }
  
  //3. APPLY FORCE TO WHEELS
  var base_vect; //vector pointing in the direction force will be applied to a wheel ; relative to the wheel.
  
  //if accelerator is pressed down and speed limit has not been reached, go forwards
  if ((this.accelerate==ACC_ACCELERATE) && (this.getSpeedKMH() < this.max_speed)) {
    base_vect=[0, -1];
  } else if (this.accelerate==ACC_BRAKE) {
    //braking, but still moving forwards - increased force
    if(this.getLocalVelocity()[1]<0)base_vect=[0, 1.3];
    //going in reverse - less force
    else base_vect=[0, 0.7];
  } else base_vect=[0, 0];

  //multiply by engine power, which gives us a force vector relative to the wheel
  var fvect=[this.power*base_vect[0], this.power*base_vect[1]];

  //apply force to each wheel
  wheels=this.getPoweredWheels();
  for (i=0;i<wheels.length;i++) {
    var position=wheels[i].body.GetWorldCenter();
    wheels[i].body.ApplyForce(wheels[i].body.GetWorldVector(new b2Vec2(fvect[0], fvect[1])), position );
  }

  if (ON_GRASS && this.getSpeedKMH() > CAR_SPEED_ON_GRASS) {
    this.setSpeed(CAR_SPEED_ON_GRASS)
  }
  
  //if going very slow, stop - to prevent endless sliding
  if ( (this.getSpeedKMH()<4) &&(this.accelerate==ACC_NONE)) {
    this.setSpeed(0);
  }
};

function drawCircuit() {
  $('#' + CANVAS_NAME).css("background-image", "url(" + tracks[TRACK].img + ")");  

  // -- Bordes -------------------------
  for (var i=0; i< tracks[TRACK].borders.length; i++) {
    var aBorder = tracks[TRACK].borders[i];
    for (var ii=0; ii< aBorder.length-1; ii++) {
      var x      = aBorder[ii].x;
      var y      = aBorder[ii].y;
      var width  = aBorder[ii+1].x - x ;
      var height = aBorder[ii+1].y - y;
      var roadBodyDef = new b2BodyDef;
      roadBodyDef.type = b2Body.b2_staticBody;
      roadBodyDef.position.x = x / SCALE;
      roadBodyDef.position.y = y / SCALE;
      var roadFixtureDef = new b2FixtureDef;
      roadFixtureDef.filter.groupIndex = -1;
      roadFixtureDef.shape = new b2PolygonShape;
      roadFixtureDef.shape.SetAsEdge(new b2Vec2(0, 0), new b2Vec2(width / SCALE, height / SCALE));
      roadFixtureDef.friction = 1;
      b2world.CreateBody(roadBodyDef).CreateFixture(roadFixtureDef);
    }
  }

  // -- Meta ----------------------------
  var metaBodyDef = new b2BodyDef;
  metaBodyDef.type = b2Body.b2_staticBody;
  var x      = tracks[TRACK].meta[0].x;
  var y      = tracks[TRACK].meta[0].y;
  var width  = tracks[TRACK].meta[1].x - x;
  var height = tracks[TRACK].meta[1].y - y;
  metaBodyDef.position.x = x / SCALE;
  metaBodyDef.position.y = y / SCALE;
  metaBodyDef.userData = { t: 'meta'};
  var metaFixtureDef = new b2FixtureDef;
  metaFixtureDef.filter.groupIndex = -1;
  metaFixtureDef.shape = new b2PolygonShape;
  metaFixtureDef.shape.SetAsEdge(new b2Vec2(0, 0), new b2Vec2(width / SCALE, height / SCALE));
  metaFixtureDef.friction = 0;
  metaFixtureDef.isSensor = true;
  var lineMeta = b2world.CreateBody(metaBodyDef)
  lineMeta.CreateFixture(metaFixtureDef);

  // -- Pasos ---------------------------------
  for (var i=0; i< tracks[TRACK].pasos.length; i++) {
    var aPaso = tracks[TRACK].pasos[i];
    var metaBodyDef = new b2BodyDef;
    metaBodyDef.type = b2Body.b2_staticBody;
    var x      = aPaso[0].x;
    var y      = aPaso[0].y;
    var width  = aPaso[1].x - x;
    var height = aPaso[1].y - y;
    metaBodyDef.position.x = x / SCALE;
    metaBodyDef.position.y = y / SCALE;
    metaBodyDef.userData = {t:'paso', v:(i+1)};
    var metaFixtureDef = new b2FixtureDef;
    metaFixtureDef.filter.groupIndex = -1;
    metaFixtureDef.shape = new b2PolygonShape;
    metaFixtureDef.shape.SetAsEdge(new b2Vec2(0, 0), new b2Vec2(width / SCALE, height / SCALE));
    metaFixtureDef.friction = 0;
    metaFixtureDef.isSensor = true;
    var linePaso = b2world.CreateBody(metaBodyDef)
    linePaso.CreateFixture(metaFixtureDef);      
  }

  // -- Barreras ---------------------------------
  barreras = {};
  for (var i=0; i< tracks[TRACK].barreras.length; i++) {
    var hBarreras = tracks[TRACK].barreras[i];
    barreras[ i+1 ] = hBarreras.necesita; // La barrera y permite el paso a todos los estados "necesita"
    for (var ii=0; ii< hBarreras.def.length; ii++) {
      var aPaso = hBarreras.def[ii];
      var x      = aPaso[0].x;
      var y      = aPaso[0].y;
      var width  = aPaso[1].x - x ;
      var height = aPaso[1].y - y;
      var roadBodyDef = new b2BodyDef;
      roadBodyDef.type = b2Body.b2_staticBody;
      roadBodyDef.position.x = x / SCALE;
      roadBodyDef.position.y = y / SCALE;
      roadBodyDef.userData = {t:'barrera', v:(i+1)};
      var roadFixtureDef = new b2FixtureDef;
      roadFixtureDef.filter.groupIndex = -1;
      roadFixtureDef.shape = new b2PolygonShape;
      roadFixtureDef.shape.SetAsEdge(new b2Vec2(0, 0), new b2Vec2(width / SCALE, height / SCALE));
      roadFixtureDef.friction = 1;
      roadFixtureDef.isSensor = false;
      b2world.CreateBody(roadBodyDef).CreateFixture(roadFixtureDef);
    }
  }

  for (var i=0; i< tracks[TRACK].puentes.length; i++) {
    tracks[TRACK].puentes[i].image = new Image();
    tracks[TRACK].puentes[i].image.src = tracks[TRACK].puentes[i].img;
  }

  // Esto pone una caja de barro
  // var bodyDef = new b2BodyDef;
  // bodyDef.type = b2Body.b2_kinematicBody;
  // bodyDef.position.Set(50,70);
  // bodyDef.userData = 'barro'; 
  // var fixDef = new b2FixtureDef;      
  // fixDef.restitution = .5;    
  // fixDef.shape = new b2PolygonShape;
  // fixDef.shape.SetAsBox(16.5,16.5); 
  // var fixDefk_s = new b2FixtureDef;
  // fixDefk_s.shape = new b2PolygonShape;
  // fixDefk_s.shape.SetAsOrientedBox(20,20,new b2Vec2(0,0),0);
  // fixDefk_s.isSensor = true;
  // var box1k = b2world.CreateBody(bodyDef);
  // box1k.CreateFixture(fixDefk_s);
  //
  // -------------------------------
  //
  // var roadBodyDef = new b2BodyDef;
  // roadBodyDef.type = b2Body.b2_staticBody;
  // roadBodyDef.position.x = 5;
  // roadBodyDef.position.y = 15;
  // var roadFixtureDef = new b2FixtureDef;
  // roadFixtureDef.filter.groupIndex = -1;
  // roadFixtureDef.shape = new b2PolygonShape;
  // roadFixtureDef.shape.SetAsEdge(new b2Vec2(0, 0), new b2Vec2(20, 20));
  // roadFixtureDef.friction = 0;
  // roadFixtureDef.isSensor = true;
  // var line1 = b2world.CreateBody(roadBodyDef)
  // line1.CreateFixture(roadFixtureDef);
  //
}

function initContactListeners() {
  listener = new Box2D.Dynamics.b2ContactListener;
  listener.BeginContact = function(contact) {
    fxA=contact.GetFixtureA();
    fxB=contact.GetFixtureB();
    // console.log(["BeginContact", fxA.GetBody().GetUserData(), fxB.GetBody().GetUserData()]);
    sA=fxA.IsSensor();
    sB=fxB.IsSensor();
    if((sA && !sB) || (sB && !sA)) {
      var object1 = fxA.GetBody().GetUserData();
      var object2 = fxB.GetBody().GetUserData();
      if (object1 && object2 && (object1.t == "car" || object2.t == "car")) {
        if ( object1.t == "meta" || object2.t == "meta") { // pasamos por la meta
          if (pasos == tracks[TRACK].pasos.length) { // Ha pasado por todos los sitios
            vuelta += 1;
            console.log("vuelta:" + vuelta);              
          }
          pasos  = 0; // empezamos en el paso 0
          lastpaso = 0;
        } else if (object1.t == "paso" || object2.t == "paso") { // pasamos por un paso
          var paso = (object1.v || object2.v);
          if (paso == (pasos + 1)) pasos = paso; // solo aumentamos el paso si vamos en el orden correcto
          lastpaso = paso;
        }
      }
    }
  }
  
  listener.EndContact = function(contact) {
    // console.log("EndContact");
    fxA=contact.GetFixtureA();
    fxB=contact.GetFixtureB();
    sA=fxA.IsSensor();
    sB=fxB.IsSensor();
    if((sA && !sB) || (sB && !sA)) {
     //ON_GRASS = false;
    }
  }
  
  listener.PreSolve = function(contact, c, d) {
    fxA=contact.GetFixtureA();
    fxB=contact.GetFixtureB();
    var object1 = fxA.GetBody().GetUserData();
    var object2 = fxB.GetBody().GetUserData();
    if (object1 && object2 && (object1.t == "car" || object2.t == "car")) {
      if (object1.t == "barrera" || object2.t == "barrera") { // pasamos por un paso
        var barrera = (object1.v || object2.v);
        if (barreras[barrera].indexOf(lastpaso) > -1) {
          contact.SetEnabled(false);
        }
      }
    }
   }
   b2world.SetContactListener(listener);
}

function init(){
   
  b2world=new b2World(new b2Vec2(0, 0), false);
    
  var debugDraw = new b2DebugDraw();
  canvas    = $("#gjs-canvas");
  WIDTH_PX  = canvas.width();   //screen width in pixels
  HEIGHT_PX = canvas.height(); //screen height in pixels
  WIDTH_M   = WIDTH_PX/SCALE; //world width in meters. for this example, world is as large as the screen
  HEIGHT_M  = HEIGHT_PX/SCALE; //world height in meters
  context   = canvas[0].getContext("2d");

  debugDraw.SetSprite( context );
  debugDraw.SetDrawScale(SCALE);
  debugDraw.SetFillAlpha(0.5);
  debugDraw.SetLineThickness(1.0);
  debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
  b2world.SetDebugDraw(debugDraw);

  canvas.bind("mousedown", mouseDownHandler);
  canvas.bind("touchstart", touchDownHandler)
    
  //initialize car
  car=new Car({ 'width'           : CAR_WIDTH,
                'length'          : CAR_LENGTH,
                'position'        : [tracks[TRACK].car.x, tracks[TRACK].car.y],
                'angle'           : tracks[TRACK].car.angle, 
                'power'           : CAR_POWER,
                'max_steer_angle' : CAR_MAX_STEER_ANGLE,
                'max_speed'       : CAR_MAX_SPEED,
                'wheels':[
                { 'x':-1,//top left
                  'y':-2.2,
                  'width':    CAR_FRONT_WHEEL_WIDTH,
                  'length':   CAR_FRONT_WHEEL_LENGTH,
                  'revolving':CAR_FRONT_WHEELS_REVOLVING,
                  'powered':  CAR_FRONT_WHEELS_POWER
                }, 
                {'x':1, //top right
                 'y':-2.2,
                 'width':CAR_FRONT_WHEEL_WIDTH,
                 'length':CAR_FRONT_WHEEL_LENGTH,
                 'revolving':CAR_FRONT_WHEELS_REVOLVING, 
                 'powered':CAR_FRONT_WHEELS_POWER
                }, 
                {'x':-1, //back left
                 'y':2.2, 
                 'width':CAR_BACK_WHEEL_WIDTH,
                 'length':CAR_BACK_WHEEL_LENGTH,
                 'revolving':CAR_BACK_WHEELS_REVOLVING,
                 'powered':CAR_BACK_WHEELS_POWER
                }, 
                {'x':1, //back right
                 'y':2.2, 
                 'width':CAR_BACK_WHEEL_WIDTH,
                 'length':CAR_BACK_WHEEL_LENGTH,
                 'revolving':CAR_BACK_WHEELS_REVOLVING,
                 'powered':CAR_BACK_WHEELS_POWER
                }]
  }); 
}

//GAME LOOP
function update() {
  if (DEBUG) stats.begin();
  
  if( KEYS_DOWN[hKeys.up]){
    car.accelerate=ACC_ACCELERATE;
  } else if (KEYS_DOWN[hKeys.down]){
     car.accelerate=ACC_BRAKE;
  } else {
    car.accelerate=ACC_NONE;
  }
  
  if(KEYS_DOWN[hKeys.right]){
    car.steer=STEER_RIGHT;
  } else if(KEYS_DOWN[hKeys.left]){
    car.steer=STEER_LEFT;
  } else {
    car.steer=STEER_NONE;
  }
  
  car.update();

  b2world.Step(
    1 / FRAMERATE,   //frame-rate
    10,       //position iterations
    10
  );
               
  if (DEBUG) b2world.DrawDebugData();

  b2world.ClearForces();

  if (!DEBUG) {
    context.clearRect ( 0 , 0 , WIDTH_PX, HEIGHT_PX );
    context.save();
    context.translate(car.body.GetPosition().x * SCALE, car.body.GetPosition().y * SCALE);
    context.rotate( car.body.GetAngle() );
    context.drawImage( car.image, -1* car.image_width / 2 , -1 * car.image_height / 2, car.image_width , car.image_height );
    context.restore();
  }

  for (var i=0; i< tracks[TRACK].puentes.length; i++) {
    var oPuente = tracks[TRACK].puentes[i];
    if (oPuente.antes.indexOf(lastpaso) > -1) {
      context.save();
      context.globalAlpha = 0.9;
      context.translate(oPuente.position.x, oPuente.position.y);
      context.drawImage( oPuente.image, 0, 0, oPuente.image.width , oPuente.image.height );
      context.restore();
    }
  }  

  if (DEBUG) stats.end();

  requestAnimFrame(update);
};

        //Create a line from the last point to this one
    function createEdge(x, y) {
      if (edge) {
        edge.width = x - edge.x;
        edge.height = y - edge.y;

        //Only draw if moved over 20 pixels, calculated with Pythagoras
        if (Math.sqrt(Math.pow(edge.width, 2) + Math.pow(edge.height, 2)) > 20) {
          edges.push( { x : edge.x, y: edge.y } );

          var roadBodyDef = new b2BodyDef;
          roadBodyDef.type = b2Body.b2_staticBody;
          roadBodyDef.position.x = edge.x / SCALE;
          roadBodyDef.position.y = edge.y / SCALE;

          var roadFixtureDef = new b2FixtureDef;

          roadFixtureDef.filter.groupIndex = -1;
          roadFixtureDef.shape = new b2PolygonShape;
          roadFixtureDef.shape.SetAsEdge(new b2Vec2(0, 0), new b2Vec2(edge.width / SCALE, edge.height / SCALE));
          roadFixtureDef.friction = 1;

          b2world.CreateBody(roadBodyDef).CreateFixture(roadFixtureDef);
          edge = {
            x: x,
            y: y
          };
        }

      } else {
        edges.push( { x : x, y: y } );
        edge = {
          x: x,
          y: y
        };
      }
    }  


    //When drawing the "road" get mouse or touch positions
    function mouseMoveHandler(e) {
      var touch = e.originalEvent;
      if (touch && touch.touches && touch.touches.length == 1) {
        //Prevent the default action for the touch event; scrolling
        touch.preventDefault();
        createEdge(touch.touches[0].pageX, touch.touches[0].pageY);
      } else {
        createEdge(e.pageX, e.pageY);
      }
      // update();
    }

    //Touch and mouse handlers, remove the event listeners that you dont need
    function mouseUpHandler(e) {
      canvas.bind("mousedown", mouseDownHandler);
      canvas.unbind("mousemove", mouseMoveHandler);
      edge = null;
    }

    function mouseDownHandler(e) {
      canvas.unbind("mousedown", mouseDownHandler);
      canvas.bind("mouseup", mouseUpHandler);
      canvas.bind("mousemove", mouseMoveHandler);
    }

    function touchUpHandler(e) {
      canvas.bind("touchstart", touchDownHandler);
      canvas.unbind("touchmove", mouseMoveHandler);
      edge = null;
    }

    function touchDownHandler(e) {
      canvas.unbind("touchstart", touchDownHandler);
      canvas.bind("touchend", touchUpHandler);
      canvas.bind("touchmove", mouseMoveHandler);
    }



$( document ).ready(function() {
  if (DEBUG) {
    stats = new Stats();
    stats.setMode(1); // 0: fps, 1: ms
    stats.domElement.style.position = 'absolute';
    stats.domElement.style.left = '0px';
    stats.domElement.style.top = '0px';
    document.body.appendChild( stats.domElement );
  }

  window.requestAnimFrame = (function(){
        return  window.requestAnimationFrame       || 
                window.webkitRequestAnimationFrame || 
                window.mozRequestAnimationFrame    || 
                window.oRequestAnimationFrame      || 
                window.msRequestAnimationFrame     || 
                function(/* function */ callback, /* DOMElement */ element){
                  window.setTimeout(callback, MILLISECONSPERFRAME);
                };
  })();

  $( window ).keydown(function(event) {
    if (event.which == 74) console.log(JSON.stringify(edges)); // j print edges
    if (event.which == 67) edges = [];         // c clear edges
    if (event.which == 49) $('#' + CANVAS_NAME).css("background-image", "url(" + tracks[TRACK].img + ")"); // 1 pone fondo
    if (event.which == 50) $('#' + CANVAS_NAME).css("background-image", "url()"); // 2 quita fondo
    KEYS_DOWN[ event.which ] = true;
  });

  $( window ).keyup(function(event) {
    KEYS_DOWN[ event.which ] = false;
  });

  init();

  drawCircuit();

  initContactListeners();

  requestAnimFrame(update);
});
