var Mohawk = {
    version: '0.1',
    UI: {
        selection: false,
        selectables: []
    },
    
    Utils: {
    },
    
    css: [],
    
    loadCss: function () {
        Mohawk.css.forEach(function () {
            var link = document.createElement('LINK');
            link.href = this;
            link.type = 'text/css';
            link.rel = 'stylesheet';
            document.getElementsByTagName('HEAD')[0].appendChild(link);
        });
    }
}

var IE = typeof window.ActiveXObject != 'undefined';
var FF = navigator.userAgent.match('Firefox') != null;
var OPERA = typeof window.opera != 'undefined';

var BTN_LEFT = IE ? 1 : 0;
var BTN_MIDDLE = IE ? 4 : 1;
var BTN_RIGHT = 2;

function is_scalar(variable) {
    return ['number', 'string', 'boolean'].has(typeof variable);
}

function is_numeric(variable) {
    return (typeof variable == 'number') || (typeof variable == 'string' && variable * 1 + '' == variable);
}

function extend(target, source, directly) {
    if (target.prototype && !directly) {
        extend(target.prototype, source);
    } else {
        for (var i in source) {
            target[i] = source[i];
        }
    }
}

Function.prototype.parse = function () {
    var source = typeof this.toSource == 'function' ? this.toSource() : this.toString().replace(new RegExp('\n', 'g'), ' ');
    var regex = new RegExp('\\(?\\s*function\\s+([a-z$_][a-z$_0-9]*)?\\s*\\(\\s*([^\\)]*)\\s*\\)\\s*\\{(.*)\\}\\s*\\)?\\s*', 'img');
    var m = null;
    if (m = regex.exec(source)) {
        return {
            name: m[1],
            params: m[2].split(new RegExp('\\s*,\\s*')),
            body: m[3]
        }
    } else {
        return null;
    }
}

var ObjectInterface = {
    exists: function (key) {
        return typeof this[key] != 'undefined';
    },
    
    get: function (key, default_value) {
        if (typeof this[key] != 'undefined') {
            return this[key];
        } else {
            return default_value ? default_value : '';
        }
    },

    has: function (value) {
        for (var i in this) {
            if (value == this[i]) {
                return true;
            }
        }
        return false;
    },

    key: function (value) {
        for (var i in this) {
            if (value == this[i]) {
                return i;
            }
        }
        return false;
    },

    keys: function () {
        var keys = [];
        for (var i in this) {
            if (typeof Object.prototype[i] == 'undefined') {
                keys.push(i);
            }
        }
        return keys;
    },

    forEach: function (action) {
        for (var i in this) {
            if (typeof Object.prototype[i] == 'undefined') {
                action.call(this[i], i, this[i]);
            }
        }
    }
};

var ArrayInterface = {
    last: function () {
        return this.slice(-1)[0];
    },

    pull: function (value) {
        for (var i in this) {
            if (value == this[i]) {
                this.splice(i, 1);
            }
        }
    },

    forEach: function (action) {
        for (var i = 0; i < this.length; i ++) {
            action.call(this[i], i, this[i]);
        }
    },
    
    binSearch: function (value) {
        var l = 0;
        var r = this.length - 1;
        if (this[l] > value) {
            return l;
        }
        if (this[r] < value) {
            return r + 1;
        }
        do {
            var m = Math.round((r + l) / 2);
            if (m == l || m == r || this[m] == value) {
                break;
            } else if (this[m] < value) {
                l = m;
            } else {
                r = m;
            }
        } while (true);
        return m;
    },
    
    intersect: function (array, sorted) {
        if (!sorted) {
            this.sort();
            array.sort();
        }
        var i = 0, j = 0;
        var unique = [];
        while (i < this.length && j < array.length) {
            if (this[i] < array[j]) {
                i ++;
            } else if (this[i] > array[j]) {
                j ++;
            } else {
                unique.push(this[i]);
                i ++;
                j ++;
            }
        }
        return unique;
    }
};

extend(Object, ObjectInterface);
extend(Array, ArrayInterface);

var MathInterface = {
    sign: function (x) {
        return x == 0 ? 0 : (x > 0 ? 1 : -1);
    },

    rand: function (min, max) {
        if (!max) {
            min = 0;
            max = min
        }
        return Math.round(Math.random() * (max - min)) + min;
    }
}
extend(Math, MathInterface);

