/* <copyright>
This file contains proprietary software owned by Motorola Mobility, Inc.<br/>
No rights, expressed or implied, whatsoever to this software are provided by Motorola Mobility, Inc. hereunder.<br/>
(c) Copyright 2011 Motorola Mobility, Inc.  All Rights Reserved.
</copyright> */

var RDGE = RDGE || {};

/*
 *	Manage state instances
 */
RDGE.stateManager = function () {
	// a stack of states
	this.stateStack = [];
	
	// number of states on the stack
    this.stateTop       = undefined;
    
    // the states of the context
    this.RDGEInitState	= null;
    this.RDGERunState	= null;
    
    this.currentState = function () {
		if(this.stateTop != undefined)
			return this.stateStack[this.stateTop];
			
		return null;
    };
	
	/*
	 *  Push new IRuntime state - engine executes the new state
	 */
    this.PushState = function (state, flags) {
        if (state != null && typeof state.Init == 'function') {
			if(this.stateTop != undefined)
				this.stateStack[this.stateTop].LeaveState();
			
			if(flags == undefined || flags != "noInit")
				state.Init();
				
			this.stateTop = this.stateStack.push(state) - 1;
		}
    };

	/*
	 *  Remove IRuntime state from stack, engine executes previous state
	 */
    this.PopState = function () {
		state = this.stateStack.pop();
        if (state != null) {
			state.Shutdown();
		}
		
		this.stateTop = this.stateTop > 0 ? this.stateTop - 1 : 0;
	    
        if (this.stateStack[this.stateTop]) {
			this.stateStack[this.stateTop].ReInit();
		}
    };

	/*
	 *  Remove all states from the stack
	 */
    this.PopAll = function () {
        while (this.stateStack[this.stateTop] != null) {
			this.PopState();
		}
    };
	
    this.tick = function (dt) {
        if (this.stateStack[this.stateTop] != null) {
			this.stateStack[this.stateTop].Update(dt);
			this.stateStack[this.stateTop].Resize();
			this.stateStack[this.stateTop].Draw();
		}
    };
};

RDGE.Engine = function () {
    this._assetPath = "assets/";

    // map of scene graphs to names
    this.sceneMap = [];

    // number of states on the stack
    this.stateTop = undefined;

    // size of the browser window
    this.lastWindowWidth = window.innerWidth;
    this.lastWindowHeight = window.innerHeight;

    this.defaultContext = null;

    this.lightManager = null;

    clearColor = [0.0, 0.0, 0.0, 0.0];

    this.initializeComplete = false;

    this.RDGECanvas = null;

    /*
    *	a map of canvas names to renderer
    */
    this.canvasToRendererMap = {};

    /*
    *	states to canvas map - maps a state stack to the canvas context it belongs to
    */
    this.canvasNameToStateStack = {};

    /*
    *	the list of context's that are active
    */
    this.canvasCtxList = [];

    /*
    *	regex object to verify runtime object is not some sort of exploit
    */
    invalidObj = new RegExp("([()]|function)");

    isValidObj = function (name) {
        // do a quick test make sure user isn't trying to execute a function
        if (invalidObj.test(name)) {
            window.console.error("invalid object name passed to RDGE, " + name + " - looks like a function");
            return false;
        }

        return true;
    };

    /*
    *	The context definition - every context shares these parameters
    */
    contextDef = function () {
        this.id = null;
        this.renderer = null;
        this.ctxStateManager = null;
        this.startUpState = null;
        this.sceneGraphMap = [];
        this.currentScene = null;
        this.getScene = function () {
            return this.sceneGraphMap[this.currentScene];
        }
        this.debug =
        {
            'frameCounter': 0,
            'mat4CallCount': 0
        }
    };

    // maintains the contexts
    contextManager = new RDGE.objectManager();
    this.ctxMan = contextManager;

    // the context currently being updated
    contextManager.currentCtx = null;

    contextManager._addObject = contextManager.addObject;
    contextManager.contextMap = {};

    contextManager.addObject = function (context) {
        this.contextMap[context.id] = context;
        return this._addObject(context);
    };

    contextManager.start = function () {
        var len = this.objects.length;
        for (var i = 0; i < len; ++i) {
            // set the current context
            contextManager.currentCtx = this.objects[i];
            this.objects[i].ctxStateManager.PushState(this.objects[i].startUpState);
        }
    };

    contextManager.forEach = function (cb) {
        var len = this.objects.length;
        for (var i = 0; i < len; ++i) {
            cb(this.objects[i]);
        }
    };

    this.getContext = function (optCanvasID) {
        if (!optCanvasID) {
            return contextManager.currentCtx;
        }
        else {
            return contextManager.contextMap[optCanvasID];
        }
    };

    this.clearContext = function (canvasID) {
        contextManager.contextMap[canvasID] = undefined;
    };

    /*
    *	give the contextID (canvas id) of the context to set
    */
    this.setContext = function (contextID) {
        contextManager.currentCtx = contextManager.contextMap[contextID];
    };

    this.tickContext = function (contextID) {
        var savedCtx = contextManager.currentCtx;
        contextManager.currentCtx = contextManager.contextMap[contextID];
        this.objects[i].ctxStateManager.tick(dt);
        contextManager.currentCtx = savedCtx;
    };

    this.setAssetPath = function (path) {
        this._assetPath = path.slice();
    };

    this.remapAssetFolder = function (url) {
        var searchStr = "assets/";
        var index = url.indexOf(searchStr);
        var rtnPath = url;
        if (index >= 0) {
            rtnPath = url.substr(index + searchStr.length);
            rtnPath = this._assetPath + rtnPath;
        }
        return rtnPath;
    };
};

