#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>
#include <math.h>
#include <io.h>

// Includes D3D
#define  D3D_OVERLOADS
#include <ddraw.h>
#include <d3d.h>
#include <d3dx.h>

// Includes utilitaires D3D
#include "d3dmath.h"
#include "d3dutil.h"
#include "D3DEnum.h"

// Ids Resources
//#include "resource.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"

void vSaveBin(void)
{
    char cBuffer[13];
    static TCHAR sFileName[512];
    FILE* hFile;

#ifndef _AMIGA_
    static TCHAR sInitialDir[512] = "";
    TCHAR sCurrentName[512] = "*.sk3d";

	// Récupérer le directory courant
	GetCurrentDirectory(sizeof(sInitialDir), sInitialDir);

    // Préparer une structure OPENFILENAME
	OPENFILENAME sOFName = { sizeof(OPENFILENAME), NULL, NULL,
                         GetCatalogStr(hCat, MSG_0634, "Fichiers Scènes sKulpt (*.sk3d)\0*.sk3d\0\0"),
                         NULL, 0, 1, sCurrentName, 512, sFileName, 512,
                         sInitialDir, GetCatalogStr(hCat, MSG_0635, "Ouvrir un fichier scène sKulpt"), 0, 0, 1,
                         ".sk3d", 0, NULL, NULL };

    // Choisir un fichier
    if(!GetOpenFileName(&sOFName))
        return;

    // Stocker le nom du directory pour le prochain appel
    strcpy(sInitialDir, sCurrentName);
    strstr(sInitialDir, sFileName)[0] = '\0';
#else
    strcpy(sFileName, "#?.sk3d");
    if (!FSelect(GetCatalogStr(hCat, MSG_0638, "Ouvrir..."), sFileName))
        return;
#endif

    hFile = fopen(sFileName,
#ifndef _AMIGA_
                     "wb");
#else
                     "w");
#endif

    if (!hFile)
    {
        vTrace(GetCatalogStr(hCat, MSG_0641, "*** E0096 : ouverture fichier %s"), sFileName);
        return;
    }

    vTrace(GetCatalogStr(hCat, MSG_0642, "*** Fichier scène ouvert : %s"), sFileName);
        
    // Garbage collecter
    vCollect();

    // Ecrire la structure d'entête et les données sKulpt 3D mode binaire
    strcpy(cBuffer,
#ifndef _AMIGA_
                    "FORMINTLSK3D"
#else
                    "FORMM68KSK3D"
#endif
           );

    fwrite(cBuffer, 1, 12, hFile);

    vTrace(GetCatalogStr(hCat, MSG_0645, "Ecriture de %d vertices"), iVertFirstAvailable);
    fwrite("VERT", 1, 4, hFile);
    fwrite(&iVertFirstAvailable, 1, sizeof(iVertFirstAvailable), hFile);
    fwrite(Vertices, 1, iVertFirstAvailable * sizeof(gSommet), hFile);

    vTrace(GetCatalogStr(hCat, MSG_0647, "Ecriture de %d lampes"), iLampFirstAvailable);
    fwrite("LAMP", 1, 4, hFile);
    fwrite(&iLampFirstAvailable, 1, sizeof(iLampFirstAvailable), hFile);
    fwrite(Lampes, 1, iLampFirstAvailable * sizeof(gLamp), hFile);

    vTrace(GetCatalogStr(hCat, MSG_0649, "Ecriture de %d triangles"), iTriaFirstAvailable);
    fwrite("FACE", 1, 4, hFile);
    fwrite(&iTriaFirstAvailable, 1, sizeof(iTriaFirstAvailable), hFile);
    fwrite(Triangles, 1, iTriaFirstAvailable * sizeof(gTri), hFile);

    vTrace(GetCatalogStr(hCat, MSG_0651, "Ecriture de %d arêtes"), iEdgeFirstAvailable);
    fwrite("EDGE", 1, 4, hFile);
    fwrite(&iEdgeFirstAvailable, 1, sizeof(iEdgeFirstAvailable), hFile);
    fwrite(Edges, 1, iEdgeFirstAvailable * sizeof(gEdge), hFile);

    vTrace(GetCatalogStr(hCat, MSG_0653, "Ecriture de %d matériaux"), iMtrlFirstAvailable);
    fwrite("MTRL", 1, 4, hFile);
    fwrite(&iMtrlFirstAvailable, 1, sizeof(iMtrlFirstAvailable), hFile);
    fwrite(Materials, 1, iMtrlFirstAvailable * sizeof(gMtrl), hFile);

    vTrace(GetCatalogStr(hCat, MSG_0655, "Ecriture de l'environnement"));
    fwrite("OBSV", 1, 4, hFile);
    fwrite(&Observer, 1, sizeof(Observer), hFile);
    fwrite(&Target, 1, sizeof(Target), hFile);
    fwrite(&cAmbient, 1, sizeof(cAmbient), hFile);
    fwrite(&cBack, 1, sizeof(cBack), hFile);

    // Fermer le fichier
    fclose(hFile);
    vTrace(GetCatalogStr(hCat, MSG_0657, "Fichier scène créé"));
}

