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

#include "CRessourceManager.h"

char pngbuffer[16*1024];

namespace L3D
{
    CRessourceManager::CRessourceManager()
    {
        //MAJ de variables
        img_num = 0;
        mod_num = 0;
        imgList.clear();
        modList.clear();
    }

    CRessourceManager::~CRessourceManager()
    {
        //Nettoyage
        imgList.clear();
        modList.clear();
    }

    UL_IMAGE* CRessourceManager::LoadImage(string filename, int location, int pixelFormat)
    {
        //Verification de l'existance possible de l'image
        //Iterateur
        for (ImageList::iterator i = imgList.begin(); i!=imgList.end(); ++i)
        {
            if (i->first == filename)
            {
                //ulDebug("[Notif] Image deja chargée (%s)", filename.c_str() );
                return i->second;
            }
        }

        //Chargement depuis le fichier
        UL_IMAGE* img;

        FILE* file;
        int size;
        //On ouvre le fichier
        file = fopen((char*)filename.c_str(), "rb");
        if(file != NULL)
        {
            //Cn charge le fichier en mémoire
            fseek(file, 0, SEEK_END);
            size = ftell(file);
            fseek(file, 0, SEEK_SET);
            fread(pngbuffer, 1, size, file);
            fclose (file);

            //On charge le png
            ulSetTransparentColor(RGB15(31, 0, 31));
            ulSetLoadUtilitySource(VF_MEMORY);
            img = ulLoadImageFilePNG((const char*)pngbuffer, size, location, pixelFormat);
            ulDisableTransparentColor();
            //free(buffer);
        }
        else
            img = NULL;

        imgList[filename] = img;
        img_num++;

        //Envoi de l'image
        return imgList[filename];
    }

