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

///////////////////////////////////////////////////////////////////////
// Class GLMaterial
//      GL representation of a material.
///////////////////////////////////////////////////////////////////////
var Material = function GLMaterial( world ) {
    ///////////////////////////////////////////////////////////////////////
    // Instance variables
    ///////////////////////////////////////////////////////////////////////
	this._name = "GLMaterial";
	this._shaderName = "undefined";

	// keep a reference to the owning GLWorld
	this._world = null;
    if(world) {
        this._world = world;
    }

	this._shininess = 60;
		
	this._ambient  = [0.0, 0.0, 0.0,  1.0];
	this._diffuse  = [0.0, 0.0, 0.0,  1.0];
	this._specular = [0.0, 0.0, 0.0,  1.0];

	this._texture = null;

	// vertex deformation variables
	this._hasVertexDeformation = false;
	this._vertexDeformationRange = [0, 0, 1, 1];	// (xMin, yMin, xMax, yMax)
	this._vertexDeformationTolerance = 0.1;

	// RDGE variables
	this._shader = null;
	this._materialNode = null;

    ///////////////////////////////////////////////////////////////////////
    // Property Accessors
    ///////////////////////////////////////////////////////////////////////
	this.getShininess = function() {
        return this._shininess;
    };

	this.setShininess = function(s) {
        this._shininess = s;
    };

	this.setName = function(n) {
        this._name = n;
    };

	this.getName = function() {
        return this._name;
    };

	this.setShaderName = function(n) {
        this._shaderName = n;
    };

	this.getShaderName = function() {
        return this._shaderName;
    };

	this.setWorld = function(world) {
        this._world = world;
    };

	this.getWorld = function() {
        return this._world;
    };

	this.setAmbient	= function(r, g, b, a)	{
        this._ambient = [r, g, b, a];
    };

	this.getAmbient = function() {
        return [this._ambient[0], this._ambient[1], this._ambient[2], this._ambient[3]];
    };

	this.setDiffuse	= function(r, g, b, a)	{
        this._diffuse = [r, g, b, a];
    };

	this.getDiffuse	= function() {
        return [this._diffuse[0], this._diffuse[1], this._diffuse[2], this._diffuse[3]];
    };

	this.setSpecular = function(r, g, b, a)	{
        this._specular = [r, g, b, a];
    };

	this.getSpecular = function() {
        return [this._specular[0], this._specular[1], this._specular[2], this._specular[3]];
    };

	this.getShader = function() {
        return this._shader;
    };

	this.getMaterialNode = function() {
        return this._materialNode;
    };

	// a material can be animated or not. default is not.  
	// Any material needing continuous rendering should override this method
	this.isAnimated	= function() {
        return false;
    };

	// the vertex shader can apply deformations requiring refinement in
	// certain areas.
	this.hasVertexDeformation = function()	{
        return this._hasVertexDeformation;
    };

	this.getVertexDeformationRange = function()	{
        return this._vertexDeformationRange.slice();
    };

	this.getVertexDeformationTolerance = function()	{
        return this._vertexDeformationTolerance;
    };

    ///////////////////////////////////////////////////////////////////////
    // Common Material Methods
    ///////////////////////////////////////////////////////////////////////
	this.getProperty = function( propName ) {
		return this._propValues[propName];
	};

	this.getPropertyCount = function() {
		return this._propNames.length;
	};

	this.getPropertyAtIndex = function( index ) {
		var rtnArr = [];
		if ((index < 0) || (index >= this.getPropertyCount())) {
			throw new Error( "property index " + index + " is out of range for material" );
        }

		return [ this._propNames[index],  this._propLabels[index],  this._propTypes[index],  this._propValues[index] ];
	};

	this.getAllProperties = function( propNames,  propValues,  propTypes,  propLabels) {
		// clear all the input arrays if there is junk in them
		propNames.length	= 0;
		propValues.length	= 0;
		propTypes.length	= 0;
		propLabels.length	= 0;

		var nProps = this._propNames.length;
		for (var i=0;  i<nProps;  i++) {
			propNames[i]	= this._propNames[i];
			propValues[i]	= this._propValues[this._propNames[i]];
			propTypes[i]	= this._propTypes[i];
			propLabels[i]	= this._propLabels[i];
		}
	};

	this.validateProperty = function( prop, value ) {
		var rtnVal = false;
		try
		{
			//if (!this._propValues[prop])  return false;

			// find the index of the property
			var n = this._propNames.length;
			var valType =  typeof value;
			for (var i=0;  i<n;  i++) {
				if (this._propNames[i] == prop) {

					switch (this._propTypes[i])
					{
						case "color":
							rtnVal = ((valType == "object") && (value.length >= 4));
							break;

						case "vector2d":
							rtnVal = ((valType == "object") && (value.length >= 2));
							break;
							
						case "vector3d":
							rtnVal = ((valType == "object") && (value.length >= 3));
							break;

						case "float":
							rtnVal = (valType == "number");
							break;

						case "file":
							rtnVal = ((valType == "string") || !value);
							break;
					}

					break;
				}
			}
		}
		catch(e)  {
			console.log( "setting invalid material property: " + prop + ", value: " + value );
		}
		
		if (!rtnVal) {
			console.log( "invalid material property: " + prop + " : " + value );
        }

		return rtnVal;
	};
    ///////////////////////////////////////////////////////////////////////
    
    ///////////////////////////////////////////////////////////////////////
    // Methods
    ///////////////////////////////////////////////////////////////////////
	// duplcate method required by sub class
	this.dup = function() {
		throw new Error( "Material.dup() must be overridden by subclass" );
	};

	this.init = function( world ) {
		throw new Error( "Material.init() must be overridden by subclass" );
	};

	this.update = function( time ) {
		// animated materials should implement the update method
	};

	this.registerTexture = function( texture ) {
		// the world needs to know about the texture map
		var world = this.getWorld();
		if (!world) {
			console.log( "**** world not defined for registering texture map: " + texture.lookUpName );
        } else {
			world.textureToLoad( texture );
        }
	};

	this.loadTexture = function( texMapName, wrap, mips ) {
		var tex;
		var world = this.getWorld();
		if (!world) {
			console.log( "world not defined for material with texture map" );
        } else {
			var renderer = world.getRenderer();
			tex = renderer.getTextureByName(texMapName, wrap, mips );
			this.registerTexture( tex );
		}

		return tex;
	};

	/*
	this.setRenderProperties = function( glContext, shaderProgram )
	{
		glContext.uniform1f( shaderProgram.materialShininessUniform, this._shininess );

		if (this._texture)
			this.prepareTextureForRender( 0 );
		else
			glContext.uniform1i( shaderProgram.useTextureUniform,	false  );

		var amb = this._ambient,  diff = this._diffuse,  spec = this._specular;
		glContext.uniform4f( shaderProgram.materialAmbientUniform,		 amb[0],  amb[1],  amb[2],  amb[3]);
		glContext.uniform4f( shaderProgram.materialDiffuseUniform,		diff[0], diff[1], diff[2], diff[3]);
		glContext.uniform4f( shaderProgram.materialSpecularUniform,		spec[0], spec[1], spec[2], spec[3]);
	}
 
 
 
    this.prepareTextureForRender = function ( index )
	{
		// we will need to be able to handle multiple textures.
		// currently only dealing with 1.
		index = 0;
		var texture = this._texture;

		var gl = this.getWorld().getGLContext();
		var shaderProgram = this.getWorld().getShaderProgram();

		gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture);
		gl.uniform1i(shaderProgram.samplerUniform, 0);
		gl.uniform1i( shaderProgram.useTextureUniform,	true  );
    }
 
    this.textureLoadHandler = function (event)
	{
		var texture = this._texture;

		var gl = this._world.getGLContext();
		var shaderProgram = this._world.getShaderProgram();

		gl.activeTexture(gl.TEXTURE0);
        gl.bindTexture(gl.TEXTURE_2D, texture);
        gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
        gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
        gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);

//		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
//		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
		gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);

        gl.bindTexture(gl.TEXTURE_2D, null);

		this._material._texture = this._texture;
		this._world.render();
	}
 
 
    this.loadTexture = function( path )
	{
        var gl = this.getWorld().getGLContext();
		var tex = gl.createTexture();
        tex.image = new Image();
		tex.image._world = this._world;
		tex.image._material = this;
		tex.image._texture = tex;
        tex.image.onload = this.textureLoadHandler;
        tex.image.src = path;
    }
	*/
};

if (typeof exports === "object") {
    exports.Material = Material;
}