#define STRICT

// Includes standard Windows
#include <windows.h>
#include <windowsx.h>
#include <time.h>
#include <stdlib.h>
#include <malloc.h>
#include <memory.h>
#include <stdio.h>

// Includes D3D
#define  D3D_OVERLOADS
#include <ddraw.h>
#include <d3d.h>

// Includes utilitaires D3D
#include "d3dmath.h"
#include "d3dutil.h"
#include "D3DEnum.h"

#include <d3dx.h>

// Constantes
#include "const.h"

// Types
#include "types.h"

// Variables globales projet
#include "vars.h"

// Prototypes fonctions autres modules
#include "proto.h"

// Macros
#include "macros.h"

// Charger une image ARGB en mémoire à partir d'un fichier avec les dadatypes
UBYTE *LoadTextureFromFile(APTR hPtr, char *sFileName, UBYTE Opacity, int *iWidth, int *iHeight)
{
    BitMapHeader *bh;
    UBYTE *hImg = NULL;
    HGDIOBJ hObject = NULL;
    pdtBlitPixelArray pBPA;
    DOUBLE fDim, fDum;
    int iCnt;
 
    // Charger la texture avec picture.datatype et la sous classe associée au type de fichier image
    if (!(hObject = NewDTObject(sFileName,
		DTA_SourceType,		DTST_FILE,
        DTA_GroupID,        GID_PICTURE,
        PDTA_DestMode,      PMODE_V43,
    	TAG_DONE)))
    {
        vTrace(GetCatalogStr(hCat, MSG_0467, "*** E0082 : format du fichier texture [%s] inconnu"), sFileName);
        goto __Exit;
    }

    // Récupérer le bitmap header pour avoir les dimensions
    GetDTAttrs(hObject, PDTA_BitMapHeader, &bh, TAG_DONE);
    if (!bh)
    {
        vTrace(GetCatalogStr(hCat, MSG_0468, "*** E0083 : structure texture [%s] incompatible"), sFileName);
        goto __Exit;
    }

    *iWidth = bh -> bmh_Width; *iHeight = bh -> bmh_Height;

#if 1 // N'autoriser que des textures carrées coté puissance de 2
    // Vérifier que la texture est carrée
    if (*iWidth != *iHeight)
    {
        vTrace(GetCatalogStr(hCat, MSG_0469, "*** E0110 : texture %s non carrée (%d x %d)"), sFileName, *iWidth, *iHeight);
        goto __Exit;
    }

    // Vérifier que ses dimensions sont un multiple de 2
    fDim = log(*iWidth) / log(2);
    if (modf(fDim, &fDum) > 1.e-5)
    {
        vTrace(GetCatalogStr(hCat, MSG_0470, "*** E0111 : dimensions texture %s non puissance de 2 (%d x %d)"), sFileName, *iWidth, *iHeight);
        goto __Exit;
    }
#endif

    // Allouer le buffer pour y copier le bitmap
    if (!(hImg = (UBYTE *) AllocVec(4 * *iWidth * *iHeight, MEMF_PUBLIC)))
    {
        vTrace(GetCatalogStr(hCat, MSG_0471, "*** E0084 : allocation buffer texture %s (%d x %d)"), sFileName, *iWidth, *iHeight);
        goto __Exit;
    }

	// Appeller la méthode picture.datatype::PDTM_READPIXELARRAY pour copier le bitmap de l'objet datatype dans notre buffer
    pBPA.MethodID = PDTM_READPIXELARRAY;
	pBPA.pbpa_PixelData = (APTR) hImg;
	pBPA.pbpa_PixelFormat = PBPAFMT_ARGB;
	pBPA.pbpa_PixelArrayMod = *iWidth * 4;
	pBPA.pbpa_Left = 0;
	pBPA.pbpa_Top = 0;
	pBPA.pbpa_Width = *iWidth;
	pBPA.pbpa_Height = *iHeight;                  

#ifndef __PPC__
    DoMethodA(hObject, Msg(&pBPA));
#else
    // K68_DoMethodA(hObject, Msg(&pBPA));
#endif

    // Régler l'alpha
    for (iCnt = 0 ; iCnt < bh -> bmh_Width * bh -> bmh_Height ; iCnt++)
        hImg[iCnt * 4] = Opacity;

__Exit:
	// Libérer l'objet datatype (puisqu'on a copié le bitmap dans un buffer)
    if (hObject) DisposeDTObject(hObject);

    return hImg;
}

