/*
   =================================================================
	script: pong.html (schizophrenic version)
	author: Gerard Ferrandez - [Ge1doot]
	date: May 12, 2008
	site: http://www.dhteumeuleu.com
	----------------------------------------------------------------
	sound manager by Scott Schiller at http://www.schillmania.com
   =================================================================
*/

var isIE = navigator.appName.toLowerCase().indexOf('internet explorer')+1;
var isMac = navigator.appVersion.toLowerCase().indexOf('mac')+1;

function SoundManager(container) {
  // DHTML-controlled sound via Flash
  var self = this;
  this.movies = []; // movie references
  this.container = container;
  this.unsupported = 0; // assumed to be supported
  this.defaultName = 'default'; // default movie
  
  this.FlashObject = function(url) {
    var me = this;
    this.o = null;
    this.loaded = false;
    this.isLoaded = function() {
      if (me.loaded) return true;
      if (!me.o) return false;
      me.loaded = ((typeof(me.o.readyState)!='undefined' && me.o.readyState == 4) || (typeof(me.o.PercentLoaded)!='undefined' && me.o.PercentLoaded() == 100));
      return me.loaded;
    }
    this.mC = document.createElement('div');
    this.mC.className = 'movieContainer';
    with (this.mC.style) {
      // "hide" flash movie
      position = 'absolute';
      left = '-256px';
      width = '64px';
      height = '64px';
    }
    var html = ['<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0"><param name="movie" value="'+url+'"><param name="quality" value="high"></object>','<embed src="'+url+'" width="1" height="1" quality="high" pluginspage="http://www.macromedia.com/go/getflashplayer" type="application/x-shockwave-flash"></embed>'];
    if (navigator.appName.toLowerCase().indexOf('microsoft')+1) {
      this.mC.innerHTML = html[0];
      this.o = this.mC.getElementsByTagName('object')[0];
    } else {
      this.mC.innerHTML = html[1];
      this.o = this.mC.getElementsByTagName('embed')[0];
    }
    document.getElementsByTagName('div')[0].appendChild(this.mC);
  }

  this.addMovie = function(movieName,url) {
    self.movies[movieName] = new self.FlashObject(url);
  }

  this.checkMovie = function(movieName) {
    movieName = movieName||self.defaultName;
    if (!self.movies[movieName]) {
      self.errorHandler('checkMovie','Exception: Could not find movie',arguments);
      return false;
    } else {
      return (self.movies[movieName].isLoaded())?self.movies[movieName]:false;
    }
  }

  this.errorHandler = function(methodName,message,oArguments,e) {
    writeDebug('<div class="error">soundManager.'+methodName+'('+self.getArgs(oArguments)+'): '+message+(e?' ('+e.name+' - '+(e.message||e.description||'no description'):'')+'.'+(e?')':'')+'</div>');
  }

  this.play = function(soundID,loopCount,noDebug,movieName) {
    if (self.unsupported) return false;
    movie = self.checkMovie(movieName);
    if (!movie) return false;
    if (typeof(movie.o.TCallLabel)!='undefined') {
      try {
        self.setVariable(soundID,'loopCount',loopCount||1,movie);
        movie.o.TCallLabel('/'+soundID,'start');
        if (!noDebug) writeDebug('soundManager.play('+self.getArgs(arguments)+')');
      } catch(e) {
        self.errorHandler('play','Failed: Flash unsupported / undefined sound ID (check XML)',arguments,e);
      }
    }
  }

  this.stop = function(soundID,movieName) {
    if (self.unsupported) return false;
    movie = self.checkMovie(movieName);
    if (!movie) return false;
    try {
      movie.o.TCallLabel('/'+soundID,'stop');
      writeDebug('soundManager.stop('+self.getArgs(arguments)+')');
    } catch(e) {
      // Something blew up. Not supported?
      self.errorHandler('stop','Failed: Flash unsupported / undefined sound ID (check XML)',arguments,e);
    }
  }

  this.getArgs = function(params) {
    var x = params?params.length:0;
    if (!x) return '';
    var result = '';
    for (var i=0; i<x; i++) {
      result += (i&&i<x?', ':'')+(params[i].toString().toLowerCase().indexOf('object')+1?typeof(params[i]):params[i]);
    }
    return result
  }

  this.setVariable = function(soundID,property,value,oMovie) {
    // set Flash variables within a specific movie clip
    if (!oMovie) return false;
    try {
      oMovie.o.SetVariable('/'+soundID+':'+property,value);
      // writeDebug('soundManager.setVariable('+self.getArgs(arguments)+')');
    } catch(e) {
      // d'oh
      self.errorHandler('setVariable','Failed',arguments,e);
    }
  }

  this.setVariableExec = function(soundID,fromMethodName,oMovie) {
    try {
      oMovie.o.TCallLabel('/'+soundID,'setVariable');
    } catch(e) {
      self.errorHandler(fromMethodName||'undefined','Failed',arguments,e);
    }
  }

  this.callMethodExec = function(soundID,fromMethodName,oMovie) {
    try {
      oMovie.o.TCallLabel('/'+soundID,'callMethod');
    } catch(e) {
      // Something blew up. Not supported?
      self.errorHandler(fromMethodName||'undefined','Failed',arguments,e);
    }
  }

  this.callMethod = function(soundID,methodName,methodParam,movieName) {
    movie = self.checkMovie(movieName||self.defaultName);
    if (!movie) return false;
    self.setVariable(soundID,'jsProperty',methodName,movie);
    self.setVariable(soundID,'jsPropertyValue',methodParam,movie);
    self.callMethodExec(soundID,methodName,movie);
  }

  this.setPan = function(soundID,pan,movieName) {
    self.callMethod(soundID,'setPan',pan,movieName);
  }

  this.setVolume = function(soundID,volume,movieName) {
    self.callMethod(soundID,'setVolume',volume,movieName);
  }

  // constructor - create flash objects

  if (isIE && isMac) {
    this.unsupported = 1;
  }

  if (!this.unsupported) {
    this.addMovie(this.defaultName,'soundcontroller.swf');
    // this.addMovie('rc','rubber-chicken-audio.swf');
  }

}

