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

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

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

    ///////////////////////////////////////////////////////////////////////
    // 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" );

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

	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.export = function()
	{
		// this function should be overridden by subclasses
		var exportStr = "material: " + this.getShaderName() + "\n" + "endMaterial\n";
		return exportStr;
	}

	this.import = function( importStr )
	{
		var endKey = "endMaterial\n";
		var index = importStr.indexOf( endKey );
		index += endKey.length;
		var rtnStr = importStr.substr( index );

		return rtnStr;
	}

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