package com.framsticks.net.client3D;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;

import javax.media.opengl.GL;

import pl.vorg.mowa.core.graphics.Geometry;
import pl.vorg.mowa.core.graphics.GeometryGroup;
import pl.vorg.mowa.core.graphics.SkyBox;

import com.framsticks.net.client3D.graphics.loaders.OBJLoader;
import com.sun.opengl.cg.CGannotation;
import com.sun.opengl.cg.CGcontext;
import com.sun.opengl.cg.CGeffect;
import com.sun.opengl.cg.CGparameter;
import com.sun.opengl.cg.CGpass;
import com.sun.opengl.cg.CGtechnique;
import com.sun.opengl.cg.CgGL;
import com.sun.opengl.util.texture.Texture;
import com.sun.opengl.util.texture.TextureIO;

public class cgfxEffect {
	static CGcontext cgContext;
	public class cgParam
	{
		cgParam() {use = false;}
		CGparameter param;
		CGannotation ann;
		boolean	use;
	}
	public class cgTexture
	{
		Texture glTex;
		CGparameter param;
		void LoadTex(String fileName) throws Exception
		{
			glTex = TextureIO.newTexture(new File(fileName), false);
			glTex.bind();
			CgGL.cgGLSetTextureParameter(param,glTex.getTextureObject());
			CgGL.cgSetSamplerState(param);
			//glTex.setTexParameteri(GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT);
			//glTex.setTexParameteri(GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT);
			//glTex.bind();
			//glTex.enable();
			//CgGL.cgGLSetupSampler(param, glTex.getTextureObject());
			
			//CgGL.cgGLSetTextureParameter(param, glTex.getTextureObject());			
			
		}
	}
	void setSkyBox(SkyBox box){this.box = box;}
	SkyBox box;
	final String DEFAULT_MATERIAL = "res/shaders/default.cgfx";
	final String SHADERS_PATH = "res/shaders/";
	final String IMAGES_PATH = "res/img/";
	final String OBJS_PATH = "res/obj/";
	final String DEFAULT_TEXTURE = "res/img/stone.jpg";
	final String DEFAULT_PART_GEO = "res/obj/part.obj";
	final String DEFAULT_JOINT_GEO = "res/obj/stick5red.obj";
	cgParam m_cgModelView;
	cgParam m_cgModelViewProj;
	cgParam m_cgModelViewIT;
	ArrayList<cgTexture> texArray;
	cgParam m_cgDetailScale;
	cgParam m_cgShiness;
	cgParam m_cgLights;
	CGparameter cgSkybox;
	
	private GeometryGroup partGeo;
    private GeometryGroup jointGeo;
	
	CGtechnique m_cgPartTech;
	CGtechnique m_cgJointTech;
	CGtechnique m_cgTechnique;
	
	CGeffect m_cgEffect;
	boolean m_bIsInited;
	
	
	boolean isInitialized() {return m_bIsInited;}
	String name;
	public boolean Init()
	{
		return LoadByName(name);
	}
	public cgfxEffect(String name)
	{
		m_bIsInited = false;
		this.name = name;
	}
	