function SoundManagerNull() {
  // Null object for unsupported case
  this.movies = []; // movie references
  this.container = null;
  this.unsupported = 1;
  this.FlashObject = function(url) {}
  this.addMovie = function(name,url) {}
  this.play = function(movieName,soundID) {
    return false;
  }
  this.defaultName = 'default';
}

function writeDebug(msg) {
  var o = document.getElementById('debugContainer');
  if (!o) return false;
  var d = document.createElement('div');
  d.innerHTML = msg;
  o.appendChild(d);
}

var soundManager = null;

function soundManagerInit() {
  soundManager = new SoundManager();
}

var js = {
	/* ===== crossbrowsers events ==== */
	addEvent : function ( obj, type, fn ) {
        if (obj.addEventListener) obj.addEventListener(type, fn, false);
        else if (obj.attachEvent) obj.attachEvent('on' + type, function() { return fn.call(obj, window.event); });
	},
	/* ===== create JS Object ==== */
	JSObject : function (att) {
		var object = new Object();
		for( var i in att) object[i] = att[i];
		return object;
	},
	/* ===== append HTML Element ==== */
	Append : function (tag, att, css) {
		var object = document.createElement(tag);
		for( var i in att) object[i] = att[i];
		for( var i in css) object.style[i] = css[i];
		this.appendChild(object);
		object.append = js.Append;
		return object;
	},
	/* ===== soundify a href links ==== */
	soundifyLinks : function (container, over, clic){
		if (container){
			var l = container.getElementsByTagName('a');
			for(var i = 0, n = l.length; i < n; i++){
				this.addEvent(l[i], 'click', new Function("soundManager.play('"+clic+"');"));
				this.addEvent(l[i], 'mouseover', new Function("soundManager.play('"+over+"');"));
			}
		}
	}
}


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////




