/* ===============================================================
	 Filename: blowflyAnimation-5.js
   Javascript objects for animation
   using the browser viewport (vp) as boundary.
   Kindly give credit to arc <http://www.arc.id.au/>

   Date     Description                                       By
   --------|--------------------------------------------------|---
   15Oct06  Version of animation code for blow fly simulation  ARC
   11Nov09  Changed sound player to Java                       ARC
   23Jan10  Clean and modernise code                           ARC
   23Jan10  HTML5 audio type                                   ARC
   24Jan10  Removed audio player specific code
            start resets pathIdx
            added resume() starts from current pathIdx value   ARC
 * ===============================================================*/

//Globals
var tickInt = 30;    // tick interval in msec
var PIon180 = Math.PI / 180;
var cosTilt = 0.3;

// constructor for absolute position of object (in pixels)
function pos(x, y)
{
  this.x = Math.round(x);
  this.y = Math.round(y);
}

function size(w, h)
{
	this.width = w;
	this.height = h;
}

function boundary(left, top, width, height, reflect)
{
	this.left = left;
	this.right = left + width;
	this.top = top;
	this.bottom = top + height;
	this.reflect = reflect;    // bounce off wall else disappear
}

function getCurrPos(id)
{
	var docObj = document.getElementById(id);

  x = docObj.offsetLeft;     // offset form parent left position (in pixels)
  y = docObj.offsetTop;      // offset form parent top position

  return new pos(x,y);
}

function getElementSize(id)
{
	var w, h;
 	var docObj = document.getElementById(id);

	w = docObj.offsetWidth;   // value in pixels
  h = docObj.offsetHeight;

	return new size(w, h);
}

// viewPort dimensions need to be global
var vp = new boundary(0, 0, 0, 0, true);

function getViewport()  // isr to recacl the viewport boundaries
{
// ref: http://www.quirksmode.org/viewport/compatibility.html
	if (self.pageYOffset) // all except Explorer
	{
		vp.left = self.pageXOffset;
		vp.top = self.pageYOffset;
		vp.right = vp.left + self.innerWidth;
		vp.bottom = vp.top + self.innerHeight;
	}
	else if (document.documentElement)  	// Explorer 6 Strict
	{
		vp.left = document.documentElement.scrollLeft;
		vp.top = document.documentElement.scrollTop;
		vp.right = vp.left + document.documentElement.clientWidth;
		vp.bottom = vp.top + document.documentElement.clientHeight;
	}
	else if (document.body) // all other Explorers
	{
		vp.left = document.body.scrollLeft;
		vp.top = document.body.scrollTop;
		vp.right = vp.left + document.body.clientWidth;
		vp.bottom = vp.top + document.body.clientHeight;
	}
}

function randomInt(first, last)
{
	var nVals = last - first + 1;
	return Math.floor(Math.random() * nVals + first);
}

function turnPic(imgElem, dir)
{
  if (dir < 0)
    imgElem.src = imgLeft.src;
  else
    imgElem.src = imgRight.src;
}

function animation(id, path, steps)
{
  this.elem = document.getElementById(id);
  this.active = 0;
  this.timer = null;
	this.path = path;   // pointer to the path object to be stepped along
  this.pathIdx = 0;
	this.numSteps = steps; // number of step to take, 0 = go forever
}

animation.prototype.start = function()
{
  if (this.active)
		return;

	var savThis = this;

	this.pathIdx = 0;  // start at beginning of path
	this.step();
  this.active = 1;
  // create closure
	this.timer = setInterval(function(){savThis.step()}, tickInt);
	playSound('flySound');
}

animation.prototype.resume = function()
{
  if (this.active)
		return;

	var savThis = this;

	// this.pathIdx resumes where it was stopped
	this.step();
  this.active = 1;
  // create closure
	this.timer = setInterval(function(){savThis.step()}, tickInt);
	playSound('flySound');
}

animation.prototype.stop = function()    // same as pause, pathIdx not reset to 0
{
  if (!this.timer)
		 return false;
  clearInterval(this.timer);
  this.active = 0;
	stopSound('flySound');
}

animation.prototype.step = function()
{
	var nextPos = this.path.nextStep(this.pathIdx);

	this.moveTo(nextPos.x, nextPos.y);
	if ((this.numSteps > 0) && (this.pathIdx >= this.numSteps))
		this.stop();
	else
	 	this.pathIdx++;
}

animation.prototype.moveTo = function(x, y)
{
	this.elem.style.top = y + 'px';
  this.elem.style.left = x + 'px';
}