	public void render(GL gl, Creature[] creatures, Creature.ModelType modelType) {
		
//		gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, new float[] { 5, -5, 5}, 0);
		
		
		gl.glEnable(GL.GL_LIGHT0);
		gl.glEnable(GL.GL_LIGHTING);
		
		//parts
		
		if (texArray != null && texArray.size() > 0)
		{
			texArray.get(0).glTex.enable();
			texArray.get(0).glTex.bind();
		}
		
		
		CGpass pass = CgGL.cgGetFirstPass(m_cgPartTech);
		int passNum = 0;
		while (pass != null)
		{
			gl.glPushAttrib(GL.GL_ALL_ATTRIB_BITS);
			CgGL.cgSetPassState(pass);
			int j=0;
			for(Creature creature : creatures) {
				float[][] parts = creature.getParts(modelType);
				for(int i=0; i<creature.getParts(modelType).length; i++) {
					j = i;
					if (j >= creature.getJoints().length) j--;
					float[] r = creature.jointRotation(j, modelType);
					gl.glPushMatrix();
					gl.glTranslatef(parts[i][0], parts[i][1], parts[i][2]);
					gl.glRotatef(r[0], r[1], r[2], r[3]);
					gl.glScalef(0.7f, 0.7f, 0.7f);
					
					if (m_cgModelView.use) 
						CgGL.cgGLSetStateMatrixParameter(m_cgModelView.param, CgGL.CG_GL_MODELVIEW_MATRIX,
								CgGL.CG_GL_MATRIX_IDENTITY);
					if (m_cgModelViewIT.use)
						CgGL.cgGLSetStateMatrixParameter(m_cgModelViewIT.param, CgGL.CG_GL_MODELVIEW_MATRIX,
								CgGL.CG_GL_MATRIX_INVERSE_TRANSPOSE);
					if (m_cgModelViewProj.use)
						CgGL.cgGLSetStateMatrixParameter(m_cgModelViewProj.param, CgGL.CG_GL_MODELVIEW_PROJECTION_MATRIX,
								CgGL.CG_GL_MATRIX_IDENTITY);
					
					partGeo.display(gl);
					gl.glPopMatrix();
				}	
			}
			CgGL.cgResetPassState(pass);
			gl.glPopAttrib();
			pass = CgGL.cgGetNextPass(pass);

			passNum++;
		}
		
		if (texArray != null && texArray.size() > 0)
		{
			texArray.get(0).glTex.disable();
		}	
		//joints
		
		if (texArray != null && texArray.size() > 1)
		{
			texArray.get(1).glTex.enable();
			texArray.get(1).glTex.bind();
		}
		
		
		pass = CgGL.cgGetFirstPass(m_cgJointTech);
		while (pass != null)
		{
			CgGL.cgSetPassState(pass);
			for(Creature creature : creatures) {
				int[][] joints = creature.getJoints();
				for(int i=0; i<joints.length; i++) {
					gl.glPushMatrix();
					float[] t = creature.jointTranslation(i, modelType);
					float[] r = creature.jointRotation(i, modelType);
					float l = creature.jointLength(i, modelType);
					gl.glTranslatef(t[0], t[1], t[2]);
					gl.glRotatef(r[0], r[1], r[2], r[3]);
					gl.glScalef(1.0f , 1.0f, l);
					
					if (m_cgModelView.use) 
						CgGL.cgGLSetStateMatrixParameter(m_cgModelView.param, CgGL.CG_GL_MODELVIEW_MATRIX,
								CgGL.CG_GL_MATRIX_IDENTITY);
					if (m_cgModelViewIT.use)
						CgGL.cgGLSetStateMatrixParameter(m_cgModelViewIT.param, CgGL.CG_GL_MODELVIEW_MATRIX,
								CgGL.CG_GL_MATRIX_INVERSE_TRANSPOSE);
					if (m_cgModelViewProj.use)
						CgGL.cgGLSetStateMatrixParameter(m_cgModelViewProj.param, CgGL.CG_GL_MODELVIEW_PROJECTION_MATRIX,
								CgGL.CG_GL_MATRIX_IDENTITY);
					
					jointGeo.display(gl);
					gl.glPopMatrix();
				}		
			}
			CgGL.cgResetPassState(pass);
			pass = CgGL.cgGetNextPass(pass);
		}
		if (texArray != null && texArray.size() > 1)
		{
			texArray.get(1).glTex.disable();
		}	

		gl.glDisable(GL.GL_LIGHT0);
		gl.glDisable(GL.GL_LIGHTING);
	}
	public void DrawMesh(Geometry geom, GL gl)
	{
		geom.display(gl);
	}
	public void SetMatrices()
	{
		
	}
	public void SetMaterialData()
	{
		
	}


