/* <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> */


/*
 *	Creates a unique node id
 */
 nodeIdGen = {};
 
 nodeIdGen.counter = 0;
 
 nodeIdGen.getId = function()
 {
	return  "gen_" + nodeIdGen.counter++;
 }

/*
 *	Constructs a new transform node
 */
function createTransformNode(nodeName)
{
	node = { 'name': nodeName };
	
	node.transformNodeTemplate = new transformNodeTemplate(node);
	
	return node;
}

/*
 *	Constructs a new material node
 */
function createMaterialNode(nodeName)
{
	node = { 'name': nodeName };
	
	node.materialNodeTemplate = new materialNodeTemplate(node);
	
	return node;
}

/*
 *	Constructs a new mesh node
 */
function createMeshNode(nodeName, primitive)
{
	meshNode = { 'mesh':{}, 'meshNodeTemplate':{} };
	
	var renderer = g_Engine.getContext().renderer;
		
	if(!primitive.built)
    {
        renderer.createPrimitive(primitive);
    }

    var model = g_meshMan.getModelByName(nodeName);
    if(!model)
    {
        meshNode.mesh.meshNodeTemplate = new meshNodeTemplate(meshNode.mesh, primitive, nodeName);

        g_meshMan.modelMap[nodeName] = meshNode.mesh;
        return meshNode; // --early out--
    }
    else if(!renderer.buffers[model.primitive.buffersID])
	{
		renderer.createPrimitive(model.primitive);
	}
	
    meshNode.mesh.meshNodeTemplate = new meshNodeTemplate(meshNode.mesh, model.primitive, nodeName);
	
	
	return meshNode;
}

/*
 *	Construct a light node
 */
function createLightNode(nodeName)
{
    node = { 'name': nodeName };
	node.lightNodeTemplate = new lightNodeTemplate(node);
	
	return node;
}

/*
 *	creates a specialized mesh node representing a screen aligned quad with identity transform
 */
function createScreenQuadNode()
{
	var trNode = createTransformNode();
	trNode.attachMeshNode("screenQuad", createScreenAlignedQuad());
	return trNode; 
}

function verifyTransformNode( node )
{
    if(node.transformNodeTemplate == undefined)
    {
        node.transformNodeTemplate = new transformNodeTemplate(node);
    }
}

function verifyMaterialNode( node )
{
    if(node.materialNodeTemplate == undefined)
    {
        node.materialNodeTemplate = new materialNodeTemplate(node);
    }
}

function verifyLightNode( node )
{
    if(node.lightNodeTemplate == undefined)
    {
        node.lightNodeTemplate = new lightNodeTemplate(node);
    }
}


/*
 *	Takes an object and attachs transform node
 *	functions and fields if they are not defined
 */