void vXRefMaterials2Textures(void)
{
    vTrace(GetCatalogStr(hCat, MSG_0472, "### Référencement croisé textures / matériaux ..."));

    // Référencer tous les matériaux texturés
    for (int iMtrl = 0 ; iMtrl <= iMtrlLastUsed ; iMtrl++)
    {
        if  (!Materials[iMtrl].bEnabled) continue;

        Materials[iMtrl].iTexture = 0;

        if (Materials[iMtrl].bTextured)
        {
            Materials[iMtrl].bTextured = FALSE;
            for (int iText = 0 ; iText < XDC_NUMTEX ; iText++)
                if (Textures[iText].hTexMap && Textures[iText].hTexture && !stricmp(Materials[iMtrl].sTexName, Textures[iText].sName))
                {
                     vTrace(GetCatalogStr(hCat, MSG_0473, "\tMatch matériau n° %d (%s) avec texture n° %d (%s)"), iMtrl, Materials[iMtrl].sName, iText, Textures[iText].sName);
                     Materials[iMtrl].iTexture = iText;
                     Materials[iMtrl].bTextured = TRUE;
                     iText = XDC_NUMTEX;
                }

            if (!Materials[iMtrl].bTextured)
                vTrace(GetCatalogStr(hCat, MSG_0474, "*** E0112 : texture [%s] introuvable"), Materials[iMtrl].sTexName);
        }
    }
    vTrace(GetCatalogStr(hCat, MSG_0475, "### Référencement terminé"));
}

BOOL GenTextures(W3D_Context* hW3DC)
{
	int iText;
	ULONG lError;

    vTrace(GetCatalogStr(hCat, MSG_0476, "### Installation des textures dans le contexte 3D ..."));

    // Uploader toutes les textures disponibles dans le contexte W3D
    for (iText = 0 ; iText <= iTextLastUsed ; iText++)
        if (Textures[iText].hTexMap)
        {
            TagItem sTagList[] =
            {
            	{ W3D_ATO_IMAGE,      (ULONG) Textures[iText].hTexMap },
            	{ W3D_ATO_FORMAT,     W3D_A8R8G8B8 },
                { W3D_ATO_WIDTH,      Textures[iText].iWidth },
            	{ W3D_ATO_HEIGHT,     Textures[iText].iHeight },
            	{ TAG_DONE,             TAG_DONE }
            };

        	Textures[iText].hTexture = W3D_AllocTexObj(hW3DC, &lError, sTagList);
 
        	if (!Textures[iText].hTexture || lError != W3D_SUCCESS)
            {
        		switch(lError)
                {
            		case (ULONG) W3D_ILLEGALINPUT:          vTrace(GetCatalogStr(hCat, MSG_0477, "*** E0085 : format incompatible"));              break;
            		case (ULONG) W3D_NOMEMORY:  			vTrace(GetCatalogStr(hCat, MSG_0478, "*** E0086 : plus de mémoire"));  	            break;
            		case (ULONG) W3D_UNSUPPORTEDTEXSIZE:    vTrace(GetCatalogStr(hCat, MSG_0479, "*** E0087 : taille de texture incompatible"));   break;
            		case (ULONG) W3D_NOPALETTE: 			vTrace(GetCatalogStr(hCat, MSG_0480, "*** E0088 : texture chunky sans palette"));      break;
            		case (ULONG) W3D_UNSUPPORTEDTEXFMT:		vTrace(GetCatalogStr(hCat, MSG_0481, "*** E0089 : format inconnu"));     			    break;
            		default:            			vTrace(GetCatalogStr(hCat, MSG_0482, "*** E0090 : erreur interne"));
                }
                FreeVec(Textures[iText].hTexMap);
                memcpy(&Textures[iText], &Textures[iText + 1], (iTextLastUsed-- - iText) * sizeof(gTex));
        		continue;
        	}

        	vTrace(GetCatalogStr(hCat, MSG_0483, "\tTexture W3D [%s] installée, taille %ld × %ld"), Textures[iText].sName, Textures[iText].iWidth, Textures[iText].iHeight);

        	// Set texture wrapping mode to on.
        	W3D_SetWrapMode(hW3DC, Textures[iText].hTexture, W3D_REPEAT, W3D_REPEAT, NULL);

        	// Set blending (W3D_REPLACE, W3D_DECAL, W3D_MODULATE, W3D_BLEND) (devrait se faire par material)
        	W3D_SetTexEnv(hW3DC, Textures[iText].hTexture, W3D_REPLACE, NULL);

            // Set bilinear filter
            W3D_SetFilter(hW3DC, Textures[iText].hTexture, W3D_LINEAR, W3D_LINEAR);
        }

    vTrace(GetCatalogStr(hCat, MSG_0484, "### Installation des textures terminée"));

    // Référencer les matériaux
    vXRefMaterials2Textures();

	return TRUE;
}

