// vim: et:ts=4:sw=4:sts=4:ft=js
/*@
Ox.SplitPanel <f:Ox.Element> SpliPanel Object
    () ->              <f> SpliPanel Object
    (options) ->       <f> SpliPanel Object
    (options, self) -> <f> SpliPanel Object
    options <o> Options object
    self    <o> shared private variable
    resize <!> resize
    toggle <!> toggle
@*/

/**
    options:
        elements: [{            array of one, two or three elements
            collapsible: false, collapsible or not (only for outer elements)
            collapsed: false,   collapsed or not (only for collapsible elements)
            element: {},        OxElement (if any element is resizable or
                                collapsible, all OxElements must have an id)
            resizable: false,   resizable or not (only for outer elements)
            resize: [],         array of sizes (only for resizable elements,
                                first value is min, last value is max,
                                other values are 'snappy' points in between)
            size: 0             size in px (one element must have no size)
        }],
        orientation: ''         'horizontal' or 'vertical'
    events:
        resize
        toggle
*/
Ox.SplitPanel = function(options, self) {

    var self = self || {},
        that = new Ox.Element({}, self) // fixme: Container
            .defaults({
                elements: [],
                orientation: 'horizontal'
            })
            .options(options || {})
            .addClass('OxSplitPanel');

    $.extend(self, {
        dimensions: Ox.UI.DIMENSIONS[self.options.orientation],
        edges: Ox.UI.EDGES[self.options.orientation],
        length: self.options.elements.length,
        resizebarElements: [],
        $resizebars: []
    });

    // create elements
    that.$elements = [];
    self.options.elements.forEach(function(v, i) {
        self.options.elements[i] = $.extend({
            collapsible: false,
            collapsed: false,
            resizable: false,
            resize: [],
            size: 'auto'
        }, v);
        that.$elements[i] = v.element
            .css(self.edges[2], (parseInt(v.element.css(self.edges[2])) || 0) + 'px')
            .css(self.edges[3], (parseInt(v.element.css(self.edges[3])) || 0) + 'px');
        //alert(v.element.css(self.edges[3]))
    });

    // create resizebars
    self.options.elements.forEach(function(v, i) {
        //that.append(element)
        //Ox.print('V: ', v, that.$elements[i])
        var index = i == 0 ? 0 : 1;
        that.$elements[i].appendTo(that.$element); // fixme: that.$content
        if (v.collapsible || v.resizable) {
            //Ox.print('v.size', v.size)
            self.resizebarElements[index] = i < 2 ? [0, 1] : [1, 2];
            self.$resizebars[index] = new Ox.Resizebar({
                collapsible: v.collapsible,
                edge: self.edges[index],
                elements: [
                    that.$elements[self.resizebarElements[index][0]],
                    that.$elements[self.resizebarElements[index][1]]
                ],
                id: v.element.options('id'),
                orientation: self.options.orientation == 'horizontal' ? 'vertical' : 'horizontal',
                parent: that, // fixme: that.$content
                resizable: v.resizable,
                resize: v.resize,
                size: v.size
            });
            self.$resizebars[index][i == 0 ? 'insertAfter' : 'insertBefore'](that.$elements[i]);
        }
    });

    self.options.elements.forEach(function(v, i) {
        v.collapsed && that.css(
            self.edges[i == 0 ? 0 : 1], -self.options.elements[i].size + 'px'
        );
    });

    setSizes(true);

    function getPositionById(id) {
        var position = -1;
        Ox.forEach(self.options.elements, function(element, i) {
            if (element.element.options('id') == id) {
                position = i;
                return false;
            }
        });
        //Ox.print('getPositionById', id, position);
        return position;
    }

    function getSize(element) {
        return element.size + (element.collapsible || element.resizable);
        //return (element.size + (element.collapsible || element.resizable)) * !element.collapsed;
    }

    function getVisibleSize(element) {
        return getSize(element) * !element.collapsed;
    }

    function setSizes(init) {
        self.options.elements.forEach(function(v, i) {
            // fixme: maybe we can add a conditional here, since init
            // is about elements that are collapsed splitpanels
            var edges = [
                (init && parseInt(that.$elements[i].css(self.edges[0]))) || 0,
                (init && parseInt(that.$elements[i].css(self.edges[1]))) || 0
            ];
            v.size != 'auto' && that.$elements[i].css(self.dimensions[0], v.size + 'px');
            if (i == 0) {
                that.$elements[i].css(
                    self.edges[0], edges[0] + 'px'
                );
                that.$elements[i].css(
                    self.edges[1], (getSize(self.options.elements[1]) + (length == 3 ? getSize(self.options.elements[2]) : 0)) + 'px'
                );
            } else if (i == 1) {
				that.$elements[i].css(
                    self.edges[0], self.options.elements[0].size == 'auto' ? 'auto' :
                    edges[0] + getSize(self.options.elements[0]) + 'px'
                );
                (self.options.elements[0].size != 'auto' || v.size != 'auto') && that.$elements[i].css(
                    self.edges[1], (self.length == 3 ? getSize(self.options.elements[2]) : 0) + 'px'
                );
            } else {
                that.$elements[i].css(
                    self.edges[0], (self.options.elements[1].size == 'auto' || v.size == 'auto') ? 'auto' :
                    (getVisibleSize(self.options.elements[0]) + getVisibleSize(self.options.elements[1])) + 'px'
                );
                that.$elements[i].css(
                    self.edges[1], edges[1] + 'px'
                );
            }
            if (v.collapsible || v.resizable) {
                self.$resizebars[i == 0 ? 0 : 1].css(self.edges[i == 0 ? 0 : 1], v.size);
            }
        });
    }

    that.isCollapsed = function(id) {
        var pos = Ox.isNumber(id) ? id : getPositionById(id);
        return self.options.elements[pos].collapsed;
    };

    that.replaceElement = function(id, element) {
        // one can pass pos instead of id
        var pos = Ox.isNumber(id) ? id : getPositionById(id);
        //Ox.print('replace', pos, element);
        //Ox.print('element', self.options.elements[pos].element, element)
        that.$elements[pos] = element
            .css(self.edges[2], (parseInt(element.css(self.edges[2])) || 0) + 'px')
            .css(self.edges[3], (parseInt(element.css(self.edges[3])) || 0) + 'px');
        //alert(element.css(self.edges[3]))
        self.options.elements[pos].element.replaceWith(element.$element.$element || element.$element);
        self.options.elements[pos].element = element;
        setSizes();
        self.$resizebars.forEach(function($resizebar, i) {
            $resizebar.options({
                elements: [
                    that.$elements[self.resizebarElements[i][0]],
                    that.$elements[self.resizebarElements[i][1]]
                ]
            });
        });
        //Ox.print(self.options.elements[pos])
        return that;
    };

    that.replaceElements = function(elements) {
        elements.forEach(function(element, i) {
            if (Ox.isNumber(element.size)) {
                that.size(i, element.size);
                if (element.collapsible || element.resizable) {
				    self.$resizebars[i == 0 ? 0 : 1].options({
                        collapsible: element.collapsible,
                        resizable: element.resizable,
				        size: element.size
				    });
				}
            }
            that.replace(i, element.element);
        });
        self.options.elements = elements;
        self.$resizebars.forEach(function($resizebar, i) {
            $resizebar.options({
                elements: [
                    that.$elements[self.resizebarElements[i][0]],
                    that.$elements[self.resizebarElements[i][1]]
                ]
            });
        });
        return that;
    }

    that.size = function(id, size) {
        // one can pass pos instead of id
        var pos = Ox.isNumber(id) ? id : getPositionById(id),
            element = self.options.elements[pos];
		if (arguments.length == 1) {
			return element.element[self.dimensions[0]]() * !that.isCollapsed(pos);
		} else {
			element.size = size;
            setSizes();
            return that;
		}
    };
    that.getSize = function(id) {
        var pos = Ox.isNumber(id) ? id : getPositionById(id),
            element = self.options.elements[pos];
		return element.element[self.dimensions[0]]() * !that.isCollapsed(pos);
    };

    that.toggle = function(id) {
        // one can pass pos instead of id
        var pos = Ox.isNumber(id) ? id : getPositionById(id),
			element = self.options.elements[pos],
            value = parseInt(that.css(self.edges[pos == 0 ? 0 : 1])) +
				element.element[self.dimensions[0]]() *
				(element.collapsed ? 1 : -1),
            animate = {};
        animate[self.edges[pos == 0 ? 0 : 1]] = value;
        that.animate(animate, 200, function() { // fixme: 250?
            element.collapsed = !element.collapsed;
            element.element.triggerEvent('toggle', {
                'collapsed': element.collapsed
            });
			element = self.options.elements[pos == 0 ? 1 : pos - 1];
            element.element.triggerEvent(
				'resize',
				element.element[self.dimensions[0]]()
			);
        });
    };

    that.updateSize = function(pos, size) {
        // this is called from resizebar
        var pos = pos == 0 ? 0 : self.options.elements.length - 1; // fixme: silly that 0 or 1 is passed, and not pos
        self.options.elements[pos].size = size;
    }

    return that;

};