void vLoadBin(void)
{
    int iNumVertex = 0, iNumFace = 0, iNumLamps = 0, iNumEdges = 0, iNumMtrl = 0, iX;
    char cBuffer[13];
    DWORD dwNBytesRead;
    static TCHAR sFileName[512];

#ifndef _AMIGA_
    HANDLE hFile;
    static TCHAR sInitialDir[512] = "";
    TCHAR sCurrentName[512] = "*.sk3d";
    OFSTRUCT ReOpenBuff;
    // Récupérer le directory courant
    GetCurrentDirectory(sizeof(sInitialDir), sInitialDir);

    // Préparer une structure OPENFILENAME
    OPENFILENAME sOFName = { sizeof(OPENFILENAME), NULL, NULL,
                         GetCatalogStr(hCat, MSG_0660, "Fichiers Scènes sk3d (*.sk3d)\0*.sk3d\0\0"),
                         NULL, 0, 1, sCurrentName, 512, sFileName, 512,
                         sInitialDir, GetCatalogStr(hCat, MSG_0661, "Ouvrir un fichier scène sk3d"), OFN_FILEMUSTEXIST, 0, 1,
                         ".sk3d", 0, NULL, NULL };

    // Choisir un fichier
    if(!GetOpenFileName(&sOFName))
        return;

    // Stocker le nom du directory pour le prochain appel
    strcpy(sInitialDir, sCurrentName);
    strstr(sInitialDir, sFileName)[0] = '\0';

    // Ouvrir le fichier en lecture
    if ((HANDLE) HFILE_ERROR == (hFile = (HANDLE) OpenFile(sFileName, &ReOpenBuff, OF_READ)))
        return;
#else
    FILE* hFile;

    strcpy(sFileName, "#?.sk3d");

    if (!FSelect(GetCatalogStr(hCat, MSG_0664, "Ouvrir..."), sFileName))
        return;

    hFile = fopen(sFileName, "r");
    if (!hFile)
    {
        vTrace(GetCatalogStr(hCat, MSG_0666, "*** E0097 : ouverture fichier %s"), sFileName);
        return;
    }
#endif // _AMIGA_

    vTrace(GetCatalogStr(hCat, MSG_0667, "*** Fichier scène ouvert :"));
    vTrace(sFileName);
        
    // Effacer tous les objets présents en mémoire
    vDeleteObjects();

    // Vérifier la structure d'entête SKulpt 3D mode binaire
#ifndef _AMIGA_
    ReadFile(hFile, cBuffer, 12, &dwNBytesRead, NULL);
#else
    dwNBytesRead = fread(cBuffer, 1, 12, hFile);
#endif

    if (strncmp((char *) cBuffer,
#ifndef _AMIGA_
         "FORMINTLSK3D"
#else
         "FORMM68KSK3D"
#endif
         , 12))
    {
        vTrace(GetCatalogStr(hCat, MSG_0670, "*** E0024 : En-tête fichier scène incorrect"));
        goto _Fin;
    }

    // Parcourir le fichier en décodant les chunks au fur et à mesure
    cBuffer[4] = 0;
#ifndef _AMIGA_
    while ((ReadFile(hFile, cBuffer, 4, &dwNBytesRead, NULL)) && (dwNBytesRead == 4))
#else
    while (4 == (dwNBytesRead = fread(cBuffer, 1, 4, hFile)))
#endif
    {
        vTrace("Chunk : %4s", cBuffer);

        if (!strncmp((char *) cBuffer, "VERT", 4))  // Sommets
        {
            // Charger le nombre de vertex
#ifndef _AMIGA_
            ReadFile(hFile, &iNumVertex, 4, &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(&iNumVertex, 1, 4, hFile);
#endif
            vTrace(GetCatalogStr(hCat, MSG_0673, "Nombre de sommets : %ld"), iNumVertex);

            // Lire directement le chunk vertices
#ifndef _AMIGA_
            ReadFile(hFile, Vertices, iNumVertex * sizeof(gSommet), &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(Vertices, 1, iNumVertex * sizeof(gSommet), hFile);
#endif
            continue;
        }

        if (!strncmp((char *) cBuffer, "LAMP", 4))  // Lampes
        {
            // Charger le nombre de lampes
#ifndef _AMIGA_
            ReadFile(hFile, &iNumLamps, 4, &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(&iNumLamps, 1, 4, hFile);
#endif
            vTrace(GetCatalogStr(hCat, MSG_0675, "Nombre de lampes : %ld"), iNumLamps);

            // Lire les lampes
#ifndef _AMIGA_
            ReadFile(hFile, Lampes, iNumLamps * sizeof(gLamp), &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(Lampes, 1, iNumLamps * sizeof(gLamp), hFile);
#endif
            continue;
        }

        if (!strncmp((char *) cBuffer, "FACE", 4)) // Triangles
        {
            // Charger le nombre de faces
#ifndef _AMIGA_
            ReadFile(hFile, &iNumFace, 4, &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(&iNumFace, 1, 4, hFile);
#endif
            vTrace(GetCatalogStr(hCat, MSG_0677, "Nombre de faces : %ld"), iNumFace);

            // Lire les faces
#ifndef _AMIGA_
            ReadFile(hFile, Triangles, iNumFace * sizeof(gTri), &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(Triangles, 1, iNumFace * sizeof(gTri), hFile);
#endif
            continue;
        }

        if (!strncmp((char *) cBuffer, "EDGE", 4)) // Arêtes
        {
            // Charger le nombre d'arêtes
#ifndef _AMIGA_
            ReadFile(hFile, &iNumEdges, 4, &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(&iNumEdges, 1, 4, hFile);
#endif
            vTrace(GetCatalogStr(hCat, MSG_0679, "Nombre d'arêtes : %ld"), iNumEdges);

            // Lire les arêtes
#ifndef _AMIGA_
            ReadFile(hFile, Edges, iNumEdges * sizeof(gEdge), &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(Edges, 1, iNumEdges * sizeof(gEdge), hFile);
#endif
            continue;
        }

        if (!strncmp((char *) cBuffer, "MTRL", 4)) // Matériaux
        {
            // Charger le nombre de matériaux
#ifndef _AMIGA_
            ReadFile(hFile, &iNumMtrl, 4, &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(&iNumMtrl, 1, 4, hFile);
#endif
            vTrace(GetCatalogStr(hCat, MSG_0681, "Nombre de matériaux : %ld"), iNumMtrl);

            // Lire les arêtes
#ifndef _AMIGA_
            ReadFile(hFile, Materials, iNumMtrl * sizeof(gMtrl), &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(Materials, 1, iNumMtrl * sizeof(gMtrl), hFile);
#endif
            continue;
        }

        if (!strncmp((char *) cBuffer, "OBSV", 4)) // Environnement
        {
            // Charger la structure observer
#ifndef _AMIGA_
            ReadFile(hFile, &Observer, sizeof(Observer), &dwNBytesRead, NULL);
            ReadFile(hFile, &Target, sizeof(Observer), &dwNBytesRead, NULL);
            ReadFile(hFile, &cAmbient, sizeof(cAmbient), &dwNBytesRead, NULL);
            ReadFile(hFile, &cBack, sizeof(cBack), &dwNBytesRead, NULL);
#else
            dwNBytesRead = fread(&Observer, 1, sizeof(Observer), hFile);
            dwNBytesRead = fread(&Target, 1, sizeof(Target), hFile);
            dwNBytesRead = fread(&cAmbient, 1, sizeof(cAmbient), hFile);
            dwNBytesRead = fread(&cBack, 1, sizeof(cBack), hFile);
#endif
            vNullVect = D3DVECTOR(0, 0, 0);
            D3DUtil_SetViewMatrix(matView,
              Observer, // From
              Target,   // To
              vNullVect);
            continue;
        }

        // Chunk non traité.
#ifndef _AMIGA_
        ReadFile(hFile, &iX, 4, &dwNBytesRead, NULL);
#else
        dwNBytesRead = fread(&iX, 1, 4, hFile);
#endif

        vTrace(GetCatalogStr(hCat, MSG_0683, "Skip %d octets"), iX);
#ifndef _AMIGA_
        SetFilePointer(hFile, iX, NULL, FILE_CURRENT);
#else
        fseek(hFile, iX, SEEK_CUR);
#endif
    }

_Fin:

    // Fermer le fichier
#ifndef _AMIGA_
    CloseHandle(hFile);
#else
    fclose(hFile);
#endif

    iVertLastUsed = (iVertFirstAvailable = iNumVertex) - 1;
    iEdgeLastUsed = (iEdgeFirstAvailable = iNumEdges) - 1;
    iLampLastUsed = (iLampFirstAvailable = iNumLamps) - 1;
    iTriaLastUsed = (iTriaFirstAvailable = iNumFace) - 1;
    iMtrlLastUsed = (iMtrlFirstAvailable = iNumMtrl) - 1;

    // Référencer les matériaux avec leur texture
    vXRefMaterials2Textures();

#ifndef _AMIGA_
    // Forcer le recalcul des sommets de chaque triangle (car on a changé les points)
    for (int iTriangle = 0 ; iTriangle <= iTriaLastUsed ; iTriangle++)
        bUpdateD3DTri(iTriangle);
#endif
}
