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

    this.checkArgs(x, "x", arguments.callee.caller);
    this.checkArgs(y, "y", arguments.callee.caller);
    
    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) {
    
        if (otherPoint == null) {
            throw new Error("Null argument");
        }
        
        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);
    }
    
    this.getPosition = function() {
        return this;
    }
}

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, culprit) {
    
    if (isNaN(value)) {
        var culpritName = culprit ? ", caller: " + culprit.toString() : "";
        throw new Error(name + " is not a number, type: " + (typeof value) + ", value: " + value + culpritName);
    }
}

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 SimpleRange(min, max) {
    
    this.min = min;
    this.max = max;
}

SimpleRange.prototype.getMin = function() {
    
    return this.min;
}

SimpleRange.prototype.getMax = function() {
    
    return this.max;
}

SimpleRange.prototype.isWithin = function(candidate) {
    
    return candidate >= this.min && candidate <= this.max;
}

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);
    },
    
    getWidth: function() {
        return this.width;
    },
    
    getHeight: function() {
        return this.height;
    },    

    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;
}