Ox.SplitPanel_ = function(options, self) {

    var self = self || {},
        that = new Ox.Element({}, self)
            .defaults({
                elements: [],
                orientation: 'horizontal'
            })
            .options(options)
            .addClass(
                'OxSplitPanel_ Ox' + Ox.toTitleCase(self.options.orientation)
            );

    Ox.extend(self, {
        $separators: [],
        clientXY: self.options.orientation == 'horizontal' ? 'clientX' : 'clientY',
        dimensions: Ox.UI.DIMENSIONS[self.options.orientation],
        edges: Ox.UI.EDGES[self.options.orientation]
    });

    self.options.elements.forEach(function(element, i) {
        self.options.elements[i] = Ox.extend({
            collapsible: false,
            collapsed: false,
            resizable: false,
            resize: [],
            size: 'auto'
        }, element);
    });

    self.autoPercent = (100 - self.options.elements.reduce(function(val, element) {
        return val + (Ox.endsWith(element.size, '%') ? parseFloat(element.size) : 0);
    }, 0)) / self.options.elements.filter(function(element) {
        return element.size == 'auto';
    }).length + '%';

    self.options.elements.forEach(function(element, i) {
        var flex, index = i == 0 ? 0 : 1;
        if (Ox.isNumber(element.size)) {
            element.element.css(self.dimensions[0], element.size + 'px');
        } else {
            flex = (
                element.size == 'auto' ? self.autoPercent : element.size
            ).replace('%', '');
            element.element.css({
                boxFlex: flex,
                MozBoxFlex: flex,
                WebkitBoxFlex: flex
            });
        }
        element.element.appendTo(that);
        if (element.collapsible || element.resizable) {
            self.$separators.push(
                Ox.Element()
                    .addClass('OxSeparator')
                    .bindEvent({
                        anyclick: function() {
                            that.toggle(i);
                        },
                        dragstart: function(event, e) {
                            dragstart(i, e);
                        },
                        drag: function(event, e) {
                            drag(i, e);
                        },
                        dragend: function(event, e) {
                            dragend(i, e);
                        },
                    })
                    .append($('<div>').addClass('OxSpace'))
                    .append($('<div>').addClass('OxLine'))
                    .append($('<div>').addClass('OxSpace'))
                    ['insert' + (index ? 'Before' : 'After')](element.element)
            );
        }
        
    });

    function dragstart(pos, e) {
        var element = self.options.elements[pos],
            size = element.element[self.dimensions[0]]();
        if (element.resizable && !element.collapsed) {
			self.drag = {
			    size: size,
			    startPos: e[self.clientXY],
                startSize: size
            };
            Ox.print('self.drag', self.drag)
		}
    }

    function drag(pos, e) {
        var data = {},
            element = self.options.elements[pos],
            index = pos == 0 ? 0 : 1;
        if (element.resizable && !element.collapsed) {
            var d = e[self.clientXY] - self.drag.startPos,
                size = Ox.limit(
                    self.drag.startSize + d * (index ? -1 : 1),
                    element.resize[0],
                    element.resize[element.resize.length - 1]
                );
            Ox.forEach(element.resize, function(v) {
                if (size >= v - 8 && size <= v + 8) {
                    size = v;
                    return false;
                }
            });
            if (size != self.drag.size) {
                self.drag.size = size;
                data[self.dimensions[0]] = size;
                element.element
                    .css(self.dimensions[0], size + 'px')
                    .triggerEvent('resize', data);
                triggerEvents('resize', pos);
            }
        }
        
    }

    function dragend(pos, e) {
        var data = {},
            element = self.options.elements[pos];
        if (element.resizable && !element.collapsed) {
            data[self.dimensions[0]] = self.drag.size
            element.element.triggerEvent('resizeend', data);
            triggerEvents('resizeend', pos);
        }
    }

    function triggerEvents(event, pos) {
        var data = {};
        self.options.elements.forEach(function(element, i) {
            if (i != pos && element.size == 'auto') {
                data[self.dimensions[0]] = element.element[self.dimensions[0]]();
                element.element.triggerEvent(event, data);
            }
        });
    }

    that.replaceElement = function(pos, element) {
        var $element = self.options.elements[pos].element,
            size = self.options.elements[pos].size;
        $element.replaceWith(self.options.elements[pos].element = element);
        if (size == 'auto') {
            $element.css(self.boxFlexCSS);
        } else {
            $element.css(self.dimensions[0], size + 'px')
        }
        return that;
    };

    that.size = function(pos, size) {
        var element = self.options.elements[pos],
            ret;
        if (Ox.isUndefined(size)) {
            ret = element.element[self.dimensions[0]]();
        } else {
            element.size = size;
            element.element.css(self.dimensions[0], size + 'px')
            ret = that;
        }
        return that;
    }

    that.toggle = function(pos) {
        var css = {},
            element = self.options.elements[pos],
            flex,
            index = pos == 0 ? 0 : 1,
            size = element.element[self.dimensions[0]]();
        if (element.collapsible) {
            element.collapsed = !element.collapsed;
            css['margin' + Ox.toTitleCase(self.edges[0][index])] =
                element.collapsed ? -size : 0;
            Ox.print('css', css);
            that.animate(css, 250, function() {
                element.element.triggerEvent('toggle', {collapsed: element.collapsed});
                triggerEvents('resize', pos);
            });
        }
    }

    return that;
    
};
