/**
 * 
 * All content on this website (including text, images, source
 * code and any other original works), unless otherwise noted,
 * is licensed under a Creative Commons License.
 * 
 * http://creativecommons.org/licenses/by-nc-sa/2.5/
 * 
 * Copyright (C) 2004-2009 Open-Xchange, Inc.
 * Mail: info@open-xchange.com 
 * 
 * @author Matthias Biggeleben <matthias.biggeleben@open-xchange.com>
 * @ignore
 */

// DEVELOPMENT IN PROGRESS // SUBJECT TO PERMANENT CHANGE!

ox.gui.Container = ox.gui.Widget.extend({
/** @lends ox.gui.Container.prototype */
    
    /** 
     * The basic container widget
     * @constructs
     * @extends ox.gui.Widget
     * @param {String} id An optional unique id for the widget.
     */
    construct: function(id) {

        // helps the layout manager finding the proper DOM node
        this.kindergarten = null;

        // the family
        /** An array of widgets added to this container */
        this.children = [];
        /** The first child */
        this.firstChild = null;
        /** The last child */
        this.lastChild = null;

        /** The layout manager of the container */
        this.layoutManager = null;

        // call super class constructor
        this._super(this.autoId(id, "container"));

        // reset target node
        this.kindergarten = this.dom.node;
    },

    getClass: function() { return "ox.gui.Container"; },
    
    nodeInit: function() {
        // set specific style
        this.setStyle({ overflow: "hidden" });
    },

    /**
     * Adds a widget to the container
     * @param {Widget} widget The widget to be added.
     */
    add: function(widget) {
        var children = this.getChildren();
        // add to list
        children.push(widget);
        // set position
        widget.position = children.length - 1;
        this.addPost(widget);
        // support chaining
        return this;
    },
    
    /*
     * @private
     */
    addPost: function (widget) {
        // is moved?
        if (widget.parent !== null) {
            widget.parent.removeChild(widget);
        }
        // first child?
        if (this.firstChild === null) {
            this.firstChild = widget;
        }
        // last child
        this.lastChild = widget;
        // set parent widget
        widget.parent = this;
        // invalidate
        this.invalidateFamily();
    },
    
    addRange: function(list) {
        var $i = 0, $l = list.length;
        for (; $i < $l; $i++) {
            this.add(list[$i]);
        }
        return this;
    },
    
    /**
     * Inserts a widget at a specific position
     * @param {Widget} widget The widget to be inserted.
     * @param {Int} index The position. Default=0.
     */
    insert: function(widget, index) {
        var children = this.getChildren();
        // adjust index
        index = index || 0;
        // add
        children.splice(index, 0, widget);
        // first child?
        if (index === 0) {
            this.firstChild = widget;
        }
        // set parent widget
        widget.parent = this;
        // set new positions
        var $c=this.getChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
            child.position = $i;
        }
        this.lastChild = $c[$c.length-1] || null;
        // invalidate
        this.invalidateFamily();
        // support chaining
        return this;
    },
    
    /**
     * Removes all children.
     */
    removeChildren: function() {
        this.firstChild = null;
        this.lastChild = null;
        // remove from DOM
        var $c=this.getChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
            child.removeFromDOM();
        }
        // remove
        var children = this.getChildren();
        children.splice(0, this.numChildren());
        // invalidate
        this.invalidateFamily();
        // support chaining
        return this;
    },
    
    moveChild: function(widget, index) {
        var $c=this.getChildren(),$i=$c.length,child; for(;$i--;) { child=$c[$i]; // fastest loop
            if (child == widget) {
                // remove child
                $c.splice($i, 1);
                // add child
                $c.splice(index, 0, widget);
                // set new positions
                $c=this.getChildren();$i=$c.length; for(;$i--;) { child=$c[$i]; // fastest loop
                    child.position = $i;
                }
                break;
            }
        }
    },
    
    removeChild: function(widget) {
        var $c=this.getChildren(),$i=$c.length,child; for(;$i--;) { child=$c[$i]; // fastest loop
            if (child == widget) {
                // remove from DOM
                child.removeFromDOM();
                // remove child
                $c.splice($i, 1);
                // set new positions
                $c=this.getChildren();$i=$c.length; for(;$i--;) { child=$c[$i]; // fastest loop
                    child.position = $i;
                }
                // invalidate
                this.invalidateFamily();
                break;
            }
        }
        return this;
    },
    
    /**
     * Removes this widget and all its children from the DOM.
     */
    removeFromDOM: function() {
        // remove all children
        var $c=this.getChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
            child.removeFromDOM();
        }
        // remove this
        ox.gui.Widget.prototype.removeFromDOM.call(this);
        // support chaining
        return this;
    },
    
    indexOf: function(widget) {
        var $c=this.getChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
            if (child == widget) {
                return $i;
            }
        }
        return -1;
    },
    
    hasChild: function(widget) {
        return this.indexOf(widget) != -1;
    },
    
    replace: function(formerWidget, newWidget) {
        var pos = this.indexOf(formerWidget);
        if (pos != -1) {
            // replace
            this.removeChild(formerWidget);
            this.insert(newWidget, pos);
            // destroy former widget
            formerWidget.destroy();
            return true;
        } else {
            return false;
        }
    },
    
    /**
     * Returns all children. Overridden by special widgets like ox.gui.MenuStrip.
     */
    getChildren: function() {
        return this.children; 
    },
    
    /**
     * Returns all visible children.
     */
    getVisibleChildren: function() {
        var i = 0, $c = this.getChildren(), $l = $c.length, tmp = [], child;
        for (; i < $l; i++) {
            child = $c[i];
            if (child.statusVisible === true) {
                tmp.push(child);
            }
        }
        return tmp; 
    },

    /**
     * Returns the number of children.
     */
    numChildren: function() {
        return this.getChildren().length;
    },

    /**
     * Determines whether or not the container has children.
     */
    hasChildren: function() {
        return this.getChildren().length > 0;
    },
    
    /*
     * Iterates over all children.
     * @param {Function} callback A callback function called for each child.
     * The function will receive three parameters: 1) The current child,
     * 2) the current index, 3) the number of children.
     */
    each: function(callback) {
        // bind callback to current class
        callback = $.proxy(callback, this);
        // get children
        var children = this.getChildren();
        var childCount = children.length;
        // loop children
        for (var i = 0; i < childCount; i++) {
            callback(children[i], i, childCount);
        }
    },
    
    /*
     * Iterates over all visible children.
     * @param {Function} callback A callback function called for each child.
     * The function will receive three parameters: 1) The current child,
     * 2) the current index, 3) the number of children.
     */
    eachVisible: function (callback) {
        if (callback) {
            // get children
            var children = this.getVisibleChildren();
            var i = 0, $l = children.length;
            // loop children
            for (; i < $l; i++) {
                callback.call(this, children[i], i, $l);
            }
        }
    },
    
    /**
     * Sets the layout manager
     * @param {ox.gui.LayoutManager} layoutManager The layout manager for this container.
     */
    setLayout: function(layoutManager) {
        this.layoutManager = layoutManager || new ox.gui.BlockFlowLayout(this);
        if (this.layoutManager.container === null) {
            this.layoutManager.container = this;
        }
        // allow chaining
        return this;
    },
    
    /**
     * @private
     */
    requireLayout: function() {
        if (this.layoutManager === null) {
            // add default manager
            this.layoutManager = new ox.gui.BlockFlowLayout(this);
        } else if (this.layoutManager.container === null) {
            // update reference
            this.layoutManager.container = this;
        }
    },
    
    /**
     * Clears the container
     * @private
     */
    clear: function() {
        // loop children
        var $c=this.getChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
            child.removeFromDOM();
        }
        // clear inner HTML
        if (this.dom.node) {
            this.dom.node.innerHTML = "";
        }
        // support chaining
        return this;
    },
    
