//adapted from http://box2d-js.sourceforge.net/

var debug = 0;
var FPS = 30;
var world;
var barriers = {};
var bodyElements;
var b2Bodies = [];
var rectCache = {};

//returns [x, y, w, h] from DOM element
function getRect(e, clearCache) {
  if (!clearCache && rectCache[e]) return rectCache[e];

  $e = $(e);
  var r = {
    //x: parseInt($e.getStyle("left")),
    //y: parseInt($e.getStyle("top")),
    //w: parseInt($e.getStyle("width")),
    //h: parseInt($e.getStyle("height"))
    x: $e.offsetLeft,
    y: $e.offsetTop,
    w: $e.offsetWidth,
    h: $e.offsetHeight,
  };

  //add centroid, too
  r.cx = r.x + r.w/2;
  r.cy = r.y + r.h/2;

  //cache to save some cycles
  rectCache[e] = r;
  return r;
}

function allSleeping() {
  for (var i=0; i<b2Bodies.length; i++)
  {
    if (!b2Bodies[i] || !b2Bodies[i].IsSleeping()) return false;
  }
  return true;
}

function initWorld(g) {
  var rect = getRect("world", true);

  var worldAABB = new b2AABB();
  //we're using HTML coordinates, so we don't want to go offscreen
  worldAABB.minVertex.Set(0, -400);
  //worldAABB.maxVertex.Set(1000, 1000);
  worldAABB.maxVertex.Set(rect.w, rect.h);

  var gravity = new b2Vec2(0, g || 300);
  var doSleep = true; //so we don't tax the CPU too much...

  world = new b2World(worldAABB, gravity, doSleep);

  barriers.ground = createBarrier("ground");
  barriers.leftWall = createBarrier("left-wall");
  barriers.rightWall = createBarrier("right-wall");

  //createWind(); //a gentle breeze to shake things up

  bodyElements = $$("a.b2body");
  //set up bodies in the world
  for (var i=0; i < bodyElements.length; i++) {
    //var x = (i-1)*rect.w/bodyElements.length + Math.random()*100;
    var x = rect.w/2 + Math.random()*100;
    var y = -200;

    $(bodyElements[i]).hide();
    updateBody(i, x, y);

    //drop a link every dt millis
    var dt = 400;
    //drop in reverse order so more important links are on the top
    setTimeout('dropBody('+i+')', (bodyElements.length-i)*dt);
    //dropBody(i);
  }
};

function dropBody(i) {
  if (b2Bodies[i]) return; //no race conditions, please!

  $(bodyElements[i]).show();

  var bRect = getRect( bodyElements[i], true );
  //use centroid; last arg is radius
  b2Bodies[i] = addCircleBody(bRect.cx, bRect.cy, bRect.h/2);
  //b2Bodies[i].bullet = true;

  //if (b2Bodies[i-1])
  //  addRevJoint(b2Bodies[i-1], b2Bodies[i]);
}

function updateBody(id, x, y) {
  //is id an integer or string?
  var e = (parseInt(id) == id) ? bodyElements[id] : id;

  //tricky part: b2d likes center points
  //DOM likes top-left point
  //x and y are coming from b2d
  var bRect = getRect(e);
  x -= bRect.w/2;
  y -= bRect.h/2;

  $(e).setStyle({ left: x+"px", top:  y+"px" });
}

function updateWorld() {
  updateBody("ground", barriers.ground.m_position.x, barriers.ground.m_position.y);
  updateBody("left-wall", barriers.leftWall.m_position.x, barriers.leftWall.m_position.y);
  updateBody("right-wall", barriers.rightWall.m_position.x, barriers.rightWall.m_position.y);

  for (var i=0; i < bodyElements.length; i++) {
    if (b2Bodies[i]) //might not be defined yet because of setTimeout code...
      updateBody(i, b2Bodies[i].m_position.x, b2Bodies[i].m_position.y);
  }
}

function createWind(x, y) {
  var f = new b2Vec2(x, y);
}

//turns a DIV into floor/wall etc
function createBarrier(e) {
  var rect = getRect(e, true);

  //BoxDef = shape
  var barrierSd = new b2BoxDef();
  //barrierSd.extents.Set(1000, 50);
  //extents are half-width and half-height (???)
  barrierSd.extents.Set(rect.w/2, rect.h/2);
  barrierSd.restitution = 0.1;
  barrierSd.density = 0.0; //no mass means it's a static body

  //BodyDef = physical properties besides shape
  var barrierBd = new b2BodyDef();
  barrierBd.AddShape(barrierSd);
  barrierBd.position.Set(rect.cx, rect.cy);
  return world.CreateBody(barrierBd);
}

function addCircleBody(x, y, r) {
  var circleSd = new b2CircleDef();
  circleSd.density = 1.0;
  circleSd.radius = r;
  circleSd.restitution = 0.45;
  circleSd.friction = 0.5;

  var circleBd = new b2BodyDef();
  circleBd.AddShape(circleSd);
  circleBd.position.Set(x, y);

  return world.CreateBody(circleBd);
}

function addRevJoint(body1, body2) {
  var jointDef = new b2RevoluteJointDef();
  jointDef.anchorPoint.Set(250, 200);
  //jointDef.body1 = world.GetGroundBody();
  jointDef.body1 = body1;
  jointDef.body2 = body2;
  world.CreateJoint(jointDef);
}

var triggerVacuum = function() {
  var vRect = getRect("vacuum", true);

  //pt at center of vacuum
  var vCenter = new b2Vec2(vRect.cx, vRect.cy);

//alert(pt);

  //now force is vector towards pt

  for (var i=0; i < b2Bodies.length; i++) {
    var bRect = getRect( $(bodyElements[i]), true );
    var bCenter = new b2Vec2(bRect.cx, bRect.cy);
    var f = bCenter; //for convenience
    f.Subtract(vCenter); //vector from vCenter to bCenter
    f.Normalize(); //so we only have direction (magnitude probably not useful)
    //negative due to direction of vector
    f.Multiply(-200);

    b2Bodies[i].WakeUp();
    b2Bodies[i].m_linearVelocity.Set(f.x, f.y);
  }
}

function step() {
  world.Step(1.0/FPS, 1); //all physics is here
  updateWorld(); //just drawing code, no physics here

  //don't uncomment till everything's 100% working :-)
  //if (allSleeping()) clearInterval(step);
}

Event.observe(window, 'load', function() {
  initWorld(75);

  //in debug mode, step lightly
  if (debug)
    step();
  else
    setInterval(step, 10);

  $('vacuum').observe('mousedown', triggerVacuum);
});

/*
Event.observe(body, 'mousedown', function(event) {
  var p = Event.pointer(event);
  $("vacuum").setStyle({left: p.x, top: p.y});
  $("vacuum").show();
});

Event.observe(body, 'mouseup', function() {
  $("vacuum").hide();
});
*/

