//---------------------Declare Variables-----------------------

var sprites      = [];
var NUM          = 100;
var width        = 0;
var height       = 0;
var timer        = null;
var m_lastX      = 0;
var m_lastY      = 0;
var S_VMIN       = 3;
var S_VMAX       = 6;
var S_WIDTH      = 18;
var S_HEIGHT     = 18;
var CONTROLS     = 220;
var LOOM1        = 0.06;
var LOOM2        = 0.1;
var PI           = 3.14159;
var spriteradius = 16;
var collisions   = 0;

var twoLevels        = 0;

var turnangle1 = PI/4;
var turnangle2 = PI/4;

var l = 0;
var i = 0;
var j = 0;

//declare 2-dimensional arrays
var or = new Array(NUM);
for(i = 0; i < NUM; i++) 
    or[i] = new Array(NUM);
var nr = new Array(NUM);
for(i = 0; i < NUM; i++) 
    nr[i] = new Array(NUM);
var reaction = new Array(NUM);
for(i = 0; i < NUM; i++) 
    reaction[i] = new Array(NUM);
var collided = new Array(NUM);
for(i = 0; i < NUM; i++) 
    collided[i] = new Array(NUM);

//declare function pointers
var behavior1 = turnaway;
var resume1   = nothing;
var behavior2 = slow;
var resume2   = nothing;
var initcond  = randomSprite;
var endcond   = nothing;
var boundcond = bounce;
var collide   = dieoncollide;

//-----------------------Math Functions------------------------

function rnd(scale) 
{
    return Math.random()*scale;
}

function rndInt(scale) 
{
    return parseInt(rnd(scale));
}

function rndDir()
{
    if(Math.random() >= 0.5) return 1;
    else return -1;
}

function phasormag(x,y) 
{
    return Math.sqrt(Math.pow(x,2) + Math.pow(y,2));
}

function phasorangle(x, y) 
{
    if(x == 0) {
	if(y < 0) {
	    var angle = -PI/2;
	}
	if(y > 0) {
	    var angle = PI/2;
	}
    }
    else {
	var angle = Math.atan(y/x);
	if(x < 0) {
	    angle = PI + angle;
	}
    }
    return angle;
}

function radius(sprite1, sprite2) 
{
    r  = phasormag(sprite1._x-sprite2._x, sprite1._y-sprite2._y);
    return r;
}


//----------------------Animation Loop---------------------------

function animate() 
{

    for(i=0; i<sprites.length; i++) {
	for(j=i+1; j<sprites.length; j++) {
	    nr[i][j] = radius(sprites[i], sprites[j]);	    
	    if(nr[i][j] < spriteradius) {
	        if(!collided[i][j]) {
		    collisions++;
		    document.getElementById('counter').innerHTML = collisions;
		    collided[i][j] = 1;
	        }
		collide(sprites[i], sprites[j]);
	    }
	    l = (or[i][j] - nr[i][j]) / nr[i][j];
	    //Low threat, relax
       	    if(l < LOOM1 && reaction[i][j]) {
		reaction[i][j] = 0;
       		resume1(sprites[i], 1);
		resume1(sprites[j], 1);
	    }
	    //Moderate looming, take action
	    else if(l >= LOOM1 && reaction[i][j] == 0) {
		//store pre-looming velocity so we can resume it later
		sprites[i]._p1vx = sprites[i]._vx;
		sprites[i]._p1vy = sprites[i]._vy;
      		behavior1(sprites[i], sprites[j], turnangle1);

		sprites[j]._p1vx = sprites[j]._vx;
		sprites[j]._p1vy = sprites[j]._vy;
		behavior1(sprites[j], sprites[i], turnangle1);

		reaction[i][j] = 1;
	    }
	    //Moderate looming, severe action already taken
	    else if(l >= LOOM1 && l < LOOM2 && reaction[i][j] > 1 && twoLevels) {
		reaction[i][j] = 1;
		resume2(sprites[i], 2);
		resume2(sprites[j], 2);
	    }
	    //Severe looming, take different action
	    else if(l >= LOOM2 && reaction[i][j] == 1 && twoLevels) {
		//store pre-looming velocity so we can resume it later
		sprites[i]._p2vx = sprites[i]._vx;
		sprites[i]._p2vy = sprites[i]._vy;
		behavior2(sprites[i], sprites[j], turnangle2);

		sprites[j]._p2vx = sprites[j]._vx;
		sprites[j]._p2vy = sprites[j]._vy;
		behavior2(sprites[j], sprites[i], turnangle2);

		reaction[i][j] = 2;
	    }
	    or[i][j] = nr[i][j];
	}
	//move one sprite
	move(sprites[i],i);
    }

}

