/*
    ox.js
*/
Ox = {
    version: "0.1.2"
};
(function() {
    var _ = {
        uid: 0
    };
    Ox.makeObject = function() {
        /*
        >>> Ox.makeObject("foo", "bar").foo
        bar
        >>> Ox.makeObject({foo: "bar"}).foo
        bar
        */
        var obj = {};
        if (arguments.length == 1) {
            obj = arguments[0];
        } else {
            obj[arguments[0]] = arguments[1]
        }
        return obj;
    }
    Ox.uid = function() {
        /*
            returns a unique id
        */
        return _.uid++;
    }
})();
/*
    ox.ui.js
*/
(function() {
    var _ = {
        elements: [],
        jQueryFunctions: function() {
            var functions = [],
                $element = $("<div/>");
            delete $element.length;
            $.each($element, function(k, v) {
                if (typeof v == "function") {
                    functions.push(k);
                }
            });
            return functions.sort();
        }()
    };
    Ox.Widget = function(options, self) {

        // construct
        var self = self || {},
            that = this;

        // init
       (function() {
            if (typeof options == "string") {
                options = {
                    element: options
                };
            }
            that.ox = Ox.version;
            that.id = Ox.uid();
            that.$element = $("<" + (options.element || "div") + "/>")
                .data("ox", that.id);
            _.elements[that.id] = that;
            wrapjQuery();
        })();

        // private
        function wrapjQuery() {
            $.each(_.jQueryFunctions, function(i, v) {
                that[v] = function() {
                    var args = arguments,
                        length = args.length,
                        id, ret;
                    $.each(args, function(i, v) {
                        // if an ox object was passed
                        // then pass its $element instead
                        // so we can do jqObj.jqFn(oxObj)
                        if (v.ox) {
                            args[i] = v.$element;
                        }
                    });
                    // why does this not work?
                    // ret = that.$element[v].apply(this, arguments);
                    if (length == 0) {
                        ret = that.$element[v]();
                    } else if (length == 1) {
                        ret = that.$element[v](args[0]);
                    } else if (length == 2) {
                        ret = that.$element[v](args[0], args[1]);
                    } else if (length == 3) {
                        ret = that.$element[v](args[0], args[1], args[2]);
                    } else if (length == 4) {
                        ret = that.$element[v](args[0], args[1], args[2], args[3]);
                    }
                    // if the $element of an ox object was returned
                    // then return the ox object instead
                    // so we can do oxObj.jqFn().oxFn()
                    return ret.jquery && oxui.elements[id = ret.data("ox")] ?
                        oxui.elements[id] : ret;
                }
            });
        }

        // shared
        self.onChange = function() {
            /*
                self.onChange(option, value)
                is called when an option changes
                to be implemented by widget
            */
        };

        // public
        that.defaults = function(defaults) {
            /*
                that.defaults({foo: x})         sets self.defaults
            */
            self.defaults = defaults;
            return that;
        }
        that.options = function() {
            /*
                that.options()                  returns self.options
                that.options("foo")             returns self.options.foo
                that.options("foo", x)          sets self.options.foo,
                                                returns that
                that.options({foo: x, bar: y})  sets self.options.foo
                                                and self.options.bar,
                                                returns that
            */
            var length = arguments.length,
                args, ret;
            if (length == 0) {
                // options()
                ret = self.options;
            } else if (length == 1 && typeof arguments[0] == "string") {
                // options(str)
                ret = self.options[arguments[0]]
            } else {
                // options (str, val) or options({str: val, ...})
                // translate (str, val) to ({str: val})
                args = Ox.makeObject.apply(that, arguments);
                // if options have not been set, extend defaults,
                // otherwise, extend options
                self.options = $.extend(
                    self.options || self.defaults, args);
                $.each(args, function(k, v) {
                    self.onChange(k, v);
                });
                ret = that;                            
            }
            return ret;
        }

        // return
        return that;

    }
    /*
        Widget Design Pattern

        Ox.MyWidget = function(options, self) {

            // construct
            var self = self || {},
                that = new Ox.Widget({...}, self)
                    .defaults({...})
                    .options(options || {});

            // init, wrapped in function so it can be collapsed
            (function() {...})();

            // private, can be called from init
            function foo() {...}

            // private, shared
            self.onChange = function(option, value) {...}
            self.foo = function() {...}

            // public
            that.foo = function() {...}

            // return
            return that;

        }
    */
    Ox.Foo = function(options, self) {
        var self = self || {},
            that = new Ox.Widget({
                    element: "div"
                }, self)
                .defaults({
                    foo: "bar"
                })
                .options(options || {});
        (function() {
            self.time = Date.now();
        })();
        self.onChange = function(option, value) {
            if (option == "foo") {
                Ox.print("foo set to", value);
            }
        }
        that.getTime = function() {
            return self.time;
        }
        return that;
    }
    Ox.Bar = function(options, self) {
        var self = self || {},
            that = Ox.Foo({
                    foo: "baz"
                }, self)
                .defaults({
                    bar: "xyz"
                })
                .options(options || {});
        (function() {
            self.time = 0;
        })();
        that.bar = function() {
            Ox.print("Bar.bar()");
        }
        return that;
    }
})();