    MD2Entity* CRessourceManager::LoadModel(string filename, int widthheight, float scale)
    {
        //Verification de l'existance possible de l'image
        //Iterateur
        for (ModelList::iterator i = modList.begin(); i!=modList.end(); ++i)
        {
            if (i->first == filename)
            {
                //ulDebug("[Notif] Modèle deja chargé (%s)", filename.c_str() );
                return i->second;
            }
        }

        //Chargement depuis le fichier
        MD2Entity* model;
        model = new MD2Entity;

        //On charge un modèle depuis le fichier
        bool texturereverse = false;
        FILE *fp = fopen((char*)filename.c_str(),"rb");
        if (fp == NULL)
        {
            ulDebug("wasnt found");
            return NULL;
        }

        int i;

        /* read header */
        fread(&model->header, 1, sizeof (md2_header_t), fp);

        if ((model->header.ident != 844121161) || (model->header.version != 8))
        {
            /* error! */
            iprintf("error: bad version!");
            while (1==1)
            {
                //Can't go on so lets hang arround
            }
        }

        /* memory allocation */
        model->texcoords = (md2_texCoord_t *)malloc (sizeof (md2_texCoord_t) * model->header.num_st);
        model->triangles = (md2_triangle_t *)malloc (sizeof (md2_triangle_t) * model->header.num_tris);
        model->frames = (md2_frame_t *)malloc (sizeof(md2_frame_t) * model->header.num_frames);

        //Read float textcoords
        fseek (fp, model->header.offset_st, SEEK_SET);
        fread (model->texcoords, sizeof (md2_texCoord_t), model->header.num_st, fp);

        //read that float triangles
        fseek (fp, model->header.offset_tris, SEEK_SET);
        fread (model->triangles, sizeof (md2_triangle_t), model->header.num_tris, fp);

        /* read frames */
        fseek (fp, model->header.offset_frames, SEEK_SET);

        for (i = 0; i < model->header.num_frames; ++i)
        {
            /* memory allocation for vertices of this frame */
            model->frames[i].verts = (md2_vertex_t *)
                                     malloc (sizeof (md2_vertex_t) * model->header.num_vertices);

            /* read frame data */
            fread (model->frames[i].scale, sizeof (vec3_t), 1, fp);
            fread (model->frames[i].translate, sizeof (vec3_t), 1, fp);
            fread (model->frames[i].name, sizeof (char), 16, fp);
            fread (model->frames[i].verts, sizeof (md2_vertex_t), model->header.num_vertices, fp);
        }

        fclose (fp);

        //<<<<<<<<Extatcly here we got a standard md2 model stored in a standard way
        //Now we convert them for a superfast nds renderway
        //ok erstma den shit mallocen
        model->uvs = (nds_texCoord_t *)malloc (sizeof (nds_texCoord_t) * model->header.num_st);
        model->tris = (nds_triangle_t *)malloc (sizeof (nds_triangle_t) * model->header.num_tris);
        model->frms = (nds_frame_t *)malloc (sizeof(nds_frame_t) * model->header.num_frames);
		
        for (i = 0; i < model->header.num_frames; ++i)
            model->frms[i].verts = (nds_vertex_t *) malloc (sizeof (nds_vertex_t) * model->header.num_vertices);

        //nun schön brav eins nach dem anderen von float und den dreck in fixed point math konvertieren
        int I,j,N;
        float s,t;
        vec3_t v;
        md2_frame_t *pframe;
        md2_vertex_t *pvert;//der kram is nötig dafür

        //also framedata in rahmendaten umwandeln
        for (N = 0; N < model->header.num_frames; ++N)
        {
            for (I = 0; I < model->header.num_tris; ++I)
            {
                for (j = 0; j < 3; ++j)
                {
                    pframe = &model->frames[N];
                    pvert = &pframe->verts[model->triangles[I].vertex[j]];
                    //dekomresse die aktuelle framedate
                    v[0] = (pframe->scale[0] * pvert->v[0]) + pframe->translate[0];
                    v[1] = (pframe->scale[1] * pvert->v[1]) + pframe->translate[1];
                    v[2] = (pframe->scale[2] * pvert->v[2]) + pframe->translate[2];
                    //Lager die unkompressed framevertexe
                    model->frms[N].verts[model->triangles[I].vertex[j]].v[0]=floattov10ver(float(v[0]/128)*scale);
                    model->frms[N].verts[model->triangles[I].vertex[j]].v[1]=floattov10ver(float(v[1]/128)*scale);
					model->frms[N].verts[model->triangles[I].vertex[j]].v[2]=floattov10ver(float(v[2]/128)*scale);

                    //model->frms[N].verts[model->triangles[I].vertex[j]].v[2]=floattov16(float(v[2]/128)*scale);
                    //Kopiere den framename
                    strcpy(model->frms[N].name,pframe->name);
                }
            }
        }

        //Normal indices
        for (N = 0; N < model->header.num_frames; ++N)
        {
            for (I = 0; I < model->header.num_vertices; ++I)
            {
                model->frms[N].verts[I].normalindex=model->frames[N].verts[I].normalIndex;
            }
        }

        //Nun zu den texturkooridinaten
        //texturereverse = true;
        for (I = 0; I < model->header.num_tris; ++I)
        {
            for (j = 0; j < 3; ++j)
            {
                if (texturereverse)
                {
                    s = (float)model->texcoords[model->triangles[I].st[j]].s / model->header.skinwidth;
                    t = (float)model->texcoords[model->triangles[I].st[j]].t / model->header.skinheight;
                    model->uvs[model->triangles[I].st[j]].s=floattot16(float(s)*widthheight);
                    model->uvs[model->triangles[I].st[j]].t=floattot16(float(1-t)*widthheight);
                }

                if (!texturereverse)
                {
                    s = (float)model->texcoords[model->triangles[I].st[j]].s / model->header.skinwidth;
                    t = (float)model->texcoords[model->triangles[I].st[j]].t / model->header.skinheight;
                    model->uvs[model->triangles[I].st[j]].s=floattot16(float(s)*widthheight);
                    model->uvs[model->triangles[I].st[j]].t=floattot16(float(t)*widthheight);
                }
                model->tris[I].st[j]=model->triangles[I].st[j];//Index for texture coords
                model->tris[I].vertex[j]=model->triangles[I].vertex[j];//Index for vertices
            }
        }


        for (N = 0; N < model->header.num_frames; ++N)
            free(model->frames[N].verts);
        free(model->frames);
        free(model->texcoords);
        free(model->triangles);

        /*ulDebug("vert:%d\n", model->header.num_vertices);
        ulDebug("tris:%d\n", model->header.num_tris);
        ulDebug("frames:%d\n", model->header.num_frames);
        ulDebug("framew:%d - skinw:%d", model->header.skinwidth, widthheight);*/

        //Déclaration d'un pointeur vers un vertex
        /*nds_vertex_t* vert = model->frms[0].verts;
		
		//Génération de la callist
		if(model->header.num_frames == 1)
		{
			//On génère la calllist pour ce modèle
			int commandCount = 3 + model->header.num_tris*3*4;
			ulDebug("commandCount:%d\n", commandCount);

			//On alloue de quoi stocker la calllist
			model->calllist = (u32*)malloc((commandCount+1)*sizeof(u32*) );

			//On remplit la callist
			model->calllist[0] = (u32)commandCount;
			model->calllist[1] = FIFO_COMMAND_PACK(FIFO_BEGIN, FIFO_NOP, FIFO_NOP, FIFO_NOP);
			model->calllist[2] = GL_TRIANGLES;
			int mcID = 3;

			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];
					
					model->calllist[mcID] = FIFO_COMMAND_PACK(FIFO_NORMAL, FIFO_TEX_COORD, FIFO_VERTEX10, FIFO_NOP);
        			mcID++;
					model->calllist[mcID] = NORMAL_PACK(LowLevel::Model::anormtable16[vert[vertnum].normalindex][0], LowLevel::Model::anormtable16[vert[vertnum].normalindex][1], LowLevel::Model::anormtable16[vert[vertnum].normalindex][2]);
        			mcID++;
					model->calllist[mcID] = TEXTURE_PACK(model->uvs[stnum].s, model->uvs[stnum].t);
					mcID++;
					model->calllist[mcID] = DSPack10(vert[vertnum].v[0], vert[vertnum].v[1], vert[vertnum].v[2]);
					mcID++;
        		}
			}
			model->calllist[mcID] = FIFO_COMMAND_PACK(FIFO_END, FIFO_NOP, FIFO_NOP, FIFO_NOP);
			model->calllistGenerated = 1;

			//On libère l'espace...
		}
		else
			model->calllistGenerated = 0;*/

		model->calllistGenerated = 0;

        modList[filename] = model;
        mod_num++;
		
        //Envoi du modèle
        return modList[filename];
    }