transformNodeTemplate = function(trNode)
{
    // Lots of checking for things that might exist and adding them when they don't
    
    /* ------------------------------------------- */
	if(!trNode.children)
	{
		trNode.children = [];
	}
	
	if(!trNode.local)
	{
		trNode.local = mat4.identity();
	}
	
	if(!trNode.world)
	{
		trNode.world = mat4.identity();
	}
	
	if(!trNode.id)
	{
		trNode.id = nodeIdGen.getId();
	}
	
	if(!trNode.name)
	{
		trNode.name = "xfrmNode" + trNode.id;
	}
	
	if(!trNode.parent)
	{
	    trNode.parent = null;
	}
		
	if(!trNode.meshes)
	{
		trNode.meshes = [];
	}
	
	if(!trNode.nodeType)
	{
		trNode.nodeType = rdgeConstants.nodeType.TRNODE;
	}
	
    /* ------------------------------------------- */
    
    // Adding functions to the node none of these exist from export process	
	/*
	 *	Attaches a material to a node
	 */
	trNode.attachMaterial = function(matNode)
	{
	    verifyMaterialNode(matNode);
	    
		this.materialNode = matNode;
	}
	
	/*
	 *	@param node - the node to attach, can optionally be a node name paired with a primitive that will be built into a meshNode
	 *	@param primitive - an optional parameter that must be supplied if the node is a name and not an object
	 */
	trNode.attachMeshNode = function( node, primitive )
	{
		if(typeof node == "string")
		{
		    node = createMeshNode(node, primitive)
	    }
	    if(trNode.materialNode == undefined)
		{
			trNode.materialNode = createMaterialNode(trNode.name + "|defMaterial");
		}
	    
		trNode.meshes.push( {'mesh':{'name':node.mesh.attribs.name, 'id':node.mesh.attribs.id}});
	}
	
	/*
	 *	Inserts a node as a child of this node
	 */
	trNode.insertAsChild = function(transNode)
	{
	    if(this == transNode)
	        return;
	        
	   verifyTransformNode(transNode);
	    
	    transNode.parent = this;
	    this.children.push({transformNode:transNode});
	}
	
	/*
	 *	Inserts a node as the parent of this node
	 */
	trNode.insertAsParent = function(transNode)
	{
	    if(this == transNode)
	        return;
	        
	    verifyTransformNode(transNode);
	    
	    if(this.parent)
	    {
	        // remove this node from current parents list
	        var len = this.parent.children.length;
	        for(var i = 0; i < len;  ++i)
	        {
	            if(this.parent.children[i].transformNode != undefined)
	            {
	                tr = this.parent.children[i].transformNode;
	                if(tr.id == this.id)
	                {
	                    // removes 1 item starting from i
	                    this.parent.children.splice(i,1);
	                    break;
	                }
	            }
	            
	        }
	        
	        // set the new parents parent
	        transNode.parent = this.parent;
	        
	        // push passed in node into parents list
	        this.parent.children.push({transformNode:transNode});
	        
	        // set the passed in node as the current parent
	        this.parent = transNode;
	    }
	    
	    // add this node to the passed in nodes child list
	    transNode.children.push({transformNode:this});
	}

}