var pong = function() {
	/* ==== private variables and methods ==== */
	///////////////////////
	var acc = 1.01;
	var speed = .025;
	var angle = 15;
	///////////////////////
	var screen = new js.JSObject({left:0, width:0, height:0, scale_x:0, scale_y:0});
	var ball = new js.JSObject({vx:0, vy:0, x:0, y:0, w:0});
	var pad = [
				new js.JSObject({y:0, s:1, ping:Ping, x:function(){return screen.scale_x * 2;}}),
				new js.JSObject({y:0, s:-1, ping:Ping, x:function(){return screen.width - screen.scale_x * 3;}})
	];
	var time_p = new Date();
	var score = [
				new js.JSObject({s:0, d:-1}),
				new js.JSObject({s:0, d:1}),
				new js.JSObject({s:0, d:-1}),
				new js.JSObject({s:0, d:1})
	];
	var sp;
	var player = 0;

	/* ===== window resize ==== */
	function resize () {
		/* ---- screen position/size ---- */
		var o = screen.div;
		for (screen.left = 0; o != null; o = o.offsetParent) screen.left += o.offsetLeft;
		screen.width = screen.div.offsetWidth;
		screen.height = screen.div.offsetHeight;
		/* ---- scale pixels size ---- */
		screen.scale_x = screen.width * .03;
		screen.scale_y = screen.height * .25;
		/* ---- ball and pads position/size ---- */
		ball.style.width = ball.style.height = Math.round(screen.scale_x) + 'px';
		pad[0].style.width = pad[1].style.width = Math.round(screen.scale_x) + 'px';
		pad[0].style.height = pad[1].style.height = Math.round(screen.scale_y) + 'px';
		pad[0].style.left = Math.round(screen.scale_x) + "px";
		pad[1].style.left = Math.round(screen.width - screen.scale_x * 2) + "px";
		/* ---- scores ---- */
		for (var i = 0; i < 4; i++) {
			var o = score[i].div.style;
			o.width = o.height = Math.round(3 * screen.scale_x) + 'px';
			o.left = Math.round(score[i].d > 0 ? screen.width * .5 + 2 * screen.scale_x : screen.width * .4 - 2 * screen.scale_x) + 'px';
			o.top  = Math.round(i < 2 ? screen.scale_x : screen.height - 4 * screen.scale_x) + 'px';
		}
		/* ---- middle line ---- */
		sp.style.width = Math.round(screen.scale_x) + 'px';
		sp.style.left = Math.round(screen.width * .5 - screen.scale_x * .5) + 'px';
		sp.innerHTML = "";
		for (var i = 0; i < screen.height; i += screen.scale_x * 2)
			sp.append('span', false, {
				'top':Math.round(i) + 'px',
				'width':Math.round(screen.scale_x) + 'px',
				'height':Math.round(screen.scale_x) + 'px'
			});
	}
	/* ---- bounce on pad ---- */
	function Ping() {
		if (ball.y + screen.scale_x > this.y && ball.y < this.y + screen.scale_y) {
			/* ---- play sound first and return (mitigate latency) ---- */
			if (!ball.sound) {
				soundManager.play('pong1');
				ball.sound = true;
				ball.x = this.x();
				return;
			}
			ball.sound = false;
			/* ---- inc scores ---- */
			score[2].s++;
			chrono(score[2]);
			if(score[2].s > score[3].s) {
				score[3].s = score[2].s;
				chrono(score[3]);
			}
			/* ---- reverse direction and accelerate ---- */
			ball.vx = -ball.vx * acc;
			ball.x = this.x();
			/* ---- beware of the corners! ---- */
			if (ball.y < this.y) ball.vy = ball.vx * -this.s * angle + Math.random() * .1;
			else if (ball.y > this.y + screen.scale_y - screen.scale_x) ball.vy = ball.vx * this.s * angle  + Math.random() * .1;
			/* ---- angle ---- */
			else ball.vy = (Math.random() - .5) * ball.vx * angle;
		}
	}
	/* ---- player won ---- */
	function win(p) {
		soundManager.play('pong3');
		score[1 - p].s++;
		chrono(score[1 - p]);
		score[2].s = 0;
		chrono(score[2]);
		player = p;
	}
	/* ---- draw numeric digits ---- */
	function chrono(score) {
		var dir = score.d;
		var txt = score.s + '';
		score.div.innerHTML = '';
		var a = [119,36,93,109,46,107,123,37,127,111];
		var s = {1:[0,0,99,20],2:[0,0,33,45],4:[67,0,33,45],8:[0,40,100,20],16:[0,40,33,60],32:[67,40,33,60],64:[0,80,99,20]};
		for (var i = 0, n = txt.length; i < n; i++) {
			var c = txt.charAt(dir > 0 ? i : n - i - 1);
			var p = a[c];
			if (p) {
				var d = score.div.append('div', false, {'height':'100%','width':'100%','left':(120 * i * dir) + '%'});
				for (var j in s) {
					var k = s[j];
					if (p & j) d.append('span', false, {'left':k[0]+'%','top':k[1]+'%','width':k[2]+'%','height':k[3]+'%'});
				}
			}
		}
	}
	/* ==== public methods ==== */
	return {
		/* ---- initialize script ---- */
		init : function (container) {
			/* ---- container ---- */
			screen.div = document.getElementById(container);
			if (!screen.div) return false;
			screen.div.append = js.Append;
			/* ---- pads and ball ---- */
			pad[0].style = screen.div.append('span').style;
			pad[1].style = screen.div.append('span').style;
			ball.style = screen.div.append('span').style;
			/* ---- scores ---- */
			for (var i = 0; i < 4; i++) {
				score[i].div = screen.div.append('div');
				chrono(score[i]);
			}
			/* ---- center line ---- */
			sp = screen.div.append('div', false, {'height':'100%'});
			/* ---- do resize ---- */
			resize();
			/* ---- move pads ---- */
			js.addEvent(screen.div, 'mousemove', function(e) {
				e = e || window.event;
				var y = screen.height - screen.scale_y;
				var x = (e.clientX - screen.left) / screen.width;
				pad[0].y = x * y;
				pad[1].y = (1 - x) * y;
				return false;
			});
			/* ---- launch ball ---- */
			js.addEvent(screen.div, 'click', function(e) {
				if (player >= 0) {
					soundManager.play('pong1');
					ball.vx = screen.scale_x * speed;
					ball.vy = (Math.random() - .5) * screen.height * .01;
					if (player == 1) ball.vx = -ball.vx;
					player = -1;
				}
			});
			/* ---- window resize event ---- */
			js.addEvent(window, 'resize', resize);
			/* ---- launch script ---- */
			setInterval(pong.run, 16);
		},
		/* ==== main loop ==== */
		run : function () {
			/* ---- ball movment ---- */
			var time = new Date();
			var vx = ball.vx * (time - time_p);
			if (player < 0) {
				/* ---- playing ---- */
				ball.x += vx;
				ball.y += ball.vy;
			} else {
				/* ---- waiting ---- */
				ball.x = pad[player].x();
				ball.y = pad[player].y + screen.scale_y * .5 - screen.scale_x * .5;
			}
			time_p = time;
			/* ---- test collisions ---- */
			if (ball.vx > 0) {
				/* ---- right player lost ---- */
				if (ball.x > screen.width + screen.scale_x) win(1);
				/* ---- bounce pad ---- */
				else if (ball.x > screen.width - screen.scale_x * 3) pad[1].ping();
			} else {
				/* ---- left player lost ---- */
				if (ball.x < -screen.scale_x) win(0);
				/* ---- bounce pad ---- */
				else if (ball.x < screen.scale_x * 2) pad[0].ping();
			}
			/* ---- collision top / bottom ---- */
			if (ball.y > screen.height - screen.scale_x || ball.y < 0) {
				soundManager.play('pong2');
				ball.vy = -ball.vy;
			}
			/* ---- animate HTML elements ---- */
			pad[0].style.top  = Math.round(pad[0].y) + 'px';
			pad[1].style.top  = Math.round(pad[1].y) + 'px';
			ball.style.left   = Math.round(ball.x) + 'px';
			ball.style.top    = Math.round(ball.y) + 'px';
		}
	}
}();
/* ==== global onload ==== */
onload = function () {
	/* ---- init sounds ---- */
	soundManagerInit();
	js.soundifyLinks(document.body, 'link', 'select');
	/* ---- init script ---- */
	pong.init('screen');
}
