/**
 * @author Patrick Kopp
 */

/**
 *
 * @param {Number} x
 * @param {Number} y
 */
function Point(x, y){

  this.checkArgs(x, "x");
  this.checkArgs(y, "y");

  this.x = Math.round(x);
  this.y = Math.round(y);

  this.toString = function(){
    return "Point: (" + this.x + "/" + this.y + ")";
  }

  /**
   * Returns distance to an other point
   * @param {Point} otherPoint
   */
  this.getDistance = function(otherPoint){

    var dX = this.x - otherPoint.x;
    var dY = this.y - otherPoint.y;
    var ret = Math.sqrt(Math.pow(dX, 2) + Math.pow(dY, 2));

    if(isNaN(ret)) {
      throw new Error("Distance is not a number: " + otherPoint.x + " " + otherPoint.y);
    }

    return ret;

  }

  this.getY = function(y){
    return this.y;
  }

  this.getX = function(x){
    return this.x;
  }

  this.newPoint = function(xOffset, yOffset) {
    this.checkArgs(xOffset, "xOffset");
    this.checkArgs(yOffset, "yOffset");
    return new Point(this.x + xOffset, this.y + yOffset);
  }

  this.clone = function() {
    return new Point(this.x, this.y);
  }

  this.add = function(point) {
    return new Point(this.x + point.x, this.y + point.y);
  }

  this.subtract = function(point) {
    return new Point(this.x - point.x, this.y - point.y);
  }

}

Point.prototype.setPosition = function(point){
  this.x = point.x;
  this.y = point.y;
}

Point.prototype.setY = function(y){
  this.checkArgs(y, "y");
  this.y = Math.round(y);
}

Point.prototype.setX = function(x){
  this.checkArgs(x, "x");
  this.x = Math.round(x);
}

Point.prototype.checkArgs = function(value, name) {
  if(isNaN(value)) {
//    throw new Error(name + " is not a number\nCalled by: " + checkArg.caller());
    throw new Error(name + " is not a number, type: " + (typeof value) + ", value: " + value);
  }
}

function ComplexPoint(x, y){
  this.Point(x, y);
  this.listeners = [];
}

copyPrototype(ComplexPoint, Point);

ComplexPoint.prototype.addListener = function(listerner) {
  this.listeners.push(listerner);
}

ComplexPoint.prototype.setX = function(x){
  Point.prototype.setX.apply(this, arguments);
  this.notifyListeners();
}

ComplexPoint.prototype.setY = function(y){
  Point.prototype.setY.apply(this, arguments);
  this.notifyListeners();
}

ComplexPoint.prototype.setPosition = function(point){
  Point.prototype.setPosition.apply(this, arguments);
  this.notifyListeners();
}

ComplexPoint.prototype.notifyListeners = function(){

  for(var i = 0; i < this.listeners.length; i++) {
    var listener = this.listeners[i];
    listener.pointChanged(this);
  }
}

function Debug(window) {
  this.window = window;
  this.document = this.window.document;
  this.document.open();
  this.document.write("<pre>");
}

Debug.prototype.debug = function(message) {

    this.document.write(message + "\n");
}

Debug.prototype.close = function(message) {

    this.document.write("</pre>");
  this.document.close();
}

function copyPrototype(descendant, parent) {

  var sConstructor = parent.toString();
  var aMatch = sConstructor.match( /\s*function (.*)\(/ );
  if ( aMatch != null ) { descendant.prototype[aMatch[1]] = parent; }
  for (var m in parent.prototype) {
    descendant.prototype[m] = parent.prototype[m];
  }
}

WindowDimensions = Class.create();
WindowDimensions.prototype = {

 win: null,
 width: null,
 height: null,
 centerX: null,
 centerY: null,
 listeners: new Array(),

  determineDimensions: function(mustNotifyListeners){
  var d = this.win.document;
    if(this.win.innerHeight && this.win.innerWidth) {
      this.height  = this.win.innerHeight;
      this.width   = this.win.innerWidth;
    } else if (d.documentElement && d.documentElement.clientWidth) {
      this.width   = d.documentElement.clientWidth;
      this.height  = d.documentElement.clientHeight;
    } else {
      this.height  = d.body.clientHeight;
      this.width   = d.body.clientWidth;
    }

  	this.centerX = Math.round(this.width / 2);
  	this.centerY = Math.round(this.height / 2);

	if(mustNotifyListeners) {
	  for(var i = 0; i < this.listeners.length; i++) {
	    var listener = this.listeners[i];
	    listener.windowChanged(this);
	  }
	}
 },

 initialize: function(w){
  this.win = w;
  this.determineDimensions(false);
  this.win.onresize = function() {
    this.determineDimensions(true);
  }.bindAsEventListener(this);
 },

 getPoint: function(xInPercent, yInPercent){
    var xCoord = this.width / 100 * xInPercent;
    var yCoord = this.height / 100 * yInPercent;
    return new Point(xCoord, yCoord);
 },
 getCenterPoint: function(){
	return new Point(this.centerX, this.centerY);
 },

 getTopRightPoint: function() {
	return new Point(this.width, 0);
 },
 getTopLeftPoint: function() {
	return new Point(0, 0);
 },
 getBottomRightPoint: function() {
	return new Point(this.width, this.height);
 },
 getBottomLeftPoint: function() {
	return new Point(0, this.height);
 },

 addListener: function(listener) {
	this.listeners.push(listener);
  }
}



/**
 * @param {Point} begin
 * @param {Point} end
 */
function Line(begin, end) {

  this.begin = begin;
  this.end = end;
  this.distance = begin.getDistance(end);
  this.alpha = Math.acos(Math.abs(begin.x - end.x) / this.distance);

  this.xMultiplier = (begin.x > end.x) ? -1 : 1;
  this.yMultiplier = (begin.y > end.y) ? -1 : 1;

}

Line.prototype.getPoints = function(amount) {

    var result = new Array();
    result.push(new Point(this.begin.x, this.begin.y));
    var myAmount = amount - 1;

    for(var i = 1; i < myAmount; i++) {

      var hypotenuse = i * this.distance / myAmount;
      var x = Math.cos(this.alpha) * hypotenuse * this.xMultiplier;
      var y = Math.sin(this.alpha) * hypotenuse * this.yMultiplier;

      result.push(new Point(this.begin.x + x, this.begin.y + y));
    }

    result.push(new Point(this.end.x, this.end.y));

    return result;
}