	public boolean LoadFromFile(String fileName)
	{
	
		if (cgContext == null)
		{
			cgContext = CgGL.cgCreateContext();
			CgGL.cgGLSetManageTextureParameters(cgContext, true);
			CgGL.cgGLRegisterStates(cgContext);
		}
		//	check if material file exist.
		FileInputStream f;
		try
		{
			f = new FileInputStream(fileName);
			if (f!=null)
				f.close();
		}
		catch(Exception e)
		{
			fileName = DEFAULT_MATERIAL;
		}
		
		
		m_cgEffect = CgGL.cgCreateEffectFromFile(cgContext, fileName, null);
		if (m_cgEffect == null)
		{
			Log.getInstance().log("err","cgfxEffect: Error while loading effect file! "+CgGL.cgGetLastListing(cgContext));
			
			return false;
		}

		//initing parameters
		 CGparameter p = CgGL.cgGetFirstEffectParameter(m_cgEffect);
		 while (p != null) 
		 {
			  if (CgGL.cgGetParameterType(p) == CgGL.CG_SAMPLER2D)
			  {
				//check what type of texture is it ;]
				  Log.getInstance().log("dbg",CgGL.cgGetParameterSemantic(p));
				  CGannotation ann = CgGL.cgGetFirstParameterAnnotation(p);
				  cgTexture newTex = new cgTexture();
				  newTex.param = p;
				  try{
					  newTex.LoadTex(IMAGES_PATH+CgGL.cgGetStringAnnotationValue(ann));
				  }catch(Exception e)
				  {
					  Log.getInstance().log("err","Texture with file name "+CgGL.cgGetStringAnnotationValue(ann)+" not found! " +e.getMessage());
					  try
					  {
						  newTex.LoadTex(DEFAULT_TEXTURE);
					  }
					  catch(Exception ex)
					  {
						  Log.getInstance().log("err","Default texture file not found! "+ex.getMessage());
					  }
				  }
				  
				  if (texArray == null) texArray = new ArrayList<cgTexture>(); 
				  texArray.add(newTex);
				  
			  }
			  else if (CgGL.cgGetParameterType(p) == CgGL.CG_SAMPLERCUBE)
			  {
				  CgGL.cgGLSetupSampler(p, box.getCube());
				  cgSkybox = p;
				  Log.getInstance().log("assigning skybox");
			  }
			  else if (CgGL.cgGetParameterType(p) == CgGL.CG_FLOAT4x4)
			  {
				  if (CgGL.cgGetParameterSemantic(p).equals("MODELVIEW"))
				  {
					  m_cgModelView = new cgParam();
					  m_cgModelView.param = p;
					  m_cgModelView.use	= true;
				  }
				  else if (CgGL.cgGetParameterSemantic(p).equals("MODELVIEWPROJECTION"))
				  {
					  m_cgModelViewProj = new cgParam();					 
					  m_cgModelViewProj.param = p;
					 m_cgModelViewProj.use = true;
				  }
				  else if (CgGL.cgGetParameterSemantic(p).equals("MODELVIEWIT"))
				  {
					  m_cgModelViewIT = new cgParam();
					  m_cgModelViewIT.param = p;
					 m_cgModelViewIT.use = true;
				  }

			  }
			  else if (CgGL.cgGetParameterType(p) == CgGL.CG_STRING)
			  {
				  if (CgGL.cgGetParameterName(p).equals("partGeo"))
				  {
					  try{
					  	partGeo = OBJLoader.load(OBJS_PATH+CgGL.cgGetStringParameterValue(p));
					  }catch(IOException ioe)
					  {
						  Log.getInstance().log("err","Object with file name "+CgGL.cgGetStringParameterValue(p)+" not found!");
						  partGeo = null;
					  }
				  }
				  else if (CgGL.cgGetParameterName(p).equals("jointGeo"))
				  {
					  try{
						  	jointGeo = OBJLoader.load(OBJS_PATH+CgGL.cgGetStringParameterValue(p));
						  }catch(IOException ioe)
						  {
							  Log.getInstance().log("err","Object with file name "+CgGL.cgGetStringParameterValue(p)+" not found!");
							  jointGeo = null;
						  }
				  }
					  
			  }
			  else // (cgGetParameterType(p) == cgGetNamedUserType(m_cgEffectVec[lightNum],"LIGHT"))
			  {
				  if (CgGL.cgGetParameterSemantic(p).equals("lights"))
					 // || strcmp(cgGetParameterSemantic(p),"LIGHTS")==0)
				  {
					  m_cgLights = new cgParam();
					  m_cgLights.param = p;
					  m_cgLights.use = true;
					  //  CGparameter array;
						//CGtype t  = cgGetNamedUserType(g_CEffectManager.GetDefaultEffect(),"PointLight");
						//array = cgCreateParameterArray(g_CEffectManager.GetCGContext(), t, 2);

						//cgConnectParameter(CEffectLight::GetLightsParam(lightNum), m_cgLights[lightNum].param);
				 }
				  else
						Log.getInstance().log("err","cgfxEffect: Unkonwn semantic ");
				  }
			 p = CgGL.cgGetNextParameter(p);
		 }
		
		


		 	m_cgTechnique = CgGL.cgGetFirstTechnique(m_cgEffect);
	    while (m_cgTechnique != null) 
		{
	    	if (!CgGL.cgValidateTechnique(m_cgTechnique))
	        	Log.getInstance().log("err", "Technique " +CgGL.cgGetTechniqueName(m_cgTechnique)+ " did not validate.  Skipping.\n");
	        else
	        {
	        	CGannotation ann = CgGL.cgGetFirstTechniqueAnnotation(m_cgTechnique);
	        	 Log.getInstance().log("dbg",CgGL.cgGetStringAnnotationValue(ann));
	        	if (CgGL.cgGetStringAnnotationValue(ann).equals("parts"))
	        	{
	        		//m_cgJointTech = m_cgTechnique;
	        		m_cgPartTech = m_cgTechnique;
	        	} else if (CgGL.cgGetStringAnnotationValue(ann).equals("joints"))
	        	{
	        		m_cgJointTech = m_cgTechnique;
	        		//m_cgPartTech = m_cgTechnique;
	        	} else if(CgGL.cgGetStringAnnotationValue(ann).equals("both"))
	        	{
	        		m_cgPartTech = m_cgTechnique;
	        		m_cgJointTech = m_cgTechnique;
	        		
	        	}
	        	if (m_cgJointTech != null && m_cgPartTech != null)
	        		break;
	        		        		
	        }
		   m_cgTechnique = CgGL.cgGetNextTechnique(m_cgTechnique);
	    }

	   	if (m_cgJointTech == null) 
		{
	        Log.getInstance().log("err", "No valid 'joints' technique in effect file!");
	        return false;
	    }
	   	if (m_cgPartTech == null) 
		{
	        Log.getInstance().log("err", "No valid 'parts' technique in effect file!");
	        return false;
	    }
	   	if (partGeo == null)
	   	{
	   		try{
			  	partGeo = OBJLoader.load(DEFAULT_PART_GEO);
			  }catch(IOException ioe)
			  {
				  Log.getInstance().log("err","Object with file name "+DEFAULT_PART_GEO+" not found!");
				  partGeo = null;
			  }
	   	}
		if (jointGeo == null)
	   	{
	   		try{
			  	jointGeo = OBJLoader.load(DEFAULT_JOINT_GEO);
			  }catch(IOException ioe)
			  {
				  Log.getInstance().log("err","Object with file name "+DEFAULT_PART_GEO+" not found!");
				  jointGeo = null;
			  }
	   	}
		 //  m_cgTechnique = cgGetFirstTechnique(m_cgEffect);
		m_bIsInited = true;
		return true;
	}
	public boolean LoadByName(String name) 
	{
		String path;
		path = SHADERS_PATH;
		path += name;
		path += ".cgfx";
		return LoadFromFile(path);
		
	}
	
}
