'use strict';

/*@
Ox.canvas <function> Generic canvas object
    Returns an object with the properties: <code>canvas</code>,
    <code>context</code>, <code>data</code> and <code>imageData</code>.
    # Usage --------------------------------------------------------------------
    Ox.canvas(width, height) -> <object> canvas
    Ox.canvas(image)         -> <object> canvas
    # Arguments ----------------------------------------------------------------
    width  <n> Width in px
    height <n> Height in px
    image  <e> Image object
@*/

Ox.canvas = function() {
    // Ox.print("CANVAS", arguments)
    var c = {}, isImage = arguments.length == 1,
        image = isImage ? arguments[0] : {
            width: arguments[0], height: arguments[1]
        };
    c.context = (c.canvas = Ox.element('<canvas>').attr({
        width: image.width, height: image.height
    })[0]).getContext('2d');
    isImage && c.context.drawImage(image, 0, 0);
    c.data = (c.imageData = c.context.getImageData(
        0, 0, image.width, image.height
    )).data;
    return c;
};

/*@
Ox.documentReady <function> Calls a callback function once the DOM is ready
    (callback) -> <boolean> If true, the document was ready
    callback <function> Callback function
@*/
Ox.documentReady = (function() {
    var callbacks = [];
    document.onreadystatechange = function() {
        if (document.readyState == 'complete') {
            callbacks.forEach(function(callback) {
                callback();
            });
        }
    };
    return function(callback) {
        if (document.readyState == 'complete') {
            callback();
            return true;
        } else {
            callbacks.push(callback);
            return false;
        }
    };
}());

/*@
Ox.element <f> Generic HTML element, mimics jQuery
    (str) -> <o> Element object
    str <s> Tagname ('<tagname>') or selector ('tagname', '.classname', '#id')
    > Ox.element("<div>").addClass("red").hasClass("red")
    true
    > Ox.element("<div>").addClass("red").removeClass("red").hasClass("red")
    false
    > Ox.element("<div>").addClass("red").addClass("red")[0].className
    "red"
    > Ox.element("<div>").attr({id: "red"}).attr("id")
    "red"
    > Ox.element("<div>").css("color", "red").css("color")
    "red"
    > Ox.element("<div>").html("red").html()
    "red"
@*/
Ox.element = function(str) {
    return {
        //@ 0 <e> The DOM element itself
        0: str[0] == '<' ? document.createElement(str.substr(1, str.length - 2)) :
            // fixme: why only take the first match?
            str[0] == '.' ? document.getElementsByClassName(str.substr(1))[0] :
            str[0] == '#' ? document.getElementById(str.substr(1)) :
            document.getElementsByTagName(str)[0],
        /*@
        addClass <f> Adds a class name
            (className) -> <o> This element
            className <s> Class name
        @*/
        addClass: function(str) {
            /* fixme:
            this[0].className = Ox.unique(
                this[0].className.split(' ').push(className)
            ).join(' ');
            */
            this[0].className = this[0].className
                ? Ox.unique(
                    (this[0].className + ' ' + str).split(' ')
                ).join(' ')
                : str;
            return this;
        },
        /*@
        append <f> Appends another element to this element
            (element) -> <o> This element
            element <o> Another element
        @*/
        append: function(element) {
            this[0].appendChild(element[0]);
            return this;
        },
        /*@
        appendTo <f> appends this element object to another element object
            (element) -> <o> This element
            element <o> Another element
        @*/
        appendTo: function(element) {
            element[0].appendChild(this[0]);
            return this;
        },
        /*@
        attr <f> Gets or sets an attribute
            (key)          -> <s> Value
            (key, value)   -> <o> This element
            ({key, value}) -> <o> This element
            key   <str> Attribute name
            value <str> Attribute value
        @*/
        attr: function() {
            var ret, that = this;
            if (arguments.length == 1 && Ox.isString(arguments[0])) {
                ret = this[0].getAttribute(arguments[0]);
            } else {
                Ox.forEach(Ox.makeObject(arguments), function(v, k) {
                    that[0].setAttribute(k, v);
                });
                ret = this;
            }
            return ret;
        },
        /*@
        click <f> Binds a function to the click event
            (callback) -> <o> This element
            callback <f> Callback function
                event <o> The DOM event
        @*/
        // fixme: why not a generic bind?
        click: function(callback) {
            this[0].onclick = callback;
            return this;
        },
        /*@
        css <f> Gets or sets a CSS attribute
            (key)          -> <s> Value
            (key, value)   -> <o> This element
            ({key, value}) -> <o> This element
            key   <str> Attribute name
            value <str> Attribute value
        @*/
        css: function() {
            var ret, that = this;
            if (arguments.length == 1 && Ox.isString(arguments[0])) {
                ret = this[0].style[arguments[0]];
            } else {
                Ox.forEach(Ox.makeObject(arguments), function(v, k) {
                    that[0].style[k] = v;
                });
                ret = this;
            }
            return ret;
        },
        /*@
        hasClass <f> Returns true if the element has a given class
            (className) -> <b> True if the element has the class
            className <s> Class name
        @*/
        hasClass: function(str) {
            return this[0].className.split(' ').indexOf(str) > -1;
        },
        /*@
        html <f> Gets or sets the inner HTML
            ()     -> <s> The inner HTML
            (html) -> <o> This element
            html <s> The inner HTML
        @*/
        html: function(str) {
            var ret;
            if (Ox.isUndefined(str)) {
                ret = this[0].innerHTML;
            } else {
                this[0].innerHTML = str;
                ret = this;
            }
            return ret;
        },
        /*@
        mousedown <f> Binds a function to the mousedown event
            (callback) -> <o> This element
            callback <f> Callback function
                event <o> The DOM event
        @*/
        // fixme: why not a generic bind?
        mousedown: function(callback) {
            this[0].onmousedown = callback;
            return this;
        },
        /*@
        removeAttr <f> Removes an attribute
            (key) -> <o> This element
            key <s> The attribute
        @*/
        removeAttr: function(key) {
            this[0].removeAttribute(key);
            return this;
        },
        /*@
        removeClass <f> Removes a class name
            (className) -> <o> This element
            className <s> Class name
        @*/
        removeClass: function(str) {
            this[0].className = Ox.filter(
                this[0].className.split(' '), function(className) {
                    return className != str;
                }
            ).join(' ');
            return this;
        }
    }
};