/*
 *   Initialize the RDGE web engine
 */
RDGE.Engine.prototype.init = function (userInitState, userRunState, canvasObject) {
    this.GlInit(canvasObject);

    globalParamFuncSet = function (param) {
		this.data = param.data;
		this.type =param.type;
		
        this.set = function (v) {
			var len = this.data ? this.data.length : 0;
			for(var i=0;i<len;++i)
				this.data[i]=v[i];
    	}
        this.get = function () {
            if (this.data.length == undefined) {
    			return this.data;
    		}
            else {
    			return this.data.slice();
    		}
    	}
    };
    
    // light manager init before global parameters structure is reconfigured
    this.lightManager = new RDGE.LightManager(RDGE.rdgeGlobalParameters.rdge_lights);
    	
    // added getter and setter to global uniforms
    for (var p in RDGE.rdgeGlobalParameters) {
        if (p != "rdge_lights") {
            RDGE.rdgeGlobalParameters[p] = new globalParamFuncSet(RDGE.rdgeGlobalParameters[p]);
		}
        else {
            var lights = RDGE.rdgeGlobalParameters[p];
            for (var l in lights) {
                RDGE.rdgeGlobalParameters[l] = new globalParamFuncSet(lights[l]);
			}
		}
    }
    
    // initial window
    this.lastWindowWidth = window.innerWidth;
    this.lastWindowHeight = window.innerHeight;

    // setup default render context
    this.defaultContext = new RDGE.RenderContext();

    this.defaultContext.uniforms = [
        { 'name': "u_matAmbient", 'value': [0.02,0.02,0.02, 1.0] },
        { 'name': "u_matDiffuse", 'value': [1.0, 1.0, 1.0, 1.0] },
        { 'name': "u_matSpecular", 'value': [1.0, 1.0, 1.0, 1.0] },
        { 'name': "u_matShininess", 'value': [128.0] },
        { 'name': "u_matEmission", 'value': [0.0, 0.0, 0.0, 1.0] }
    ];
    
	// startup the contexts
    contextManager.start();
		
    this.initializeComplete = true;
};

// shutdown the engine clears all states
RDGE.Engine.prototype.Shutdown = function () {
    this.PopAll();
};

// initialize WebGL
RDGE.Engine.prototype.GlInit = function (canvasObject) {
    // Initialize
    var canvases = document.getElementsByTagName("canvas");
    
    // transverse the canvases and create the contexts
    var numCv = canvases.length;
    for (var cvIdx = 0; cvIdx < numCv; ++cvIdx) {
	    var canvas;
		
		// if this canvas has a rdge attribute initialize the render context
		var rdgeAttr = canvases[cvIdx].getAttribute("rdge");
        if (rdgeAttr == "true") {
			// hack ~ while implementing multi-context
			canvas = canvases[cvIdx];
			this.registerCanvas(canvas);
		}

    }
/*
    canvas.addEventListener("webglcontextlost", contextLostHandler, false);
    canvas.addEventListener("webglcontextrestored", contextRestoredHandler, false);
*/
};
   
