/*
    Global EventBus
*/
EventBus = $({});

/*
    Safely resolve a property chain
*/
Object.resolvePropertyChain = function(object, chain) {
    var i = 0;
    do {
        object=object[chain[i++]];
    } while((typeof object != "undefined") && (i < chain.length))

    return object;
}

/*
    NOP Function
*/
Function.nop = function() {
    return true;
}

Function.nopIfNull = function(fx) {
    return (typeof fx == "function") ? fx : Function.nop;
}

/*
    Degrees to Radians  Function
*/
Math.deg2rad = function(deg) {
    return deg*Math.PI/180;
}

/*
    Attach a @font-face to the page
*/
function addCSS(uri) {
    var cssNode = document.createElement('link');
        cssNode.setAttribute('rel', 'stylesheet');
        cssNode.setAttribute('type', 'text/css');
        cssNode.setAttribute('href', uri);
    document.getElementsByTagName('head')[0].appendChild(cssNode);
}

/*
    Function Queue
*/
function FunctionQueue() {
    this.queue=[];
}

FunctionQueue.prototype.push = function(fx) {
    var self = this;
    var idx = this.queue.length;

    this.queue.push(function(onDone) {
        var next = self.queue[idx+1];
        if(next) {
            fx(function() {
                next(onDone);
            });
        } else {
            fx(onDone);
        }
    });

    return self;
}

FunctionQueue.prototype.run = function(callback) {
    if(!callback) {
        callback = function() {}
    }
    if(this.queue.length != 0) {
        this.queue[0](callback);
    } else {
        callback();
    }
}

/*
    String.format
*/
String.prototype.format = function(map) {
	var str = this;
	for(var idx in map) {
        str = str.replace(
            new RegExp('{'+idx+'}',"gi"),
            map[idx]
        );
    }
	return str;
}

/*
    String.capitalize
*/
String.prototype.capitalize = function() {
    return this.replace(/^\w/, function($0) { return $0.toUpperCase(); })
}

/*
    Widget
*/
function Widget(selector) {
    this.rootEl=$(selector);
    this.isActive=false;
    this.isRendered=false;
}

Widget.prototype.activate = function(callback) {
    this.onActivated(callback);
}

Widget.prototype.onActivated = function(callback) {
    this.isActive=true;

    Function.nopIfNull(callback)();
}

Widget.prototype.onAfterRender = function() {
    this.isRendered=true;
}

Widget.prototype.deactivate = function(callback) {
    this.onDeactivated(callback);
}

Widget.prototype.onDeactivated = function(callback) {
    this.isActive=false;
    this.isRendered=false;

    Function.nopIfNull(callback)();
}

/*
    Panel Widget
*/
function Panel(selector) {
    Widget.call(this,selector);

    this.children=[];
    this.activationQueue=new FunctionQueue();
    this.deactivationQueue=new FunctionQueue();
}

Panel.prototype.initialize = function() {
    $.log("Panel.initialize...");
}

Panel.prototype.addChild = function(widget) {
    this.children.push(widget);

    this.activationQueue.push(
        this.newActivationCallback(widget)
    );

    this.deactivationQueue.push(
        this.newDeactivationCallback(widget)
    );
}

Panel.prototype.newActivationCallback = function(widget) {
    return function(callback) {
        $.log("Panel.activate",widget);
        widget.activate(callback);
    };
}

Panel.prototype.newDeactivationCallback = function(widget) {
    return function(callback) {
        $.log("Panel.deactivate",widget);
        widget.deactivate(callback);
    };
}

Panel.prototype.activate = function(callback) {
    var self = this;
    this.activationQueue.run(function() {
        self.doActivate(callback);
    });
}

Panel.prototype.doActivate = function(callback) {
    $.log("Panel.doActivate");
    Function.nopIfNull(callback)();
}

Panel.prototype.deactivate = function(callback) {
    var self = this;
    this.deactivationQueue.run(function() {
        self.doDeactivate(callback);
    });
}

Panel.prototype.doDeactivate = function(callback) {
    $.log("Panel.doDeactivate");
    Function.nopIfNull(callback)();
}

/*
    A very basic CountDownLatch
*/
function CountDownLatch(count) {
    this.count=count;
}

CountDownLatch.prototype.release = function() {
    if(--this.count == 0) {
        this.onReleased();
        return true;
    } else {
        return false;
    }
}

CountDownLatch.prototype.onReleased = function() {
    $.log("CountDownLatch.onReleased")
}

/*
    Image Utilities
*/
Images = {
    replaceOnError : function(img,url) {
        img.onerror=null;
        img.src=url;
    }
};