	MD2Entity* CRessourceManager::LoadModelBin(string filename, int widthheight, float scale)
	{
		//Verification de l'existance possible de l'image
        //Iterateur
        for (ModelList::iterator i = modList.begin(); i!=modList.end(); ++i)
        {
            if (i->first == filename)
            {
                //ulDebug("[Notif] Modèle deja chargé (%s)", filename.c_str() );
                return i->second;
            }
        }

        //Chargement depuis le fichier
        MD2Entity* model;
        model = new MD2Entity;

		//Lecture de la calllist
		FILE* file = fopen(filename.c_str(), "rb");
		
		//On lit la taille des commandes
		fread(&model->calllistSize, sizeof(u32), 1, file);
		//ulDebug("size:%d\n", model->calllistSize);
		rewind(file);

		//On alloue de la place pour la calllist
		model->calllist = (u32*)malloc((model->calllistSize+1)*sizeof(u32) );
		model->calllistGenerated = 1;

		fread(model->calllist, sizeof(u32), (model->calllistSize+1), file);

		fclose(file);

		modList[filename] = model;
        mod_num++;

		return modList[filename];
	}

    void CRessourceManager::DeleteImage(UL_IMAGE* img)
    {
        //Iterateur
        for (ImageList::iterator i = imgList.begin(); i!=imgList.end(); ++i)
        {
            if (i->second == img)
            {
                //ulDebug("Image supprimée (%s)\n", i->first.c_str());
				//On supprime l'image
				ulDeleteImage(img);
				img_num--;
                imgList.erase(i->first);
				delete img;
                break;
            }
        }

    }

    void CRessourceManager::DeleteModel(MD2Entity* model)
    {
        //Iterateur
        for (ModelList::iterator i = modList.begin(); i!=modList.end(); ++i)
        {
            if (i->second == model)
            {
                //ulDebug("Image supprimée (%s)\n", i->first.c_str());
				//On supprime le modèle
				if(model->calllistGenerated == 1)
				{
					free(model->calllist);
				}
				else
				{
					int N;
					for (N = 0; N < model->header.num_frames; ++N)
						free(model->frms[N].verts);

					free(model->frms);
					free(model->uvs);
					free(model->tris);
				}

				mod_num--;
                modList.erase(i->first);
				delete model;
                break;
            }
        }
    }

    void CRessourceManager::Free()
    {
        //On supprime les images
        img_num = 0;
        mod_num = 0;
		
        //Iterateur
        for (ImageList::iterator i = imgList.begin(); i != imgList.end();)
        {
            //ulDebug("Image supprimée (%s)\n", i->first.c_str());
            ulDeleteImage(i->second);
            imgList.erase(i++);
        }
        imgList.clear();
        //Iterateur
        for (ModelList::iterator i = modList.begin(); i != modList.end();)
        {
            //ulDebug("Image supprimée (%s)\n", i->first.c_str());
            int N;
            for (N = 0; N < (i->second)->header.num_frames; ++N)
                free((i->second)->frms[N].verts);

            free((i->second)->frms);
            free((i->second)->uvs);
            free((i->second)->tris);

            modList.erase(i++);
        }
        modList.clear();
    }

    unsigned int CRessourceManager::GetImageCount()
    {
        return img_num;
    }

    unsigned int CRessourceManager::GetModelCount()
    {
        return mod_num;
    }
};