function move(sprite, i) 
{
    if(sprite._vx || sprite._vy) {
	//check if goal is achieved for this sprite
	endcond(sprite,i);
	//check if this sprite hit the boundaries
	boundcond(sprite);
	sprite._x += sprite._vx;
	sprite._y += sprite._vy;    
	sprite.style.left = parseInt(sprite._x)+'px';
	sprite.style.top = parseInt(height - sprite._y - S_HEIGHT)+'px';
    }
}


//---------------------Specific Behaviors--------------------------

function nothing(sprite)
{
    return false;
}

function stop(sprite) 
{
    sprite._vx = 0; sprite._vy = 0;
}

function turnaway(sprite1, sprite2, turnangle) 
{
    var theta = phasorangle(sprite1._vx, sprite1._vy);
    var phi   = phasorangle(sprite2._x-sprite1._x, sprite2._y-sprite1._y);
    var speed = phasormag(sprite1._vx, sprite1._vy);
    if(theta > phi) {
	theta = theta + turnangle;
    }
    else {
	theta = theta - turnangle;
    }
    sprite1._vx = speed * Math.cos(theta);
    sprite1._vy = speed * Math.sin(theta);
}

function turnto(sprite1, sprite2, turnangle) 
{
    var theta = phasorangle(sprite1._vx, sprite1._vy);
    var phi   = phasorangle(sprite2._x-sprite1._x, sprite2._y-sprite1._y);
    var speed = phasormag(sprite1._vx, sprite1._vy);
    //    alert(theta+', '+phi);
    if(theta > phi) {
	theta = theta - turnangle;
    }
    else {
	theta = theta + turnangle;
    }
    sprite1._vx = speed * Math.cos(theta);
    sprite1._vy = speed * Math.sin(theta);
}

function turnopp(sprite1, sprite2) 
{
    var theta = phasorangle(sprite1._vx, sprite1._vy);
    var phi   = phasorangle(sprite2._x-sprite1._x, sprite2._y-sprite1._y);
    var speed = phasormag(sprite1._vx, sprite1._vy);
    theta = PI + phi;
    sprite1._vx = speed * Math.cos(theta);
    sprite1._vy = speed * Math.sin(theta);
}

function turnat(sprite1, sprite2) 
{
    var theta = phasorangle(sprite1._vx, sprite1._vy);
    var phi   = phasorangle(sprite2._x-sprite1._x, sprite2._y-sprite1._y);
    var speed = phasormag(sprite1._vx, sprite1._vy);
    theta = phi;
    sprite1._vx = speed * Math.cos(theta);
    sprite1._vy = speed * Math.sin(theta);
}

function slow(sprite) {
    var speed = phasormag(sprite._vx, sprite._vy);
    var theta = phasorangle(sprite._vx, sprite._vy);
    sprite._vx = (speed / 2) * Math.cos(theta);
    sprite._vy = (speed / 2) * Math.sin(theta);
}


//---------------------Collision Functions---------------------

function dieoncollide(sprite1, sprite2) 
{
    sprite1.src = 'sprite-crash.gif';
    sprite1._vx = 0; sprite1._vy = 0;
    sprite1._ovx = 0; sprite1._ovy = 0;
    sprite1._p1vx = 0; sprite1._p1vy = 0;
    sprite1._p2vx = 0; sprite1._p2vy = 0;

    sprite2.src = 'sprite-crash.gif';
    sprite2._vx = 0; sprite2._vy = 0;
    sprite2._ovx = 0; sprite2._ovy = 0;
    sprite2._p1vx = 0; sprite2._p1vy = 0;
    sprite2._p2vx = 0; sprite2._p2vy = 0;
}

function bounceoff(sprite1, sprite2)
{
    sprite1._vx = -sprite1._vx;
    sprite1._vy = -sprite1._vy;
    sprite2._vx = -sprite2._vx;
    sprite2._vy = -sprite2._vy;
}