void vCloseTextures(W3D_Context *hW3DC)
{
    vTrace(GetCatalogStr(hCat, MSG_0485, "### Suppression des textures du contexte 3D ..."));

    W3D_FlushTextures(hW3DC);

    for (int iText = 0 ; iText < XDC_NUMTEX ; iText++)
        if (Textures[iText].hTexMap)
            if (Textures[iText].hTexture)
            {
                W3D_FreeTexObj(hW3DC, Textures[iText].hTexture);
                Textures[iText].hTexture = NULL;
            }

    vTrace(GetCatalogStr(hCat, MSG_0486, "### Suppression des textures du contexte 3D terminée"));
}

BOOL bLoadTextures(void)
{
    int iText;
    BOOL bOk                = FALSE;
    BPTR hTexDir            = Lock("Textures", ACCESS_READ);
    ExAllData *hDirData     = (ExAllData *) AllocVec(4096, MEMF_PUBLIC);
    ExAllControl *hExamine  = (ExAllControl *) AllocDosObject(DOS_EXALLCONTROL,NULL);

    vTrace(GetCatalogStr(hCat, MSG_0488, "### Chargement des textures du catalogue..."));

	// RAZ textures
	for (iText = 0 ; iText < XDC_NUMTEX ; iText++)
    	Textures[iText].hTexMap = NULL;

    if (!hTexDir)
    {
        vTrace(GetCatalogStr(hCat, MSG_0489, "*** E0091 : lecture dir textures"));
        goto __bltFail;
    }

    if (!hDirData)
    {
        vTrace(GetCatalogStr(hCat, MSG_0490, "*** E0092 : allocation buffer"));
        goto __bltFail;
    }

    if (!hExamine)
    {
        vTrace(GetCatalogStr(hCat, MSG_0491, "*** E0093 : allocation contrôle examine"));
        goto __bltFail;
    }

    hExamine -> eac_LastKey = 0;
    iText = 0;
    do
    {
        ExAllData *hFileData;
        bOk = ExAll(hTexDir, hDirData, 4096, ED_NAME, hExamine);
        if ((!bOk) && (IoErr() != ERROR_NO_MORE_ENTRIES))
        {
            // ExAll failed abnormally
            break;
        }
        if (hExamine -> eac_Entries == 0)
        {
            // ExAll failed normally with no entries
            continue;                   // ("bOk" is *usually* zero)
        }
        hFileData = (ExAllData *) hDirData;
        do
        {
            // use hFileData here
            strcpy(Textures[iText].sName, "Textures/");
            strcat(Textures[iText].sName, hFileData -> ed_Name);

            if (Textures[iText].hTexMap = LoadTextureFromFile( NULL,
                                Textures[iText].sName,
                                0x7F,
                                &Textures[iText].iWidth,
                                &Textures[iText].iHeight))

            {          
                vTrace(GetCatalogStr(hCat, MSG_0493, "\tTexture n° %d : [%s] (%d x %d) chargée"), iText, hFileData -> ed_Name, Textures[iText].iWidth, Textures[iText].iHeight);
                strcpy(Textures[iText].sName, hFileData -> ed_Name);
                iText++;
            }

            // get next hFileData
            hFileData = hFileData -> ed_Next;
        }
        while (hFileData);

    } while (bOk);
  
    bOk = TRUE;
    iTextLastUsed = iText - 1;

__bltFail:
    if (hExamine)   FreeDosObject(DOS_EXALLCONTROL, hExamine);
    if (hDirData)   FreeVec(hDirData);
    if (hTexDir)    UnLock(hTexDir);

    vTrace(GetCatalogStr(hCat, MSG_0494, "### Chargement des textures terminé"));
    return bOk;
}

void vUnLoadTextures(void)
{
	// RAZ textures
	for (int iText = 0 ; iText < XDC_NUMTEX ; iText++)
    	if (Textures[iText].hTexMap) FreeVec(Textures[iText].hTexMap);
}