var NumberInterface = {
    toHex: function () {
        if (this == 0) {
             return '0';
        }
        var hex = '';
        var digits = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'];
        var d = this;
        var pow = Math.floor(Math.log(d) / Math.log(16));
        var base = Math.pow(16, pow);
        for (var i = pow; i >= 0; i --) {
            var q = Math.floor(d / base);
            d -= q * base;
            hex += digits[q];
            base /= 16;
        }
        return hex;
    }
}
extend(Number, NumberInterface);

var StringInterface = {
    pad: function (length, char, right) {
        var str = this;
        var size = length - this.length;
        char = char || ' ';
        for (var i = 0; i < size; i ++) {
            str = right ? str.concat(char) : char.concat(str); 
        }
        return str;
    }
}
extend(String, StringInterface);

RegExp.quote = function (str) {
    var regexp = new RegExp('([\\<\\>\\/\\?\\.\\$\\^\\[\\]\\*\\+\\\\])', 'g');
    return str.replace(regexp, '\\$1');
}

var Class = function (object) {
    return this.extend(object);
}

var Singletone = function (object) {
    var class_interface = new Class(object);
    var instance = new class_interface;
//    Class.setSelf(instance);
    return instance;
}
Class.prototype = {
    self: null,
    parent: null,
    extend: function (descendant) {
        var child = Class.method(descendant.__construct || (this.prototype && this.prototype.__construct ? this.prototype.__construct : function () {}),
//        'var self = this; var object = this; do {object.self = self; object = object.parent;} while (object && object.parent); var parent = self.parent;');
        'Class.setSelf(this); var self = this.self; var parent = this.parent;');

        // copy parent prototype
        var methods = descendant.keys();
        child.prototype.parent = {};
//        child.prototype.__base = child.prototype; // uncomment this line if you need base class

        for (var i in this.prototype) {
            /*if (typeof Object.prototype[i] != 'undefined') {
                continue;
            }*/
            
            if (this.prototype[i] instanceof Function) {
                child.prototype.parent[i] = Class.method(this.prototype[i]);
            } else {
                child.prototype.parent[i] = this.prototype[i];
            }

            if (i != 'parent') {
                if (this.prototype[i] instanceof Function) {
                    child.prototype[i] = Class.method(this.prototype[i], !methods.has(i) ? 'if (parent && parent.parent) {parent = parent.parent};' + Class.label : '');
                } else {
                    child.prototype[i] = this.prototype[i];
                }
            }
        }

        // copy child prototype
        for (var i in descendant) {
            /*if (typeof Object.prototype[i] != 'undefined') {
                continue;
            }*/
            if (descendant[i] instanceof Function) {
                //child.prototype[i] = Class.method(descendant[i], 'var self = this.self; var parent = this.parent; /*var object = parent; while (object) {object.self = self; object = object.parent;}*/' + Class.label);
                child.prototype[i] = Class.method(descendant[i], 'var self = this.self; var parent = this.parent;' + Class.label + 'if (self) {try {parent.self = self} catch (e) {}}');
            } else {
                child.prototype[i] = descendant[i];
            }
        }

        child.extend = this.extend;

        return child;
    }
}

Class.extend = Class.prototype.extend;
Class.label = 'Class.here();';
Class.here = function () {};
Class.method = function (action, add) {
    var source = action.parse();
    var f = function () {};
    if (source) {
        if (source.body.indexOf(Class.label) > 0) {
            source.body = source.body.replace(Class.label, add || '');
        } else {
            source.body = (add || '') + source.body;
        }
        
        eval('f = function (' + source.params.join(',') + ') {' + source.body + '};');
    } else {
        // TODO: some error here
        f = false;
    }
    return f;
}
Class.setSelf = function (object) {
    var base = object;
    do {
        object.self = base;
        object = object.parent;
    } while (object && object.parent);
    
/*    var parents = 0;
    while (object.parent) {
        parents ++;
        object = object.parent;
    };
    
    var obj = 'object';
    console.dir(object);
    console.log(parents);
    for (var i = 0; i <= parents; i ++) {
        console.log(obj + '.self = base;');
        eval(obj + '.self = base;');
        obj += '.parent';
    }*/
}

//{import path="mohawk.kernel.geom" type="js" /}
//{import path="mohawk.kernel.Effects" type="js" /}
//{import path="mohawk.kernel.Template" type="js" /}