//    __update: function() {
//        // loop children
//        var $c=this.getChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
//            if (!child.statusValid) {
//                child.update();
//            }
//        }
//        // update this widget
//        ox.gui.Widget.prototype.update.call(this);
//        // support chaining
//        return this;
//    },
    
    setKindergarten: function(DOMNode) {
        this.kindergarten = DOMNode;
        return this;
    },
    
    validateTree: function() {
        
        if (this.requiresValidation && this.statusVisible) {
            // take care of visible children
            var $c=this.getVisibleChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
                child.validateTree();
            }
            // validate this
            this.paint();
            // done!
            this.requiresValidation = false;
            // trigger event
            this.triggerWidgetEvent("valid");
        }
    },
    
    resize: function() {
        if (this.statusVisible) {
            // get visible children
            var $c=this.getVisibleChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
                child.resize();
            }
            // resize by layout manager
            if (this.layoutManager) {
                this.layoutManager.resize();
                this.triggerWidgetEvent("resize");
            }
        }
    },
    
    paint: function() {
        // no layout manager?
        this.requireLayout();
        // let the layout manager do the work
        this.layoutManager.doLayout();
        // support chaining
        return this;
    },
    
    repaint: function() {
        // no layout manager?
        this.requireLayout();
        // let the layout manager do the work
        this.layoutManager.update();
        // support chaining
        return this;
    },
    
    disable: function() {
        // disable children first
        var $c=this.getChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
            child.disable();
        }
        // call super class method
        ox.gui.Widget.prototype.disable.call(this);
        // support chaining
        return this;
    },
    
    enable: function() {
        // enable children first
        var $c=this.getChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
            child.enable();
        }
        // call super class method
        ox.gui.Widget.prototype.enable.call(this);
        // support chaining
        return this;
    },
    
    /**
     * Hides all widget's that implement hideHovers. (Don't ask)
     * @private
     */
    hideHovers: function() {
        // loop children
        var $c=this.getChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
            if (child.numHovers > 0) {
                child.hideHovers();
                child.numHovers = 0;
            }
        }
        // support chaining
        return this;
    },
    
    hasHover: function (flag) {
        // inc/dec
        this.numHovers = Math.max(0, this.numHovers + (flag === true ? 1 : -1));
        // bubble
        if (this.parent) {
            this.parent.hasHover(flag);
        }
    },
    
    getValues: function() {
        var values = {};
        // loop children
        var $c=this.getChildren(),$i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
            // container?
            if ($.isFunction(child.getValues)) {
                // get values
                values = $.extend(values, child.getValues());
            } 
            // has value?
            else if (child.hasValue()) {
                // get single value
                values[child.getName()] = child.getValue();
            }
        }
        return values;
    },
    
    getSerializedValues: function() {
        return $.param(this.getValues());
    },
    
    i18nHandler: function () {
        if (this.layoutManager) {
            this.layoutManager.i18nHandler();
        }
    },
    
    /**
     * Destroys the widget (and helps the garbage collector as much as possible)
     */
    destroy: function() {

        var p = this.parent;
        
        // remove DOM node now (for better performance)
        $(this.dom.node).remove();
        
        // destroy children (sorry for the wording)
        var $c=this.getChildren();
        if ($c) {
            var $i=$c.length; for(;$i--;) { var child=$c[$i]; // fastest loop
                child.destroy();
            }
        }
        
        // delete references
        this.children = [];
        this.firstChild = null;
        this.lastChild = null;
        this.kindergarten = null;
        
        // layout manager
        if (this.layoutManager) {
            // delete circular reference
            this.layoutManager.destroy();
            this.layoutManager = null;
        }
        
        ox.gui.Widget.prototype.destroy.call(this);
        
        // if (p) { p.validate(); }
    }
    
});
