﻿function bhajs()
{
    this.$collNsInfo = {};
    this.$nsRoot = {};
    this.$collClassInfo = {};

    // class definition $NsInfo
    this.$NsInfo = function (fullName)
    {
        this.fullName = fullName;
        this.getNamespace = function(){
            return bhajs.findNamespace(this.fullName);
        }
    }
    
    // class definition $ClassInfo
    this.$ClassInfo = function (ns, className, definition)
    {
        this.ns = ns;
        this.className = className;
        this.fullName = this.ns + '.' + className;
        this.$objectCollection = {};
        this.$staticFields = {};
//        this.$instanceEventHandlers = {};
        
        this.definition = definition;
        
//        this.addInstanceEventHandler = function(objId, handlerName) {
//            if (!objId) {
//                throw new Error('Invalid value for objId parameter.');
//            }// end if
//            if (!handlerName) {
//                throw new Error('Invalid value for definition parameter.');
//            }// end if
//            
//            if (!this.$instanceEventHandlers[objId]) {
//                this.$instanceEventHandlers[objId] = {};
//            }// end if
//            
//            var handlerColl = this.$instanceEventHandlers[objId];
//            if (!handlerColl[handlerName]) {
//                handlerColl[handlerName] = true;
//            }// end if
//        }


        this.addStaticField = function(name, value) {
            if (!name) {
                throw new Error('Invalid value for name parameter.');
            }// end if
            this.$staticFields[name] = value;

        }// end function
        
        this.getStaticField = function(name) {
            if (!name) {
                throw new Error('Invalid value for name parameter.');
            }// end if
            return this.$staticFields[name];
        
        }// end function
        
        this.registerObject = function(id, obj) {
            //alert(id);
            if (!id) {
                throw new Error('Invalid value for id parameter.');
            }// end if
            if (!obj) {
                throw new Error('Invalid value for obj parameter.');
            }// end if
            if (this.$objectCollection[id]) {
                throw new Error('Id already exists in this collection.');
            }// end if
            this.$objectCollection[id] = obj;
        }// end function
        
        this.unregisterObject = function(id) {
            if (!id) {
                throw new Error('Invalid value for id parameter.');
            }// end if
            if (!this.$objectCollection[id]) {
                throw new Error('Id does not exist in this collection.');
            }// end if
            this.$objectCollection[id] = null;
            delete this.$objectCollection[id];
        }// end function
        
        this.getObject = function(id) {
            if (!id) {
                throw new Error('Invalid value for id parameter.');
            }// end if
            return this.$objectCollection[id];
        }// end function
        
        this.createObject = function(params) {
            var args = "";
            var i;
            if (params) {
                for (i = 0; i < params.length; i++) {
                    if (i > 0) {
                        args = args + ",";
                    }
                    args = args + "params[" + i + "]";
                }
            }
            
            var obj;
            eval("obj = new (this.definition)(" + args + ");");
            return obj;
            
        }
        
        this.createInstanceMethodCall = function(id, methodName, paramNames) {
            var args = "";
            var i;
            if (paramNames) {
                for (i = 0; i < paramNames.length; i++) {
                    if (i > 0) {
                        args = args + ",";
                    }
                    args = args + paramNames[i];
                }
            }
        
            var body = "var obj;\n";
            body = body + "obj = bhajs.getObject('" + this.fullName + "','" + id + "');\n"
            body = body + "obj." + methodName + "(" + args + ");\n"
            
            var def = "result = function(" + args + "){\n" + body + "}"
            var result;
            
            eval(def);
            
            return result;
            
        }
    }
    
    this.findNamespace = function(ns) {
        try {
            if (!this.$nsRoot){
                this.$nsRoot = {};
            }
            if (!this.$collNsInfo){
                this.$collNsInfo = {};
            }
            var parsedNS = ns.split(".");
            var i;
            var tempName = null;
            var tempNS = this.$nsRoot;
            for (i = 0; i < parsedNS.length; i++) {
                tempName = parsedNS[i];
                tempNS = tempNS[tempName];
                if (!tempNS) {
                    return null;
                }// end if
            }// end for
            return tempNS;
        }catch(ex){
            return null;
        }// end try
    }// end function
   
    this.declareNamespace = function(ns) {
        if (!this.$nsRoot){
            this.$nsRoot = {};
        }
        if (!this.$collNsInfo){
            this.$collNsInfo = {};
        }
        var nsInfo = null;
        if (this.$collNsInfo[ns]) {
            nsInfo = this.$collNsInfo[ns];
        }else{
            var currentNS = this.$nsRoot;
            var parsedNS = ns.split(".");
            var i;
            var tempName = null;
            var tempFullName = null;
            for (i = 0; i < parsedNS.length; i++) {
                tempName = parsedNS[i];
                if (tempFullName){
                    tempFullName = tempFullName + '.' + tempName;
                }else{
                    tempFullName = tempName;
                }//end if
                if (!this.$collNsInfo[tempFullName]){
                    if (!currentNS[tempName]) {
                        currentNS[tempName] = {};
                    }// end if
                    nsInfo = new this.$NsInfo(tempFullName);
                    this.$collNsInfo[tempFullName] = nsInfo;
                    //currentNS = currentNS[tempName];
                }// end if
                currentNS = currentNS[tempName];
 
            }//end for
            nsInfo = this.$collNsInfo[ns];
        }// end if 
        return nsInfo.getNamespace();
    }// end function
    
    this.defineClass = function(ns, className, definition) {
        if (!this.$collClassInfo){
            this.$collClassInfo = {};
        }// end if 
        
        var fullClassName = ns + '.' + className;
        if (this.$collNsInfo[fullClassName]) {
            throw new Error('Class name conflicts with existing namespace.');
        }// end if 
        
        var tempNS = this.declareNamespace(ns);
        var classInfo = new this.$ClassInfo(ns, className, definition);
        this.$collClassInfo[fullClassName] = classInfo;
        tempNS[className] = definition;
    
    }// end function
    
    this.getClass = function(fullClassName) {
        if (!this.$collClassInfo){
            this.$collClassInfo = {};
        }// end if 
        var classInfo = this.$collClassInfo[fullClassName];
        if (classInfo){
            return classInfo.definition;
        }// end if 
    
    }// end function
    
    this.getClassInfo = function(fullClassName) {
        if (!this.$collClassInfo){
            this.$collClassInfo = {};
        }// end if 
        return this.$collClassInfo[fullClassName];
    }// end function
    
    this.getObject = function(fullClassName, id) {
        var classInfo = this.getClassInfo(fullClassName);
        return classInfo.getObject(id);
    }
    
    this.createObject = function(fullClassName, params) {
        var classInfo = this.getClassInfo(fullClassName);
        return classInfo.createObject(params);
    }
    
    //default utility class
    this.Utility = new function() {
        
        this.describeObject = function(obj)
        {
            var result = null;
            for (prop in obj) {
                //alert(prop) + ': ' + obj[prop];
                if (!result) {
                    result = '';
                }else{
                    result = result + '\n';
                }
                result = result + prop + ': ' + obj[prop];
            }
            return result;
        }// end function
        
        this.disposeObject = function(obj)
        {
            for (prop in obj) {
                obj[prop] = null;
                delete obj[prop];
            }
        }// end function
        
    }// end class Utility
    
}

var bhajs = new bhajs();