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

// RDGE namespaces
var RDGE = RDGE || {};

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

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

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

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

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

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

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

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

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

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

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

/*
*	Takes an object and attaches transform node
 *	functions and fields if they are not defined
 */
RDGE.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 = RDGE.mat4.identity();
	}
	
    if (!trNode.world) {
        trNode.world = RDGE.mat4.identity();
	}
	
    if (!trNode.id) {
        trNode.id = RDGE.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 = RDGE.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) {
        RDGE.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 = RDGE.createMeshNode(node, primitive)
	    }
        if (trNode.materialNode == undefined) {
            trNode.materialNode = RDGE.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;
	        
        RDGE.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;
	        
        RDGE.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
RDGE.materialNodeTemplate = function (matNode) {
	// enumerate constants

	// type definitions
	TEX_DIF  = 0;
	TEX_SPEC = 1;
	TEX_NORM = 2;
	TEX_GLOW = 3;
	
    if (!matNode.nodeType) {
        matNode.nodeType = RDGE.rdgeConstants.nodeType.MATNODE;
	}
	
    MATERIAL_MAX_LIGHTS = RDGE.rdgeConstants.MAX_MATERIAL_LIGHTS;
	
    if (!matNode.lightChannel) {
		matNode.lightChannel = 
		[
			null,
			null,
			null,
			null
		];
	}
	
	/*
	 *	Material categories determine sorting
	 */
    if (!matNode.sortCategory) {
        matNode.sortCategory = RDGE.rdgeConstants.categoryEnumeration.OPAQUE;
	}
	
	/*
	 *	every node has an id either generated by export or generated here
	 */
    if (!matNode.id) {
        matNode.id = RDGE.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 = RDGE.globals.engine.getContext().renderer;
	    matNode.textureList = 
	    [
        //	        {'name':"colMap",	'handle':renderer.getTextureByName("assets/images/white"),         'unit': TEX_DIF,    "type":RDGE.UNIFORMTYPE.TEXTURE2D},
        //		    {'name':"envMap",	'handle':renderer.getTextureByName("assets/images/material_paint"),'unit': TEX_SPEC,   "type":RDGE.UNIFORMTYPE.TEXTURE2D},
        //		    {'name':"normalMap",'handle':renderer.getTextureByName("assets/images/blue"),          'unit': TEX_NORM,   "type":RDGE.UNIFORMTYPE.TEXTURE2D},
        //		    {'name':"glowMap",	'handle':renderer.getTextureByName("assets/images/black"),         'unit': TEX_GLOW,   "type":RDGE.UNIFORMTYPE.TEXTURE2D}
	    ];
	}
	
    if (!matNode.uniforms) {
	    matNode.uniforms = [];
	}
	
    matNode.setTexture = function (texType, texName) {
        var renderer = RDGE.globals.engine.getContext().renderer;
        this.textureList[texType].handle = renderer.getTextureByName("assets/images/" + texName);
        this.textureList[texType].handle = renderer.getTextureByName(RDGE.globals.engine._assetPath+"/images/" + texName);
        this.textureList[texType].unit = texType;
        this.textureList[texType].type = RDGE.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) {
        RDGE.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;
    };
};

RDGE.meshNodeTemplate = function (meshNode, primitive, meshName) {
    if (!primitive.built) {
        renderer.createPrimitive(primitive);
    }
    
    if (!meshNode.nodeType) {
        meshNode.nodeType = RDGE.rdgeConstants.nodeType.MESHNODE;
	}
    
    if (!meshNode.attribs) {
        var newID = RDGE.nodeIdGen.getId();
        
        meshNode.attribs = { 'id': newID,
        'indexCount': primitive.indexCount,
        'name': meshName,
            'vertCount': primitive.posCount
        };
        
        meshNode.name = meshName;
    }
    
    if (!meshNode.bbox) {
        meshNode.bbox = new RDGE.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");
    }
}

RDGE.lightNodeTemplate = function (lightNode) {
    if (!lightNode.nodeType) {
        lightNode.nodeType = RDGE.rdgeConstants.nodeType.LIGHTNODE;
	}
	
    if (!lightNode.id) {
        lightNode.id = RDGE.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];
		}
    }
};