// Liquid3D - Moteur 3D C++ NDS par Morukutsu
// Surcouche de l'ulibrary de Brunni
// Version alpha 1

#include "CMD2MeshNode.h"

namespace L3D
{
	vec3_t CMD2MeshNode::anorms_table[162] = 
	{ 
#include "Anorms.h" 
	};

	v10    CMD2MeshNode::anormtable16[162][3];
	bool   CMD2MeshNode::isInited = false;

	CMD2MeshNode::CMD2MeshNode()
	{
		//Chargement des normales
		if(isInited == false)
		{
			for (int a = 0; a < 162; a++)
			{
				anormtable16[a][0] = floattov10(anorms_table[a][0]);
				anormtable16[a][1] = floattov10(anorms_table[a][1]);
				anormtable16[a][2] = floattov10(anorms_table[a][2]);
			}
			isInited = true;
		}

		//Inits divers
		alpha = 31;
        polygroup = 0;
		isTransition = false;
		transition = 0;
		transTimer = -1;
		color = 32767;
        frameCounter = interFrameCounter = currentFrame = 0;
        animNbr = 0;
        defaultAnim = 0;
        currentAnim = -1;
        isStarted = isPaused = isLoop = isInterpolation = false;
        isTexture = false;
        model = new MD2Entity;
        isOutline = false;
		onceMode = ONCE_STAYLAST;
		
		mNodeType = NODETYPE_MD2MESH;
	}

	CMD2MeshNode::~CMD2MeshNode()
	{

	}

	//Fonction d'affichage
	void CMD2MeshNode::Draw()
	{
		glPushMatrix();
			//Déplacement de l'ensemble des objets
			glTranslate3f32(mPosEns.x, mPosEns.y, mPosEns.z);
			MATRIX_SCALE = mScaleEns.x; MATRIX_SCALE = mScaleEns.y; MATRIX_SCALE = mScaleEns.z;
			//glRotatef32(1.0f, mRotEns.x, mRotEns.y, mRotEns.z);
			glRotateXi(mRotEns.x);
			glRotateYi(mRotEns.y);
			glRotateZi(mRotEns.z);
			
			glPushMatrix();
				//Déplacement de cet objet en particulier
				glTranslate3f32(mPosObj.x, mPosObj.y, mPosObj.z);
				MATRIX_SCALE = mScaleObj.x; MATRIX_SCALE = mScaleObj.y; MATRIX_SCALE = mScaleObj.z;
				glRotateXi(mRotObj.x);
				glRotateYi(mRotObj.y);
				glRotateZi(mRotObj.z);

				int n = currentFrame;
				if (n < 0)
					n = 0;
				//Déclaration d'un pointeur vers un vertex
				nds_vertex_t *nextvert = NULL;
				nds_vertex_t *vert = NULL;
				if(currentAnim != -1)
				{
					if(n == animations[currentAnim].endFrame)
						nextvert = model->frms[animations[currentAnim].startFrame].verts;
					else
						nextvert = model->frms[n+1].verts;
				}
				if(isTransition == true)
					vert = model->frms[transition].verts;
				else
					vert = model->frms[n].verts;

				ulVertexColor(32767);
				glPolyFmt(POLY_ALPHA(alpha) | POLY_ID(polygroup) | POLY_CULL_FRONT | POLY_FORMAT_LIGHT0 | POLY_FORMAT_LIGHT1 | POLY_FORMAT_LIGHT2 | POLY_FORMAT_LIGHT3 ) ;
				glMaterialf(GL_AMBIENT, mAmbientColor);
				glMaterialf(GL_DIFFUSE, color);
				glMaterialf(GL_SPECULAR, mSpecularColor | BIT(15)); // notice the additional BIT(15)!
				glMaterialf(GL_EMISSION, mEmissiveColor);
				//glMaterialShinyness();
				if(!isTexture)
					ulDisableTexture();
				else
					ulSetTexture(img);

				//Affichage
				ulVertexBegin(GL_TRIANGLES);
				if (!isInterpolation)
				{
					//Dessin des vertex
					for (int i = 0; i < model->header.num_tris; ++i)
					{
						for (int j = 0; j < 3; ++j)
						{
							int stnum = model->tris[i].st[j];
							int vertnum = model->tris[i].vertex[j];
							
							glNormal(NORMAL_PACK(anormtable16[vert[vertnum].normalindex][0], anormtable16[vert[vertnum].normalindex][1], anormtable16[vert[vertnum].normalindex][2]));
							glTexCoord2t16(model->uvs[stnum].s, model->uvs[stnum].t);
							DSVertex103v10(vert[vertnum].v[0], vert[vertnum].v[1], vert[vertnum].v[2] );
						}
					}
				}
				else
				{
					//Dessin des vertex
					for (int i = 0; i < model->header.num_tris; ++i)
					{
						for (int j = 0; j < 3; ++j)
						{
							int stnum = model->tris[i].st[j];
							int vertnum = model->tris[i].vertex[j];

							glTexCoord2t16(model->uvs[stnum].s, model->uvs[stnum].t);

							//On calcule le pourcentage
							v10 interpol = animations[currentAnim].pas*frameCounter;
							v10 vx = Mulfx(interpol, nextvert[vertnum].v[0] - vert[vertnum].v[0] );
							v10 vy = Mulfx(interpol, nextvert[vertnum].v[1] - vert[vertnum].v[1] );
							v10 vz = Mulfx(interpol, nextvert[vertnum].v[2] - vert[vertnum].v[2] );
							
							v10 vnx = Mulfx(interpol, anormtable16[nextvert[vertnum].normalindex][0] - anormtable16[vert[vertnum].normalindex][0] );
							v10 vny = Mulfx(interpol, anormtable16[nextvert[vertnum].normalindex][1] - anormtable16[vert[vertnum].normalindex][1] );
							v10 vnz = Mulfx(interpol, anormtable16[nextvert[vertnum].normalindex][2] - anormtable16[vert[vertnum].normalindex][2] );

							glNormal(NORMAL_PACK(anormtable16[vert[vertnum].normalindex][0] + vnx, anormtable16[vert[vertnum].normalindex][1] + vny, anormtable16[vert[vertnum].normalindex][2] + vnz));

							DSVertex103v10(vert[vertnum].v[0] + vx, vert[vertnum].v[1] + vy, vert[vertnum].v[2] + vz);
						}
					}
				}
				ulVertexEnd();
				glPolyFmt(POLY_ALPHA(31) ) ;
				ulVertexColor(32767);
				ulDisableTexture();
			glPopMatrix(1);
			//Affichage des enfants
			for(unsigned int k = 0; k < mChildrenCount; k++)
				mChildren[k]->Draw();
		glPopMatrix(1);
	}

