var go = {};

go.Class = (function() {

    function emptyFunction() {}

    var protoBase = {
        '__construct': emptyFunction,
        '__destruct': emptyFunction,
        'destroy': (function() {
            this.__destruct();
            for (var k in this) { // @todo
                this[k] = null;
            }            
        })
    };

    function objClone(obj) {
        var res = {};
        if (obj) {
            for (var k in obj) {
                res[k] = obj[k];
            }
        }
        return res;
    }
    
    function bindMethod(obj, method) {
        return (function() {
            var _t = method.$this;
            method.$this = this;
            var res = method.apply(obj, arguments);
            method.$this = _t;
            return res;
        });
    }
    
    function bindMethods(obj, binds) {
        for (var name in binds) {
            obj[name] = bindMethod(obj, binds[name]);
        }
    }

    function Class(parent, props) {

        var protoParent = parent ? parent.prototype : protoBase;    
        if (protoParent["$final"]) {
            throw ("attempt to inherit from final class");
        }
        emptyFunction.prototype = protoParent;    
        var proto   = new emptyFunction();
        var statics = objClone(protoParent["$static"]);
        var binds   = objClone(protoParent["$binds"]);
        for (var name in props) {
            var prop = props[name];
            var matches = name.match(/^(.+?)(_static|_bind)?$/);
            name = matches[1];
            postfix = matches[2];
            switch (postfix) {
                case "_static":
                    if (typeof(prop) == "function") {
                        prop.$parent = statics[name];
                    }
                    statics[name] = prop;
                    break;
                case "_bind":
                    if (typeof(prop) == "function") {
                        prop.$parent = binds[name];
                        binds[name] = prop;
                    } else {
                        proto[name] = prop;
                    }
                    break;
                default:
                    if (name != "eoc") {
                        if (typeof(prop) == "function") {
                            prop.$parent = protoParent[name];
                        }
                        proto[name] = prop;
                    }
            }
        }
        for (var name in statics) {
            construct[name] = statics[name];
        }
        proto.$static = statics;
        proto.$binds  = binds;
        proto.$self   = construct;
        
        function construct() {
            if (props.$abstract) {
                throw ("attempt to create an instance of abstract class");
            }
            bindMethods(this, binds);
            this.__construct.apply(this, arguments);
        }
        construct.prototype = proto;
    
        return construct;
    }
    
    Class.$abstract = (function() {
        //throw ("attempt to call an abstract method");
    });

    return Class;
})();