// add material handlers to a material node
materialNodeTemplate = function(matNode)
{
	// enumerate constants

	// type definitions
	TEX_DIF  = 0;
	TEX_SPEC = 1;
	TEX_NORM = 2;
	TEX_GLOW = 3;
	
	if(!matNode.nodeType)
	{
		matNode.nodeType = rdgeConstants.nodeType.MATNODE;
	}
	
	MATERIAL_MAX_LIGHTS = rdgeConstants.MAX_MATERIAL_LIGHTS;
	
	if(!matNode.lightChannel)
	{
		matNode.lightChannel = 
		[
			null,
			null,
			null,
			null
		];
	}
	
	/*
	 *	Material categories determine sorting
	 */
	if(!matNode.sortCategory)
	{
		matNode.sortCategory = rdgeConstants.categoryEnumeration.OPAQUE;
	}
	
	/*
	 *	every node has an id either generated by export or generated here
	 */
	if(!matNode.id)
	{
		matNode.id = nodeIdGen.getId();
	}
	
	/*
	 *	every node has an name either setin art pipeline or generated here
	 */
	if(!matNode.name)
	{
		matNode.name = "matNode" + matNode.id;
	}
	
	/*
	 *	Default list of textures if nothing is set
	 */
	if(!matNode.textureList)
	{
		var renderer = g_Engine.getContext().renderer;
	    matNode.textureList = 
	    [
//	        {'name':"colMap",	'handle':renderer.getTextureByName("assets/images/white"),         'unit': TEX_DIF,    "type":UNIFORMTYPE.TEXTURE2D},
//		    {'name':"envMap",	'handle':renderer.getTextureByName("assets/images/material_paint"),'unit': TEX_SPEC,   "type":UNIFORMTYPE.TEXTURE2D},
//		    {'name':"normalMap",'handle':renderer.getTextureByName("assets/images/blue"),          'unit': TEX_NORM,   "type":UNIFORMTYPE.TEXTURE2D},
//		    {'name':"glowMap",	'handle':renderer.getTextureByName("assets/images/black"),         'unit': TEX_GLOW,   "type":UNIFORMTYPE.TEXTURE2D}
	    ];
	}
	
	if(!matNode.uniforms)
	{
	    matNode.uniforms = [];
	}
	
	matNode.setTexture = function(texType, texName)
	{
		var renderer = g_Engine.getContext().renderer;
		this.textureList[texType].handle = renderer.getTextureByName(g_Engine._assetPath+"/images/" + texName);
	    this.textureList[texType].unit = texType;
		this.textureList[texType].type = UNIFORMTYPE.TEXTURE2D;
		
	}

	matNode.setDiffuseTexture = function(texName)
	{
		this.setTexture(TEX_DIF, texName);
	}
	
	matNode.setSpecTexture = function(texName)
	{
		this.setTexture(TEX_SPEC, texName);
	}
	
	matNode.setNormalTexture = function(texName)
	{
		this.setTexture(TEX_NORM, texName);
	}
	
	matNode.setGlowTexture = function(texName)
	{
		this.setTexture(TEX_GLOW, texName);
	}
	
	matNode.setUniform = function(name, arrValue)
	{
		var len = this.uniforms.length;
		for(var i = 0; i < len; ++i)
		{
			if(this.uniforms[i].name == name)
			{
				this.uniforms[i].value = arrValue;
				return;
			}
		}
		
		window.console.log("Could not find uniform: " + name);
	}
	
	matNode.setShader = function( jshaderObject )
	{
		this.shaderProgram = jshaderObject;
	}
	
	matNode.setSortCategory = function( materialCat )
	{
		matNode.sortCategory = materialCat;
	}
	
	/*
	 * Sets a light channel reference to a lightNode
	 * @param channelNumber a number indicating whick light to turn on (0 - 3), or an array of numbers if multiple lights being set
	 * @param lightNode - a refernce to a light node object or an array lights
	 */
	matNode.enableLightChannel = function( channelNumber, lightNode )
	{
		verifyLightNode(lightNode);
		
		// set an array
		if(typeof channelNumber == "object")
		{
			var len = channelNumber.length;
			var maxLight = lightNode.length != undefined ? lightNode.length : 0;
			for(var i = 0; i < len; ++i)
			{
			
				matNode.lightChannel[channelNumber] = maxLight > 0 ? lightNode[ Math.min(i, maxLight - 1)] : lightNode;
			}
		}
		else // set an individual light
		{
			if(channelNumber < MATERIAL_MAX_LIGHTS)
				matNode.lightChannel[channelNumber] = lightNode;
		}
	}

	
	matNode.disableLightChannel = function( channelNumber )
	{
		if(typeof channelNumber != "object")
		{
			var len = channelNumber.length;
			
			for(var i = 0; i < len; ++i)
			{
				if(channelNumber[i] , MATERIAL_MAX_LIGHTS)
					matNode.lightChannel[channelNumber[i]] = null;
			}
		}
		else
		{
			if(channelNumber < MATERIAL_MAX_LIGHTS)
				matNode.lightChannel[channelNumber] = null;
		}
		
	}
	
	matNode.disableAllLights = function()
	{
		for(var i = 0; i < MATERIAL_MAX_LIGHTS; ++i)
		{
			matNode.lightChannel[i] = null;
		}
	}
	
		
	matNode.toJSON = function()
	{
		var jsonObj = {'jsonExportName':"materialNode"};
		for(var member in this)
		{
			jsonObj[member] = this[member];
			
			if(member === "textureList")
			{
				var texList = jsonObj[member];
				for(var i = 0, len = texList.length; i < len; ++i)
				{
					texList[i].handle.image = texList[i].handle.lookUpName; 
				}
			}
			else if(member === "shaderProgram")
			{
				// test that the shader hasn't already been exported
				if(typeof jsonObj[member] != "string")
				{
					jsonObj[member] = jsonObj[member].exportShader();
				}
			}
		}
		
		return jsonObj;
	}
}