	void CMD2MeshNode::Update()
	{
		Play();
	}

	void CMD2MeshNode::Play()
    {
        if (!isPaused && isStarted && animNbr > 0)
        {
            //On update le framecounter
			frameCounter++;
			
			//On update le counter des transition
			if(transTimer >= 0)
				transTimer++;
			if(transTimer >= 10)
			{
				transTimer = -1;
				isTransition = false;
			}
            //On vérifie l'état du framecounter
            if (frameCounter > animations[currentAnim].fps)
            {
                frameCounter = 0;
                currentFrame++;
            }

            //On vérifie l'état de la frame courante (cas loop)
            if (isLoop && currentFrame > animations[currentAnim].endFrame)
                currentFrame = animations[currentAnim].startFrame;

            //On vérifie si l'animation est finie
            if (!isLoop)
            {
                if (currentFrame >= animations[currentAnim].endFrame)
                {
                    this->Stop();
                    if (onceMode == ONCE_STAYFIRST)
                    {
                        currentFrame = animations[currentAnim].startFrame;
                    }
                    if (onceMode == ONCE_STAYLAST)
                    {
                        currentFrame = animations[currentAnim].endFrame;
                    }
                }
            }
        }
    }

	void CMD2MeshNode::AssignModel(MD2Entity* Model)
    {
        model = Model;
    }

	void CMD2MeshNode::AssignImage(UL_IMAGE* Image)
	{
		img = Image;
		isTexture = true;
	}

    void CMD2MeshNode::Interpolate(bool trigger)
    {
        isInterpolation = trigger;
    }

    int CMD2MeshNode::AddAnim(u8 sta, u8 end, u8 fps)
    {
        //Déclaration d'une structure anim temporaire
        sModelAnim tmpAnim = {sta, end, fps, 256/fps};
        //Ajout au vector
        animations[animNbr] = tmpAnim;
        animNbr++;
        return animNbr-1;
    }

    void CMD2MeshNode::PlayAnim(int mode, int anim, bool isTrans)
    {
        //Reset de l'ancienne animation
        switch (mode)
        {
        case ANIM_LOOP:
            //Reset de l'ancienne animation
            if (currentAnim != anim)
            {
                frameCounter = 0;
				transition = currentFrame;
				isTransition = isTrans;
				transTimer = 0;
                currentFrame = animations[anim].startFrame;
                //Démarrage d'une animation
                isStarted = true;
                isLoop = true;
                //Settings
                currentAnim = anim;
            }
            break;

        case ANIM_ONCE:
            if (currentAnim != anim)
            {
                frameCounter = 0;
				transition = currentFrame;
				isTransition = isTrans;
				transTimer = 0;
                currentFrame = animations[anim].startFrame;
                //Démarrage d'une animation
                isStarted = true;
                isLoop = false;
                //Settings
                currentAnim = anim;
            }
            break;
        }
    }

    void CMD2MeshNode::SetAnimationSpeed(u8 animation, int speed)
    {
        animations[animation].fps = speed;
        animations[animation].pas = 256/speed;
    }

    void CMD2MeshNode::Pause()
    {
        isPaused = true;
    }

    void CMD2MeshNode::Restart()
    {
        isPaused = false;
    }

    void CMD2MeshNode::Stop()
    {
        frameCounter = 0;
        currentFrame--;
        isStarted = false;
    }

	void CMD2MeshNode::Tint(int c)
	{
		color = c;
	}

	void CMD2MeshNode::Outline(int color, bool trigger)
    {
        isOutline = trigger;
        outlineColor = color;
    }

    void CMD2MeshNode::SetOnceMonde(int mode)
    {
        onceMode = mode;
    }

	void CMD2MeshNode::Alpha(int _alpha, int _polygroup)
	{
		alpha = _alpha;
		polygroup = _polygroup;
	}
};