// vim: et:ts=4:sw=4:sts=4:ft=js

/*@
Ox.Dialog <f:Ox.Element> Dialog Object
    () ->              <f> Dialog Object
    (options) ->       <f> Dialog Object
    (options, self) -> <f> Dialog Object
    options <o> Options object
    self    <o> shared private variable
@*/

Ox.Dialog = function(options, self) {

    // fixme: dialog should be derived from a generic draggable
    // fixme: buttons should have a close attribute, or the dialog a close id
    var self = self || {},
        that = new Ox.Element({}, self)
            .defaults({
                title: '',
                buttons: [],
                content: null,
                height: 216,
                keys: {},
                minHeight: 144,
                minWidth: 256,
                movable: true,
                padding: 16,
                resizable: true,
                width: 384
            })
            .options(options || {})
            .addClass('OxDialog')
            .bindEvent({
                key_enter: function() {
                    keypress('enter');
                },
                key_escape: function() {
                    //Ox.print('KEY ESCAPE')
                    keypress('escape');
                }
            });

    $.extend(self, {
        initialWidth: self.options.width,
        initialHeight: self.options.height
    })

    that.$titlebar = new Ox.Bar({
            size: 'medium'
        })
        .addClass('OxTitleBar')
        .appendTo(that);
    self.options.movable && that.$titlebar
        .dblclick(center)
        .bindEvent({
            dragstart: dragstart,
            drag: drag
        });

    that.$title = new Ox.Element()
        .addClass('OxTitle')
        .html(self.options.title)
        .appendTo(that.$titlebar);

    that.$content = new Ox.Element()
        .addClass('OxContent')
        .css({
            padding: self.options.padding + 'px',
            overflow: 'auto'
        })
        .append(self.options.content)
        .appendTo(that);

    that.$buttonsbar = new Ox.Bar({})
        .addClass('OxButtonsBar')
        .appendTo(that);
    loadButtons();

    //that.$buttons[0].focus();

    that.$layer = new Ox.Element() // fixme: Layer widget that would handle click?
        .addClass('OxLayer')
        .mousedown(mousedownLayer)
        .mouseup(mouseupLayer);

    function center() {
        var documentHeight = Ox.UI.$document.height();
        that.css({
            left: 0,
            top: Math.max(parseInt(-documentHeight / 10), self.options.height - documentHeight + 40) + 'px',
            right: 0,
            bottom: 0,
            margin: 'auto'
        });
    }

    function dragstart(event, e) {
        self.drag = {
            bodyWidth: Ox.UI.$body.width(),
            bodyHeight: Ox.UI.$document.height(),
            elementWidth: that.width(),
            offset: that.offset(),
            x: e.clientX,
            y: e.clientY
        };
        that.css({
            margin: 0
        });
    }

    function drag(event, e) {
        var left = Ox.limit(
                self.drag.offset.left - self.drag.x + e.clientX,
                24 - self.drag.elementWidth, self.drag.bodyWidth - 24
                //0, self.drag.documentWidth - self.drag.elementWidth
            ),
            top = Ox.limit(
                self.drag.offset.top - self.drag.y + e.clientY,
                24, self.drag.bodyHeight - 24
                //24, self.drag.documentHeight - self.drag.elementHeight
            );
        that.css({
            left: left + 'px',
            top: top + 'px'
        });
    }

    function dragstartResize(event, e) {
        self.drag = {
            documentWidth: Ox.UI.$document.width(),
            documentHeight: Ox.UI.$document.height(),
            elementWidth: that.width(),
            elementHeight: that.height(),
            offset: that.offset(),
            x: e.clientX,
            y: e.clientY
        };
        $.extend(self.drag, {
            ratio: self.drag.elementWidth / self.drag.elementHeight
        });
        that.css({
            left: self.drag.offset.left,
            top: self.drag.offset.top,
            margin: 0
        });
    }

    function dragResize(event, e) {
        if (!e.shiftKey) {
            self.drag.ratio = self.options.width / self.options.height;
        }
        self.options.width = Ox.limit(
            self.drag.elementWidth - self.drag.x + e.clientX,
            self.options.minWidth,
            Math.min(
                self.drag.documentWidth,
                self.drag.documentWidth - self.drag.offset.left
            )
        );
        self.options.height = Ox.limit(
            self.drag.elementHeight - self.drag.y + e.clientY,
            self.options.minHeight,
            Math.min(
                self.drag.documentHeight,
                self.drag.documentHeight - self.drag.offset.top
            )
        );
        if (e.shiftKey) {
            self.options.height = Ox.limit(
                self.options.width / self.drag.ratio,
                self.options.minHeight,
                Math.min(
                    self.drag.documentHeight,
                    self.drag.documentHeight - self.drag.offset.top
                )
            );
            self.options.width = self.options.height * self.drag.ratio; 
        }
        that.width(self.options.width);
        that.height(self.options.height);
        that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically
    }

    function dragendResize(event, e) {
        triggerResizeEvent();
    }

    function getButtonById(id) {
        var ret = null;
        //Ox.print('that.$buttons', that.$buttons, id)
        Ox.forEach(that.$buttons, function(button) {
            if (button.options('id') == id) {
                ret = button;
                return false;
            }
        });
        return ret;
    }

    function keypress(key) {
        var id = self.options.keys[key];
        //Ox.print('X', key, self.options.keys)
        id && getButtonById(id).$element.trigger('click');
    }

    function loadButtons() {
        /*Ox.print('loadButtons', $.map(self.options.buttons, function(v) {
            return v;
        }));*/
        if (that.$buttons) {
            that.$buttons.forEach(function($button) {
                $button.removeElement();
            });
            that.$resize.removeElement();
            // that.$buttonsbar.empty();
        }
        that.$buttons = [];
        if (!Ox.isArray(self.options.buttons[0])) {
            self.options.buttons = [[], self.options.buttons];
        }
        self.options.buttons[0].forEach(function(button, i) {
            that.$buttons[i] = button
                .addClass('OxLeft')
                .appendTo(that.$buttonsbar);
        });
        if (self.options.resizable) {
            that.$resize = new Ox.Element()
                .addClass('OxResize')
                .dblclick(reset)
                .bindEvent({
                    dragstart: dragstartResize,
                    drag: dragResize,
                    dragend: dragendResize
                })
                .appendTo(that.$buttonsbar);
        }
        self.options.buttons[1].reverse().forEach(function(button) {
            that.$buttons[that.$buttons.length] = button
                .addClass('OxRight')
                .appendTo(that.$buttonsbar);
        });
    }

    function mousedownLayer() {
        that.$layer.stop().animate({
            opacity: 0.5
        }, 0);
    }

    function mouseupLayer() {
        that.$layer.stop().animate({
            opacity: 0
        }, 0);
    }

    function reset() {
        $.extend(self.options, {
            height: self.initialHeight,
            width: self.initialWidth
        });
        that/*.css({
                left: Math.max(that.offset().left, 24 - that.width())
            })*/
            .width(self.options.width)
            .height(self.options.height);
        that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically
        triggerResizeEvent();
    }

    function triggerResizeEvent() {
        that.triggerEvent('resize', {
            width: self.options.width,
            height: self.options.height
        });
    }

    self.setOption = function(key, value) {
        if (key == 'buttons') {
            loadButtons();
            /*
            that.$buttonsbar.children().animate({
                opacity: 0
            }, 100, function() {
                loadButtons();
                that.$buttonsbar.children().animate({
                    opacity: 1
                }, 100);
            });
            */
        } else if (key == 'content') {
            that.$content.html(value);
        } else if (key == 'height' || key == 'width') {
            that.animate({
                height: self.options.height + 'px',
                width: self.options.width + 'px'
            }, 100);
            that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically
        } else if (key == 'title') {
            that.$title.animate({
                opacity: 0
            }, 100, function() {
                that.$title.html(value).animate({
                    opacity: 1
                }, 100);
            });
        }
    }

    that.center = function() {
        
    };

    that.close = function(callback) {
        callback = callback || function() {};
        that.animate({
            opacity: 0
        }, 200, function() {
            that.$buttons.forEach(function($button) {
                $button.removeElement();
            });
            that.loseFocus();
            that.$layer.removeElement();
            that.removeElement();
            callback();
        });
        Ox.UI.$window.unbind('mouseup', mouseupLayer)
        return that;
    };

    that.content = function($element) {
        that.$content.empty().append($element);
        return that;
    }

    that.disable = function() {
        // to be used on submit of form, like login
        that.$layer.addClass('OxFront');
        return that;
    };

    that.disableButton = function(id) {
        getButtonById(id).options({
            disabled: true
        });
        return that;
    };

    that.enable = function() {
        that.$layer.removeClass('OxFront');
        return that;
    };

    that.enableButton = function(id) {
        getButtonById(id).options({
            disabled: false
        });
        return that;
    };

    that.open = function() {
        //Ox.print('before open')
        that.$layer.appendTo(Ox.UI.$body);
        that.css({
            opacity: 0
        }).appendTo(Ox.UI.$body).animate({
            opacity: 1
        }, 200);
        center();
        reset();
        // fixme: the following line prevents preview-style dialog
        that.gainFocus();
        Ox.UI.$window.bind('mouseup', mouseupLayer)
        //Ox.print('after open')
        return that;
    };

    that.size = function(width, height, callback) {
        $.extend(self, {
            initialWidth: width,
            initialHeight: height
        });
        $.extend(self.options, {
            width: width,
            height: height
        });
        // fixme: duplicated
        that.animate({
            height: self.options.height + 'px',
            width: self.options.width + 'px'
        }, 100, function() {
            that.$content.height(self.options.height - 48 - 2 * self.options.padding); // fixme: this should happen automatically
            callback();
        });
    }

    return that;

};