RDGE.Engine.prototype.loadScene = function (name) {
	var url = "assets_web/mesh/" + name + ".json"
	
	// if we are not in the load state than push it on again
    if (contextManager.currentCtx.stateMan.currentState().name == "RunState") {
		contextManager.currentCtx.stateMan.PushState(contextManager.currentCtx.stateMan.RDGEInitState);
		contextManager.currentCtx.loadScene(url, name);
	}
};
	
RDGE.Engine.prototype.getScene = function (name) {
	return contextManager.currentCtx.sceneGraphMap[name];
};

RDGE.Engine.prototype.AddScene = function (name, sceneGraph) {
	contextManager.currentCtx.sceneGraphMap[name] = sceneGraph;
	contextManager.currentCtx.currentScene = name;
};

RDGE.Engine.prototype.registerCanvas = function (canvas, runState) {
    if (canvas && this.getContext(canvas.rdgeid))
	    return;
 
    canvas.renderer = new RDGE._renderer(canvas); 	// create the renderer for the context
    this.canvasToRendererMap[canvas.rdgeid] = canvas; // store the canvas in the context map
    canvas.renderer.id = canvas.rdgeid;

    // configure the state manager for this context
    var stateMan = new RDGE.stateManager();

    // add this context to the contextManager and attach the handle to DOM canvas for user retrieval
    var context = new contextDef();

    context.id = canvas.rdgeid;
    context.renderer = canvas.renderer;
    context.ctxStateManager = stateMan;

    context.renderer.mvMatrix = RDGE.mat4.identity();
    context.renderer.invMvMatrix = RDGE.mat4.identity();
    context.renderer.projectionMatrix = RDGE.mat4.identity();
    context.renderer.normalMatrix = RDGE.mat4.identity();

    canvas.rdgeCtxHandle = contextManager.addObject(context);

    // set new context as the current context so that when the runtime object is instantiated
    // it can use the context during construction
    var oldCtx = contextManager.currentCtx;
    contextManager.currentCtx = context;

    var _runState;

    // check for runtime handlers
    if (runState) {
        _runState = runState;
    }
    else {
        var runAttr = canvas.getAttribute("rdgerun");

        if (runAttr) {
            // make sure attribute is valid
            if (!isValidObj(runAttr))
                return;
            try {
                var state = eval(runAttr);
                _runState = new state();
            }
            catch (err) {
                window.console.error("The provided RDGE state object \"" + runAttr + "\" is not defined");
            }
        }
        else {
            _runState = {};
            RDGE.utilities.validateUserState(_runState);
        }
    }

    // check for a scene
    var sceneName = canvas.getAttribute("rdgescene");

    // setup the RDGE states passing the user state or undefined
    stateMan.RDGEInitState = new RDGE.LoadState(_runState, context);
    stateMan.RDGERunState = new RDGE.RunState(_runState, context);

    // fill out any user state missing methods with dummy methods
    RDGE.utilities.validateUserState(_runState);

    if (sceneName) {
        stateMan.RDGEInitState.sceneName = sceneName;

        // run is now always the bottom state, loading can happen at any time
        stateMan.PushState(stateMan.RDGERunState, "noInit");

        context.startUpState = stateMan.RDGEInitState;
    }
    else {
        context.startUpState = stateMan.RDGERunState;
    }

    if (this.initializeComplete) {
        context.ctxStateManager.PushState(context.startUpState);
    }

    // restore previous context
    // NOTE: Ninja requires this to be commented out!
    //	if (oldCtx)
    //	{
    //		contextManager.currentCtx = oldCtx;
    //	}
};

RDGE.Engine.prototype.unregisterCanvas = function (canvas) {
     contextManager.removeObject(canvas.rdgeCtxHandle);
     this.clearContext( canvas.rdgeid );
};


RDGE.Engine.prototype.getCanvas = function (id)
{
	return this.canvasToRendererMap[id];
};