// flight plan for blowfly, a path object
function flightPlan(id, initPos, speed, walls)
{
	this.elemSize = getElementSize(id);       // size object
	this.currX = initPos.x;
	this.currY = initPos.y;
	this.walls = walls;
	this.aStep = speed;             // angle per step
  this.dir = 1;                  // 1 = cw, -1 = ccw
	this.imgDir = -1;          // -1=left, 1=right

	this.rad = 50;                 // radius of 1st sector
	this.currAngle = 90;        // angle from initPos to centre of 1st sector
  this.currStep = 0;
  this.steps = randomInt(90,270)/this.aStep;  // steps in this sector (each of angle aStep)
	this.cx = initPos.x - this.rad * Math.cos(this.currAngle * PIon180);   // coords of centre
  this.cy = initPos.y - this.rad * Math.sin(this.currAngle * PIon180);
}

flightPlan.prototype.nextStep = function(index)
{
	var x, y;    // new pos candidate

  this.currStep++;
  if (this.currStep > this.steps)
  {
    // start new sector
    this.rad = 6*randomInt(3,10);
    this.currStep = 0;
    this.steps = randomInt(90,220)/this.aStep;
    this.dir *= -1;         // go around the other way
    this.currAngle += 180;    // avoid discontinuity in path
    this.cx = this.currX - this.rad * Math.cos(this.currAngle * PIon180);   // coords of centre
    this.cy = this.currY - cosTilt*this.rad * Math.sin(this.currAngle * PIon180);
  }

  // take a step around the current sector
  this.currAngle += this.dir*this.aStep;
  x = this.cx + this.rad * Math.cos(this.currAngle * PIon180);
  y = this.cy + cosTilt*this.rad * Math.sin(this.currAngle * PIon180);

	if (x > this.walls.right - this.elemSize.width)
	{
    // start new sector with centre a random distance perpendicular to right wall
    this.rad = 6*randomInt(3,10);
    this.currStep = 0;
    this.steps = randomInt(90,220)/this.aStep;
    this.currAngle = 0;    // points from center to ball on the right wall
    this.cx = this.currX - this.rad;   // coords of centre
    this.cy = this.currY;
    x = this.cx + this.rad * Math.cos(this.currAngle * PIon180);
    y = this.cy + cosTilt*this.rad * Math.sin(this.currAngle * PIon180);
	}

	if (x < this.walls.left)
	{
    // start new sector with centre a random distance perpendicular to left wall
    this.rad = 6*randomInt(3,10);
    this.currStep = 0;
    this.steps = randomInt(90,220)/this.aStep;
    this.currAngle = 180;    // pointing at the ball on wall from centre
    this.cx = this.currX + this.rad;   // coords of centre
    this.cy = this.currY;
    x = this.cx + this.rad * Math.cos(this.currAngle * PIon180);
    y = this.cy + cosTilt*this.rad * Math.sin(this.currAngle * PIon180);
	}
	if (y > this.walls.bottom - this.elemSize.height)
	{
    // start new sector with centre a random distance perpendicular to bottom wall
    this.rad = 6*randomInt(3,10);
    this.currStep = 0;
    this.steps = randomInt(90,220)/this.aStep;
    this.currAngle = 90;   // ie pointing at the ball (on wall) from centre
    this.cx = this.currX;   // coords of centre
    this.cy = this.currY - cosTilt*this.rad;
    x = this.cx + this.rad * Math.cos(this.currAngle * PIon180);
    y = this.cy + cosTilt*this.rad * Math.sin(this.currAngle * PIon180);
	}
	if (y < this.walls.top)
	{
    // start new sector with cenre a random distance perpendicular to top wall
    this.rad = 6*randomInt(3,10);
    this.currStep = 0;
    this.steps = randomInt(90,220)/this.aStep;
    this.currAngle = -90;    // ie pointing at the ball (on wall) from centre
    this.cx = this.currX;   // coords of centre
    this.cy = this.currY + cosTilt*this.rad;
    x = this.cx + this.rad * Math.cos(this.currAngle * PIon180);
    y = this.cy + cosTilt*this.rad * Math.sin(this.currAngle * PIon180);
	}
	this.currX = x;
	this.currY = y;

	// swap the fly image to the one travelling in the right direction
	if (((this.dir < 0) && (this.currY < this.cy))
		||((this.dir > 0) && (this.currY > this.cy))) // should be facing left
	{
		if (this.imgDir > 0)
		{
			this.imgDir *= -1;
			turnPic(picElem, this.imgDir);
		}
	}
	if (((this.dir < 0) && (this.currY > this.cy))
	||((this.dir > 0) && (this.currY < this.cy))) // should be facing right
	{
		if (this.imgDir < 0)
		{
			this.imgDir *= -1;
			turnPic(picElem, this.imgDir);
		}
	}

	return new pos(this.currX, this.currY);
}