//---------------------Resumption Functions----------------------

function original(sprite)
{ 
    sprite._vx = sprite._ovx;
    sprite._vy = sprite._ovy;
}

function prelooming(sprite, n)
{
    if(n==1) {
	sprite._vx = sprite._p1vx;
	sprite._vy = sprite._p1vy;
    }
    else if(n==2) {
	sprite._vx = sprite._p2vx;
	sprite._vy = sprite._p2vy;
    }
}


//-----------------Boundary Condition Functions------------------

function bounce(sprite)
{
	if((sprite._vx>0 && sprite._x+sprite._vx+S_WIDTH>=width) || (sprite._vx<0 && sprite._x+sprite._vx<=0)) sprite._vx *= -1;
	if((sprite._vy>0 && sprite._y+sprite._vy+S_HEIGHT>=height) || (sprite._vy<0 && sprite._y+sprite._vy<=0)) sprite._vy *= -1;
}    


//------------------Initial Condition Functions------------------

function top(sprite) 
{
    sprite._x = 500;
    sprite._y = 50;
    sprite._vx = 10;
    sprite._vy = 0;
}

function randomSprite(sprite) 
{
    sprite._x = rnd(width-S_WIDTH);
    sprite._y = rnd(height-S_HEIGHT);
    sprite._vx = (S_VMIN+rnd(S_VMAX-S_VMIN)) * rndDir();
    sprite._vy = (S_VMIN+rnd(S_VMAX-S_VMIN)) * rndDir();
}

function crossStreet(sprite, n) 
{
    var spacing = height / (2*NUM);
    if(n % 2) { //different for odd and even numbered sprites
	sprite._x = S_WIDTH;
	sprite._y = n*spacing + (height/4);
	sprite._vx = S_VMIN+rnd(S_VMAX - S_VMIN);
	sprite._vy = 0;
    }
    else {
	sprite._x = width - S_WIDTH;
	sprite._y = n*spacing + (height/4);
	sprite._vx = -(S_VMIN+rnd(S_VMAX - S_VMIN));
	sprite._vy = 0;
    }
}


//-----------------------End Condition Functions-----------------------

function streetCrossed(sprite, n)
{
    if(n % 2) { //different for odd and even numbered sprites
	if(sprite._x + sprite._vx >= (width - S_WIDTH)) {
	    sprite.src = 'sprite-satisfied.gif';
	    stop(sprite);
	}
    }
    else {
	if(sprite._x + sprite._vx <= 0) {
	    sprite.src = 'sprite-satisfied.gif';
	    stop(sprite);
	}
    }
}


//-----------------------Initialization Functions----------------------

function init() 
{
    getParams();
    getWindowCoords();
    collisions = 0;
    document.getElementById('counter').innerHTML = collisions;
    sprites = document.getElementById('sprites').getElementsByTagName('img');
    while(NUM < sprites.length) {  //Removes sprites till we get to the right number
	document.getElementById('sprites').removeChild(sprites[0]);
    }
    while(NUM > sprites.length) { //Adds sprites, etc.
	document.getElementById('sprites').appendChild(sprites[0].cloneNode(true));
	window.status = sprites.length;
    }
    for (i=0; i<sprites.length; i++) {
	sprites[i].src = 'sprite.gif';
	initcond(sprites[i], i);
	//store original velocity so we can resume it later
	sprites[i]._ovx = sprites[i]._vx;
	sprites[i]._ovy = sprites[i]._vy;
	move(sprites[i]);
    }
    for(i=0; i<sprites.length; i++) {
	for(j=i+1; j<sprites.length; j++) {
	    reaction[i][j] = 0;
	    collided[i][j] = 0;
	    or[i][j] = radius(sprites[i], sprites[j]);	    
	}
    }

    timer = setInterval(animate, 20);
}