meshNodeTemplate = function( meshNode, primitive, meshName )
{
    if(!primitive.built)
    {
        renderer.createPrimitive(primitive);
    }
    
    if(!meshNode.nodeType)
	{
		meshNode.nodeType = rdgeConstants.nodeType.MESHNODE;
	}
    
    if(!meshNode.attribs)
    {
        var newID = nodeIdGen.getId();
        
        meshNode.attribs = { 'id': newID,
        'indexCount': primitive.indexCount,
        'name': meshName,
        'vertCount': primitive.posCount};
        
        meshNode.name = meshName;
    }
    
    if(!meshNode.bbox)
    {
        meshNode.bbox = new box();
    }
    
    meshNode.data = null;
    
    meshNode.primitive = primitive;
    
    // setup bounding box
    var numPositions = primitive.posCount;
  
    if(numPositions > 0)
    {
        var positions = primitive.positions;
        
        var idx = 0;
        while (idx < numPositions - 2)
        {
          var thisPos = [positions[idx+0], positions[idx+1], positions[idx+2]];
          meshNode.bbox.addVec3(thisPos);
          idx += 3;
        }
    }
    else
    {
		window.console.error("mesh " + meshNode.attribs.name + ": bounding volume not created");
    }
}

lightNodeTemplate = function(lightNode)
{
	if(!lightNode.nodeType)
	{
		lightNode.nodeType = rdgeConstants.nodeType.LIGHTNODE;
	}
	
    if(!lightNode.id)
    {
        lightNode.id = nodeIdGen.getId();
    }
    
    if(!lightNode.name)
    {
        lightNode.name = "light_" + lightNode.id;
    }
    
    if(!lightNode.typeName)
    {
        lightNode.typeName = "dir_light";
    }
    
    if(!lightNode.castShadow)
    {
        lightNode.castShadow = false;
    }
    
    if(!lightNode.depthMapBias)
    {
        lightNode.depthMapBias = 0.0179;
    }
    
    if(!lightNode.depthMapSize)
    {
        lightNode.depthMapSize = 1024;
    }
    
    if(!lightNode.coneAngle)
    {
        lightNode.coneAngle = 0.707;
    }
    
    if(!lightNode.penumbraAngle)
    {
        lightNode.coneAngle = 0.0;
    }
    
    if(!lightNode.dropOff)
    {
        lightNode.coneAngle = 0.025;
    }
    
    if(!lightNode.color)
    {
        lightNode.color = [1,1,1,1];
    }
    
    if(!lightNode.dir)
    {
        lightNode.dir = [1,-1,1];
    }
    
    if(!lightNode.links)
    {
        lightNode.links = [];
    }
    
    if(!lightNode.position)
    {
        lightNode.position = [0,0,0];
    }
    
    if(!lightNode.lightDiffuse)
    {
        lightNode.lightDiffuse = [1,1,1,1];
    }
    
    if(!lightNode.lightAmbient)
    {
        lightNode.lightAmbient = [0.5,0.5,0.5,1.0];
    }
    
    if(!lightNode.lightSpecular)
    {
        lightNode.lightSpecular = [1,1,1,1];
    }
        
    lightNode.setPosition = function( pos )
    {
		for(var i = 0; i < 3; i++)
		{
			this.position[i] = pos[i];
		}
    }
    
    lightNode.setDiffuseColor = function( color )
    {
		for(var i = 0; i < 4; i++)
		{
			this.lightDiffuse[i] = color[i];
		}
    }
    
    lightNode.setAmbientColor = function( color )
    {
		for(var i = 0; i < 4; i++)
		{
			this.lightAmbient[i] = color[i];
		}
    }
    
    lightNode.setSpecularColor = function( color )
    {
		for(var i = 0; i < 4; i++)
		{
			this.lightSpecular[i] = color[i];
		}
    }
}