function getParams() //gets all parameters from the html form initially
{
    if(document.controls.number.value) {
	NUM = document.controls.number.value;
	if(NUM > 99) {
	    NUM = 99;
	}
    }
    else {
	document.controls.number.value = NUM;
    }
        
    if(document.controls.levels.checked) {
	twoLevels = 1;
	document.getElementById('reaction2g').style.display = "block";
    }
    else {
	twoLevels = 0;
	document.getElementById('reaction2g').style.display = "none";
    }

    if(document.controls.loom1.value) {
	LOOM1 = document.controls.loom1.value;
    }
    else {
	document.controls.loom1.value = LOOM1;
    }

    if(document.controls.loom2.value) {
	LOOM2 = document.controls.loom2.value;
    }
    else {
	document.controls.loom2.value = LOOM2;
    }

    if(document.controls.angle1.value) {
	turnangle1 = (document.controls.angle1.value / 180) * PI;
    }
    else {
	document.controls.angle1.value = (turnangle1 / PI) * 180;
    }

    if(document.controls.angle2.value) {
	turnangle2 = (document.controls.angle2.value / 180) * PI;
    }
    else {
	document.controls.angle2.value = (turnangle2 / PI) * 180;
    }

    switch(document.controls.reaction1.selectedIndex) 
	{
	case 0: behavior1 = nothing;  break;
	case 1: behavior1 = stop;     break;
	case 2: behavior1 = turnaway; break;
	case 3: behavior1 = turnto;   break;	    
	case 4: behavior1 = slow;     break;
	case 5: behavior1 = turnopp;  break;
	case 6: behavior1 = turnat;   break;
	}

    switch(document.controls.reaction2.selectedIndex) 
	{
	case 0: behavior2 = nothing;  break;
	case 1: behavior2 = stop;     break;
	case 2: behavior2 = turnaway; break;
	case 3: behavior2 = turnto;   break;
	case 4: behavior2 = slow;     break;
	case 5: behavior1 = turnopp;  break;
	case 6: behavior1 = turnat;   break;
	}

    switch(document.controls.resume1.selectedIndex)
	{
	case 0: resume1 = nothing;    break;
	case 1: resume1 = original;   break;
	case 2: resume1 = prelooming; break;
	}

    switch(document.controls.resume2.selectedIndex)
	{
	case 0: resume2 = nothing;    break;
	case 1: resume2 = original;   break;
	case 2: resume2 = prelooming; break;
	}

    switch(document.controls.boundcond.selectedIndex) 
	{
	case 0: boundcond = bounce; break;
	}
    
    switch(document.controls.initcond.selectedIndex)
	{
	case 0: initcond = randomSprite; break;
	case 1: initcond = crossStreet;  break;
	}

    switch(document.controls.endcond.selectedIndex) 
	{
	case 0: endcond = nothing;       break;
	case 1: endcond = streetCrossed; break;
	}

    switch(document.controls.colcond.selectedIndex) 
	{
	case 0: collide = nothing;      break;
	case 1: collide = dieoncollide; break;
	case 2: collide = bounceoff;    break;
	}

}

//gets window dimensions initially or if the window is resized
getWindowCoords = (navigator.userAgent.toLowerCase().indexOf('opera')>0||navigator.appVersion.toLowerCase().indexOf('safari')!=-1)?function() 
{
    var canvasX = window.innerWidth;
    var canvasY = window.innerHeight;
    width = canvasX - CONTROLS - 42;
    height = canvasY - 42;
    document.getElementById('sprites').style.width = width+'px';
    document.getElementById('sprites').style.height = height+'px';
}:function () {
    var canvasX = document.documentElement.clientWidth||document.body.clientWidth||document.body.scrollWidth;
    var canvasY = document.documentElement.clientHeight||document.body.clientHeight||document.body.scrollHeight;
    width = canvasX - CONTROLS - 42;
    height = canvasY - 42;
    document.getElementById('sprites').style.width = width+'px';
    document.getElementById('sprites').style.height = height+'px';
}


//--------------Functions for Handling Controls----------------

function restartAnimation()
{
    clearInterval(timer);
    timer = null;
    init();
}

function pauseAnimation() 
{
    if(!timer) {
	timer = setInterval(animate, 20);
	return false;
    }
    else {
	clearInterval(timer);
	timer = null;
    }
}

function changeLevels(levels) 
{
    if(levels) {
	document.getElementById('reaction2g').style.display = "block";
    }
    else {
	document.getElementById('reaction2g').style.display = "none";
    }
}

//-----------------------Program Start-------------------------

window.onresize = getWindowCoords;
window.onload = init;
