#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    <stdarg.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"

// Spécifiques module
#include "libraries/gadtools.h"
#include <clib/diskfont_protos.h>

#ifdef __PPC__
#include <powerpc/powerpc.h>
#include <clib/powerpc_protos.h>
#endif

#ifdef __PPC__
char sVersion[] = "$VER: sKulpt W3D beta 3 12-Jan-2001 (STC4/GCC/W3D/PPC) - 2000/2001 stephane.guillard@steria.fr";
#else
char sVersion[] = "$VER: sKulpt W3D beta 3 12-Jan-2001 (STC4/GCC/W3D/060) - 2000/2001 stephane.guillard@steria.fr";
#endif

char s3DTitle[80];

NewWindow NWS2D =
{
    0, 0,
    30, 30,
    0, 1,
    IDCMP_NEWSIZE,  // Seul le newsize est activé à la création, mais ensuite l'IDCMP est modifié pour prise en compte de tous les autres messages
    WFLG_DEPTHGADGET | WFLG_SIZEBBOTTOM | WFLG_DRAGBAR | WFLG_SIZEGADGET | WFLG_SIMPLE_REFRESH,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    20, 20,
    (UWORD) ~0, (UWORD) ~0,
    CUSTOMSCREEN
};

NewWindow NWS3D =
{
    0, 0,
    30, 30,
    0, 1,
    NULL, // Le filtre IDCMP de la fenêtre 3D est défini après l'avoir redimensionnée pour éviter de redim backbuf+zbuf au démarrage
    WFLG_DEPTHGADGET | WFLG_SIZEBBOTTOM | WFLG_DRAGBAR | WFLG_SIZEGADGET | WFLG_SIMPLE_REFRESH,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    20, 20,
    (UWORD) ~0, (UWORD) ~0,
    CUSTOMSCREEN
};

NewWindow NWSMenu =
{
    0, 0,
    30, 30,
    0, 1,
    IDCMP_VANILLAKEY | IDCMP_CLOSEWINDOW | IDCMP_MENUPICK,
    WFLG_ACTIVATE | WFLG_DEPTHGADGET | WFLG_SIZEBBOTTOM | WFLG_DRAGBAR | WFLG_SIZEGADGET | WFLG_CLOSEGADGET | WFLG_SIMPLE_REFRESH | WFLG_NOCAREREFRESH,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    20, 20,
    (UWORD) ~0, (UWORD) ~0,
    CUSTOMSCREEN
};

HMENU hMenu = NULL;

void vWarpInfo(void)
{
    ULONG lRes;
    char cBuf[255];

    W3D_Driver **hDriver = W3D_GetDrivers();

    while (*hDriver)
    {
	sprintf(cBuf, "Driver [%s] : fmt 0x%lX ", hDriver[0] -> name, hDriver[0] -> formats);
        strcat(s3DTitle, hDriver[0] -> name); strcat(s3DTitle, "]");

	if (hDriver[0] -> swdriver) sprintf(cBuf + strlen(cBuf), "(HEL) ");
	else                        sprintf(cBuf + strlen(cBuf), "(HAL) ");

	sprintf(cBuf + strlen(cBuf), "- primitives : ");
	lRes = W3D_QueryDriver(hDriver[0], W3D_Q_DRAW_POINT, W3D_FMT_R5G5B5);
	if (lRes != W3D_NOT_SUPPORTED) sprintf(cBuf + strlen(cBuf), "point ");

	lRes = W3D_QueryDriver(hDriver[0], W3D_Q_DRAW_LINE, W3D_FMT_R5G5B5);
	if (lRes != W3D_NOT_SUPPORTED) sprintf(cBuf + strlen(cBuf), "line ");

	lRes = W3D_QueryDriver(hDriver[0], W3D_Q_DRAW_TRIANGLE, W3D_FMT_R5G5B5);
	if (lRes != W3D_NOT_SUPPORTED) sprintf(cBuf + strlen(cBuf), "triangle ");

	sprintf(cBuf + strlen(cBuf), "- filt. : ");
	lRes = W3D_QueryDriver(hDriver[0], W3D_Q_BILINEARFILTER, W3D_FMT_R5G5B5);
	if (lRes != W3D_NOT_SUPPORTED) sprintf(cBuf + strlen(cBuf), "bilinear ");

	lRes = W3D_QueryDriver(hDriver[0], W3D_Q_MMFILTER, W3D_FMT_R5G5B5);
	if (lRes != W3D_NOT_SUPPORTED) sprintf(cBuf + strlen(cBuf), "mipmap");

        vTrace(cBuf);
	hDriver++;
    }
}

// Simulation PostMessage Windows : dispatcher le message sur la WindowProc en fonction de hWnd
inline int PostMessage(HWND hWnd, int iMsg, WPARAM wParam, LPARAM lParam)
{
    if (hWnd == hWndTop || hWnd == hWndFace || hWnd == hWndRight)
    {
    	lrPlanarWndProc(hWnd, iMsg, wParam, lParam);
        return 0;
    }

    if (hWnd == hWndPersp)
    {
    	lrPerspWndProc(hWnd, iMsg, wParam, lParam);
        return 0;
    }

    if (hWnd == hWndMenu)
	lrMenuWndProc(hWnd, iMsg, wParam, lParam);

    return 0;
}

inline int iDispatchIntuiMessage(HWND wGotMsg, IntuiMessage *iMsg)
{
    register int
        iClass      = iMsg -> Class,
        iCode       = iMsg -> Code;
    register WORD
        wQualifier  = iMsg -> Qualifier,
        wMouseX     = iMsg -> MouseX,
        wMouseY     = iMsg -> MouseY;

    ReplyMsg((struct Message *) iMsg);

    switch(iClass)
    {
        case IDCMP_ACTIVEWINDOW :
            if (wGotMsg == hWndTop) lWActive = XDC_WID_TOP;
            if (wGotMsg == hWndFace) lWActive = XDC_WID_FACE;
            if (wGotMsg == hWndRight) lWActive = XDC_WID_SIDE;
            break;

        case IDCMP_VANILLAKEY :
	    PostMessage(wGotMsg, WM_CHAR, iCode, 0);
            break;

        case IDCMP_NEWSIZE :
            PostMessage(wGotMsg, WM_SIZE, 0, (ULONG) wGotMsg -> Width | ((ULONG) wGotMsg -> Height) << 16);
            break;

        case IDCMP_MOUSEMOVE:
            if (!PeekSignal(l2DWinSigMask))
                PostMessage(wGotMsg, WM_MOUSEMOVE, wQualifier, (ULONG) wMouseX & 0x0000FFFF | ((ULONG) wMouseY) << 16);
            break;

        case IDCMP_MENUPICK :
            if (iCode != MENUNULL)
                PostMessage(wGotMsg, WM_COMMAND, (WPARAM) GTMENUITEM_USERDATA(ItemAddress(hMenu, iCode)), (LPARAM) 0);
            break;

        case IDCMP_REFRESHWINDOW :
            BeginRefresh(wGotMsg);
	        PostMessage(wGotMsg, WM_PAINT, 0, 0);
            EndRefresh(wGotMsg, TRUE);
            break;

        case IDCMP_CLOSEWINDOW :
            PostMessage(wGotMsg, WM_CLOSE, 0, 0);
            break;

        case IDCMP_MOUSEBUTTONS:
            switch(iCode)
            {
                case MIDDLEDOWN:
                    wQualifier |= IEQUALIFIER_RCOMMAND;
                case SELECTDOWN:
                    PostMessage(wGotMsg, WM_LBUTTONDOWN, wQualifier, (ULONG) wMouseX & 0x0000FFFF | ((ULONG) wMouseY) << 16);
                    break;

                case MIDDLEUP:
                case SELECTUP:
                    PostMessage(wGotMsg, WM_LBUTTONUP, wQualifier, (ULONG) wMouseX & 0x0000FFFF | ((ULONG) wMouseY) << 16);
                    break;
            }
            break;

        default :
            vTrace(GetCatalogStr(hCat, MSG_0013, "*** E0034 : Message Class %08lX Code %04X Qualif %04X reçu et ignoré pour fenêtre %s"), iClass, iCode, wQualifier, wGotMsg -> Title);
            break;
    }
    return iClass;
}

#define mCleanExit(s, l)    \
{                           \
    vTrace(s);              \
    goto l;                 \
}

ULONG lFlags, lModeID, lError;

int main(int argc, char **argv)
{
    IntuiMessage *iMsg;
    int iClass = 0;
    ULONG lSigTop, lSigFace, lSigRight, lSig3D, lSigMenu, lSignals;
    TextAttr sTextAttr = {
        "courier.font",
        13,
        NULL, //FSF_ITALIC | FSF_BOLD,
        NULL
    };

    vTrace(sVersion);

    TextFont *hFont = NULL;

    // Mémoriser notre pointeur de tâche
    hApp = FindTask(NULL);

    // Ouvrir les librairies non ouvertes par défaut par les stubs Storm
    GadToolsBase   = OpenLibrary("gadtools.library",  39L);
    UtilityBase    = OpenLibrary("utility.library",   39L);
    AslBase        = OpenLibrary("asl.library",       39L);
    AmigaGuideBase = OpenLibrary("amigaguide.library",39L);
    DataTypesBase  = OpenLibrary("datatypes.library", 39L);
    IconBase       = OpenLibrary("icon.library",      39L);
    DiskfontBase   = OpenLibrary("diskfont.library",  39L);
    LocaleBase     = OpenLibrary("locale.library",    39L);
    CyberGfxBase   = OpenLibrary("cybergraphics.library", 0L);
    if (!(GadToolsBase && UtilityBase && AslBase && AmigaGuideBase && DataTypesBase && IconBase && CyberGfxBase && DiskfontBase && LocaleBase))
        mCleanExit("*** E0035 : librairie manquante, utilisez SnoopDos pour trouver laquelle\n*** E0035 : missing a library, please use SnoopDos to find which", _mce_CloseLibs);

    if (!(hCat = OpenCatalog(NULL, "skulpt.catalog",
                             OC_BuiltInLanguage, "français",
//                             OC_Version, 28,
                             TAG_DONE)))
        vTrace("*** I0000 : utilisation langage builtin (français)\n*** I0000 : using builtin language (français). No catalog found.");

#ifndef __PPC__
    Warp3DBase     = OpenLibrary("Warp3D.library",    3L);
    if (!Warp3DBase)
#else
    Warp3DPPCBase  = OpenLibrary("Warp3DPPC.library", 3L);
    if (!Warp3DPPCBase)
#endif
        mCleanExit(GetCatalogStr(hCat, MSG_0030, "*** E0036 : librairie Warp3D v3 manquante"), _mce_CloseLibs);

    if (!(hFont = OpenDiskFont(&sTextAttr)))
        mCleanExit(GetCatalogStr(hCat, MSG_0031, "*** E0098 : fonte introuvable"), _mce_CloseLibs);

    // Check for availability of drivers
    lFlags = W3D_CheckDriver();
    if (lFlags & W3D_DRIVER_UNAVAILABLE)
        mCleanExit(GetCatalogStr(hCat, MSG_0032, "*** E0037 : pas de driver Warp3D"), _mce_CloseLibs);

    // Choisir un mode d'écran compatible Warp3D
    lModeID = W3D_RequestModeTags(
	W3D_SMR_TYPE,       W3D_DRIVER_3DHW,
	W3D_SMR_DESTFMT,    ~W3D_FMT_CLUT,
        TAG_DONE);

    if (lModeID == INVALID_ID)
        mCleanExit(GetCatalogStr(hCat, MSG_0033, "*** E0038 : mode écran invalide"), _mce_CloseLibs);
                                                                            
    // Ouvrir l'écran selon mode demandé
    if (!(hInst = OpenScreenTags(NULL,
                           	 SA_DisplayID,          lModeID,
                                 SA_SharePens,          TRUE,
                                 SA_Title,              (ULONG) sVersion + 6,
                         	 SA_Draggable,          FALSE,
                                 SA_Font,               (ULONG) &sTextAttr,
                                 TAG_DONE)))
        mCleanExit(GetCatalogStr(hCat, MSG_0034, "*** E0039 : ouverture écran"), _mce_CloseLibs);

    NWS2D.Screen = NWS3D.Screen = NWSMenu.Screen = hInst;
    NWSMenu.Width = hInst -> Width; NWSMenu.Height = hInst -> Height;

    // Créer la structure de menus GadToolz en localisant le menu (c'est pourquoi il doit être inclus dans un contexte de fonction)
    {
        #include "menusample.h"
        hVi     = GetVisualInfo(hInst, TAG_DONE);
        hMenu   = CreateMenus(mNMenu, GTMN_FrontPen, 0, TAG_DONE);
        LayoutMenus(hMenu, hVi, TAG_DONE);
    }

    // Allouer les structures du filerequester
    if (!FSInit())
        mCleanExit(GetCatalogStr(hCat, MSG_0035, "*** E0040 : allocation filerequester"), _mce_CloseLibsAndScreenAndVI);

    // Allouer les structures du gestionnaire de dialogs
    if (!DLGInit("gfx/sampler.res"))
        mCleanExit(GetCatalogStr(hCat, MSG_0037, "*** E0048 : initialisation ressources"), _mce_CloseLibsAndScreenAndVIAndFS);

    // Préparer les gadgets
    if (!bPrepareGadgets())
        mCleanExit(GetCatalogStr(hCat, MSG_0038, "*** E0049 : initialisation gadgets"), _mce_CloseLibsAndScreenAndVIAndFSAndDlg);

    // Initialiser les tableaux d'objets 3D
    vDeleteObjects();

    // Se présenter...
    vLogo();
    // Créer la fenêtre de menus, y attacher le menustrip et définr son titre
    PostMessage(hWndMenu = OpenWindow(&NWSMenu), WM_CREATE, 0, 0); SetMenuStrip(hWndMenu, hMenu); SetWindowTitles(hWndMenu, sVersion + 6, (const char *) -1);

    // Attacher la console de trace à la fenêtre de menus
    if (!InitConsole(hWndMenu))
        mCleanExit(GetCatalogStr(hCat, MSG_0039, "*** E0047 : initialisation console"), _mce_CloseLibsAndScreenAndVIAndFSAndDlgAndGads);

#ifdef __PPC__  // Taing ça pulse trop aussi ici.
    Delay(50);
#endif

    // Charger les textures disponibles
    bLoadTextures();

    // Charger la configuration des hints W3D
    bLoadHints();

    // Créer les fenêtres 2D, leur envoyer un WM_CREATE, leur attacher la menustrip de la fenêtre menu, leur coller un titre
    PostMessage(hWndTop = OpenWindow(&NWS2D), WM_CREATE, 0, 0);   LendMenus(hWndTop, hWndMenu);   SetWindowTitles(hWndTop,   GetCatalogStr(hCat, MSG_0040, "Vue de dessus"), (const char *) -1); SetDrMd(GetDC(hWndTop), JAM1);
    PostMessage(hWndFace = OpenWindow(&NWS2D), WM_CREATE, 0, 0);  LendMenus(hWndFace, hWndMenu);  SetWindowTitles(hWndFace,  GetCatalogStr(hCat, MSG_0041, "Vue de face"), (const char *) -1);   SetDrMd(GetDC(hWndFace), JAM1);
    PostMessage(hWndRight = OpenWindow(&NWS2D), WM_CREATE, 0, 0); LendMenus(hWndRight, hWndMenu); SetWindowTitles(hWndRight, GetCatalogStr(hCat, MSG_0042, "Vue de droite"), (const char *) -1); SetDrMd(GetDC(hWndRight), JAM1);

    // Créer la fenêtre 3D etc....
//    PostMessage(hWndPersp = OpenWindow(&NWS3D), WM_CREATE, 0, 0); LendMenus(hWndPersp, hWndMenu);
    hWndPersp = OpenWindow(&NWS3D); LendMenus(hWndPersp, hWndMenu);

    // Laisser Intuition flusher son pipe sinon crash...
#ifdef __PPC__  // Taing, ça pulse trop.
    Delay(50);
#else
    Delay(10);
#endif

    // Redimensionner les fenêtres (ce qui vide la trace...)
    vRemakeTriview(0L);

    // Définir le filtre IDCMP de la fenêtre 3D et lui envoyer un WM_CREATE
    // (seulement maintenant pour éviter qu'elle ne fasse un WM_SIZE lors du redim des fenêtres)
    ModifyIDCMP(hWndPersp, IDCMP_VANILLAKEY | IDCMP_NEWSIZE | IDCMP_REFRESHWINDOW | IDCMP_MENUPICK);
    PostMessage(hWndPersp, WM_CREATE, 0, 0);

    // Forcer le rafraichissement complet de la fenêtre 3D
    vForce3DRefresh(XDC_MODE_COMPLET);

    // Mettre à jour les checkmarks des menus
    vUpdateMenu();

    // Créer le contexte W3D commun aux 3 fenêtres 2D (sans zbuf)
    {
        TagItem sTagList[] =
        {
            { W3D_CC_MODEID,        lModeID                             },
        	{ W3D_CC_BITMAP,        (ULONG) hInst -> RastPort.BitMap    },
        	{ W3D_CC_YOFFSET,       0                                   },
        	{ W3D_CC_DRIVERTYPE,    W3D_DRIVER_BEST                     },
            { W3D_CC_DOUBLEHEIGHT,  FALSE                               },
        	{ W3D_CC_FAST,          TRUE                                },
        	{ TAG_DONE,             TAG_DONE                            }
        };

        hW3DC_2D = W3D_CreateContext(&lError, sTagList);
    }

    if (!hW3DC_2D || lError != W3D_SUCCESS)
    {
    	switch(lError)
        {
        	case (ULONG) W3D_ILLEGALINPUT:   vTrace(GetCatalogStr(hCat, MSG_0043, "*** E0041 : paramètres CreateContext()"));  break;
        	case (ULONG) W3D_NOMEMORY:       vTrace(GetCatalogStr(hCat, MSG_0044, "*** E0042 : mémoire insuffisante"));        break;
        	case (ULONG) W3D_NODRIVER:       vTrace(GetCatalogStr(hCat, MSG_0045, "*** E0043 : driver invalide"));             break;
        	case (ULONG) W3D_UNSUPPORTEDFMT: vTrace(GetCatalogStr(hCat, MSG_0046, "*** E0044 : bitmap incompatible Warp3D"));  break;
        	case (ULONG) W3D_ILLEGALBITMAP:  vTrace(GetCatalogStr(hCat, MSG_0047, "*** E0045 : bitmap mal initialisé"));       break;
        	default:                         vTrace(GetCatalogStr(hCat, MSG_0048, "*** E0046 : erreur interne"));
        }
        goto _mce_CloseLibsAndScreenAndVIAndWindows;
    }

    // Set states pour le contexte 2D
    W3D_SetState(hW3DC_2D, W3D_SCISSOR,             W3D_ENABLE);
    W3D_SetState(hW3DC_2D, W3D_AUTOTEXMANAGEMENT,   W3D_ENABLE);

    W3D_SetState(hW3DC_2D, W3D_DITHERING,           W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_GOURAUD,             W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_ZBUFFERUPDATE,       W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_ZBUFFER,             W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_SYNCHRON,            W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_INDIRECT,            W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_TEXMAPPING,          W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_PERSPECTIVE,         W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_BLENDING,            W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_FOGGING,             W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_LOGICOP,             W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_STENCILBUFFER,       W3D_DISABLE);
    W3D_SetState(hW3DC_2D, W3D_DOUBLEHEIGHT,        W3D_DISABLE);

    // Hints pour maximiser les performances
    W3D_Hint(hW3DC_2D, W3D_H_TEXMAPPING, W3D_H_FAST);
    W3D_Hint(hW3DC_2D, W3D_H_MIPMAPPING, W3D_H_FAST);
    W3D_Hint(hW3DC_2D, W3D_H_BILINEARFILTER, W3D_H_FAST);
    W3D_Hint(hW3DC_2D, W3D_H_MMFILTER, W3D_H_FAST);
    W3D_Hint(hW3DC_2D, W3D_H_PERSPECTIVE, W3D_H_FAST);
    W3D_Hint(hW3DC_2D, W3D_H_BLENDING, W3D_H_FAST);
    W3D_Hint(hW3DC_2D, W3D_H_FOGGING, W3D_H_FAST);
    W3D_Hint(hW3DC_2D, W3D_H_ANTIALIASING, W3D_H_FAST);
    W3D_Hint(hW3DC_2D, W3D_H_DITHERING, W3D_H_FAST);
    W3D_Hint(hW3DC_2D, W3D_H_ZBUFFER, W3D_H_FAST);

    // Afficher les propriétés des drivers Warp installés
    strcpy(s3DTitle,
#ifdef __PPC__
           GetCatalogStr(hCat, MSG_0049, "Vue 3D [soft T&L (PPC), ")
#else
           GetCatalogStr(hCat, MSG_0049, "Vue 3D [soft T&L (060), ")
#endif
          );
    vWarpInfo();
    SetWindowTitles(hWndPersp, s3DTitle, (const char *) -1);

    // Préparer les matrices world / view / proj
    hrInitWorld(NULL);

    // Finaliser la liste des messages broadcastés pour les fenêtres 2D
    lSigMask = IDCMP_ACTIVEWINDOW | IDCMP_VANILLAKEY    | IDCMP_NEWSIZE
             | IDCMP_MOUSEBUTTONS | IDCMP_MOUSEMOVE     | IDCMP_REFRESHWINDOW
             | IDCMP_MENUPICK;
    ModifyIDCMP(hWndTop,    lSigMask);
    ModifyIDCMP(hWndFace,   lSigMask);
    ModifyIDCMP(hWndRight,  lSigMask);

    // Préparer les masques de signaux pour la boucle de messages
    lSigMenu = 1L << hWndMenu -> UserPort -> mp_SigBit;
    lSigTop = 1L << hWndTop -> UserPort -> mp_SigBit;
    lSigFace = 1L << hWndFace -> UserPort -> mp_SigBit;
    lSigRight = 1L << hWndRight -> UserPort -> mp_SigBit;
    lSig3D = 1L << hWndPersp -> UserPort -> mp_SigBit;
    l2DWinSigMask = lSigTop | lSigFace | lSigRight;
    lSigMask = lSigMenu | l2DWinSigMask | lSig3D;

    // Boucle principale de traitement des messages Intuition
    do
    {
	// Attendre un message sur toutes les fenêtres
#ifdef __PPC__
        lSignals = WaitPPC(lSigMask);
#else
        lSignals = Wait(lSigMask);
#endif
        // Traiter tous les messages dans la file de la fenêtre Menus
        if (lSignals & lSigMenu)
            while (iMsg = (struct IntuiMessage *) GetMsg(hWndMenu -> UserPort))
                iClass = iDispatchIntuiMessage(hWndMenu, iMsg);
      
    	// Traiter tous les messages dans la file de la fenêtre Vue de dessus
      	if (lSignals & lSigTop)
            while (iMsg = (struct IntuiMessage *) GetMsg(hWndTop -> UserPort))
                iClass = iDispatchIntuiMessage(hWndTop, iMsg);

    	// Traiter tous les messages dans la file de la fenêtre Vue de face
    	if (lSignals & lSigFace)
            while (iMsg = (struct IntuiMessage *) GetMsg(hWndFace -> UserPort))
                iClass = iDispatchIntuiMessage(hWndFace, iMsg);

    	// Traiter tous les messages dans la file de la fenêtre Vue de droite
    	if (lSignals & lSigRight)
            while (iMsg = (struct IntuiMessage *) GetMsg(hWndRight -> UserPort))
                iClass = iDispatchIntuiMessage(hWndRight, iMsg);

    	// Traiter tous les messages dans la file de la fenêtre Vue 3D
    	if (lSignals & lSig3D)
            while (iMsg = (struct IntuiMessage *) GetMsg(hWndPersp -> UserPort))
                iClass = iDispatchIntuiMessage(hWndPersp, iMsg);

        // Traiter tous les messages dans la file de la dlgbox
        if (hWndDlg && (lSignals & (1L << hWndDlg -> UserPort -> mp_SigBit)))
            vProcessDlgMessage();
    }
    // Jusqu'à IDCMP_CLOSEWINDOW (qui ne peut venir que de la fenêtre Menus)
    while (iClass != IDCMP_CLOSEWINDOW);

    if (hW3DC_2D) W3D_DestroyContext(hW3DC_2D);

_mce_CloseLibsAndScreenAndVIAndWindows:
    // Libérer les entités allouées par les fenêtres en leur envoyant un WM_DESTROY
    if (hWndDlg)    EndDialog(hWndDlg, -1);
    if (hWndTop)    PostMessage(hWndTop,    WM_DESTROY, 0, 0);
    if (hWndFace)   PostMessage(hWndFace,   WM_DESTROY, 0, 0);
    if (hWndRight)  PostMessage(hWndRight,  WM_DESTROY, 0, 0);
    if (hWndPersp)  PostMessage(hWndPersp,  WM_DESTROY, 0, 0);
    if (hWndMenu)   PostMessage(hWndMenu,   WM_DESTROY, 0, 0);

    // Détacher la menustrip
    if (hWndTop)    LendMenus(hWndTop, NULL);
    if (hWndFace)   LendMenus(hWndFace, NULL);
    if (hWndRight)  LendMenus(hWndRight, NULL);
    if (hWndPersp)  LendMenus(hWndPersp, NULL);
    if (hWndMenu)   ClearMenuStrip(hWndMenu);

    // Refermer la console de trace
    if (hWndMenu)   CloseConsole();
    
    // Fermer les fenêtres
    if (hWndTop)    CloseWindow(hWndTop);
    if (hWndFace)   CloseWindow(hWndFace);
    if (hWndRight)  CloseWindow(hWndRight);
    if (hWndPersp)  CloseWindow(hWndPersp);
    if (hWndMenu)   CloseWindow(hWndMenu);

    // Dropper les textures
    vUnLoadTextures();

_mce_CloseLibsAndScreenAndVIAndFSAndDlgAndGads:
    // Libérer les gadgets
    vEraseGadgets();

_mce_CloseLibsAndScreenAndVIAndFSAndDlg:
    // Libérer les structures File Requester et screenmoderequester et du gestionnaire de console
    DLGClose();
_mce_CloseLibsAndScreenAndVIAndFS:
    FSClose();

_mce_CloseLibsAndScreenAndVI:
    // Détruire le menustrip
    FreeMenus(hMenu);

    // Libérer le Visual Info Gadtoolz
    FreeVisualInfo(hVi);

    // Fermer l'écran s'il s'agit d'un écran custom
    if (lModeID != INVALID_ID)
        CloseScreen(hInst);

_mce_CloseLibs:
    if (hCat) CloseCatalog(hCat);

    // Refermer les librairies
    if (GadToolsBase)   CloseLibrary(GadToolsBase);
    if (UtilityBase)    CloseLibrary(UtilityBase);
    if (AslBase)        CloseLibrary(AslBase);
    if (AmigaGuideBase) CloseLibrary(AmigaGuideBase);
    if (DataTypesBase)  CloseLibrary(DataTypesBase);
    if (IconBase)       CloseLibrary(IconBase);
    if (DiskfontBase)   CloseLibrary(DiskfontBase);
    if (LocaleBase)     CloseLibrary(LocaleBase);
    if (CyberGfxBase)   CloseLibrary(CyberGfxBase);
#ifndef __PPC__
    if (Warp3DBase)     CloseLibrary(Warp3DBase);
#else
    if (Warp3DPPCBase)  CloseLibrary(Warp3DPPCBase);
#endif

    CloseFont(hFont);
}

// Sortir une trace formattée dans la listbox de la fenêtre trace
void vTrace(LPSTR fmt, ... )
{
    char cBuffer[256];
    va_list args;

    // Formatter la trace
    va_start(args, fmt);
    vsprintf(cBuffer, fmt, args);
    va_end(args);

    strcat(cBuffer, "\n");

    if (bConsoleOpened)
        ConPutS(cBuffer);
//    else
        printf(cBuffer);
}

BOOL bLoadHints(void)
{
    FILE *hFile = fopen("gfx/w3d.hints", "r");

    if (!hFile)
    {
        vTrace(GetCatalogStr(hCat, MSG_0054, "*** E0099 : ouverture fichier hints en lecture"));
        return FALSE;
    }

    fread(uHint, 1, sizeof(uHint), hFile);
    fclose(hFile);

    return TRUE;
}

BOOL bSaveHints(void)
{
    FILE *hFile = fopen("gfx/w3d.hints", "w");

    if (!hFile)
    {
        vTrace(GetCatalogStr(hCat, MSG_0057, "*** E0100 : ouverture fichier hints en écriture"));
        return FALSE;
    }

    fwrite(uHint, 1, sizeof(uHint), hFile);
    fclose(hFile);

    return TRUE;
}

void DestroyWindow(HWND hWnd)
{
    PostMessage(hWnd, WM_DESTROY, 0, 0);
}

// Gestion du clipping fenêtres
struct Region *ClipWindow(HWND hWnd, LONG lMinX, LONG lMinY, LONG lMaxX, LONG lMaxY)
{
    struct Region *hRNew;
    struct Rectangle rRect;

    rRect.MinX = lMinX;
    rRect.MinY = lMinY;
    rRect.MaxX = lMaxX;
    rRect.MaxY = lMaxY;

    hRNew = NewRegion();
    OrRectRegion(hRNew, &rRect);

    return InstallClipRegion(hWnd -> WLayer, hRNew);
}

void UnclipWindow(HWND hWnd)
{
    register struct Region *hROld;

    if (hROld = InstallClipRegion(hWnd -> WLayer, NULL))
        DisposeRegion(hROld);
}

struct RastPort *GetDC(HWND hWnd)
{
    return hWnd -> RPort;
}

BOOL ExtTextOut(HDC hdc,int X,int Y, int fuOptions, RECT *lprc, char *lpString, int cbCount, int *lpDx)
{
    Move(hdc, X, Y + 24);
    Text(hdc, lpString, cbCount);
    return TRUE;
}

BOOL PtInRect(RECT *rRect, POINT pPoint)
{
    if (pPoint.x > rRect -> right)  return FALSE;
    if (pPoint.x < rRect -> left)   return FALSE;
    if (pPoint.y > rRect -> bottom) return FALSE;
    if (pPoint.y < rRect -> top)    return FALSE;
    return TRUE;
}

// Récupérer le rectangle complet de la zone client de la fenêtre
void GetClientRect(HWND hWnd, RECT *rRect)
{
    rRect -> left   = hWnd -> BorderLeft;
    rRect -> right  = hWnd -> Width - hWnd -> BorderRight - 1;
    rRect -> top    = hWnd -> BorderTop;
    rRect -> bottom = hWnd -> Height - hWnd -> BorderBottom - 1;
}

// Cloner un rectangle (r1 <- r2)
void CopyRect(RECT *rR1, RECT *rR2)
{
    *rR1 = *rR2;
}

// Enfler un rectangle
void InflateRect(RECT *rRect, int iX, int iY)
{
    rRect -> left   -= iX;
    rRect -> right  += iX;
    rRect -> top    -= iY;
    rRect -> bottom += iY;
}

// Définir un rectangle (x / y / lagreur / hauteur)
void SetRect(RECT *rRect, int x, int y, int w, int h)
{
    rRect -> left   = x;
    rRect -> right  = x + w;
    rRect -> top    = y;
    rRect -> bottom = y + h;
}

static BOOL CheckItem(MenuItem *hMI, int iId, int iState)
{
    if (!hMI) return FALSE;

    if (GTMENUITEM_USERDATA(hMI) == (APTR) iId)
    {
        if (iState & MF_CHECKED)
            hMI -> Flags |= CHECKED;
        else
            hMI -> Flags &= ~CHECKED;
        return TRUE;
    }

    if (hMI -> SubItem && CheckItem(hMI -> SubItem, iId, iState))
            return TRUE;

    return CheckItem(hMI -> NextItem, iId, iState);
}

static BOOL CheckMenu(HMENU hMenu, int iId, int iState)
{
    if (!hMenu) return FALSE;

    if (hMenu -> FirstItem && CheckItem(hMenu -> FirstItem, iId, iState))
        return TRUE;

    return CheckMenu(hMenu -> NextMenu, iId, iState);
}

void CheckMenuItem(HMENU hMenu, int iId, int iState)
{
    CheckMenu(hMenu, iId, iState);
}

HMENU GetMenu(HWND hWnd)
{
    return hWnd -> MenuStrip;
}

BOOL bPrepareGadgets(void)
{
    if (!(gAddPoint.hIcon   = GetDiskObjectNew("gfx/BTN_ADD_POINT")))       return FALSE;
    if (!(gCenter.hIcon	    = GetDiskObjectNew("gfx/BTN_CENTER")))          return FALSE;
    if (!(gGoDown.hIcon	    = GetDiskObjectNew("gfx/BTN_GO_DOWN")))         return FALSE;
    if (!(gGoLeft.hIcon	    = GetDiskObjectNew("gfx/BTN_GO_LEFT")))         return FALSE;
    if (!(gGoUp.hIcon	    = GetDiskObjectNew("gfx/BTN_GO_UP")))           return FALSE;
    if (!(gGoRight.hIcon    = GetDiskObjectNew("gfx/BTN_GO_RIGHT")))        return FALSE;
    if (!(gGrab.hIcon	    = GetDiskObjectNew("gfx/BTN_GRABBER")))         return FALSE;
    if (!(gMakeTri.hIcon    = GetDiskObjectNew("gfx/BTN_MAKE_TRIANGLE")))   return FALSE;
    if (!(gReverse.hIcon    = GetDiskObjectNew("gfx/BTN_REVERSE")))         return FALSE;
    if (!(gRotAnti.hIcon    = GetDiskObjectNew("gfx/BTN_ROTATE_TRIGO")))    return FALSE;
    if (!(gRotClock.hIcon   = GetDiskObjectNew("gfx/BTN_ROTATE_CLOCK")))    return FALSE;
    if (!(gZoomIn.hIcon	    = GetDiskObjectNew("gfx/BTN_ZOOM_IN")))         return FALSE;
    if (!(gZoomOut.hIcon    = GetDiskObjectNew("gfx/BTN_ZOOM_OUT")))        return FALSE;

    return TRUE;
}

void vEraseGadgets(void)
{
    if (gAddPoint.hIcon)FreeDiskObject(gAddPoint.hIcon);
    if (gCenter.hIcon)  FreeDiskObject(gCenter.hIcon);
    if (gGoDown.hIcon)  FreeDiskObject(gGoDown.hIcon);
    if (gGoLeft.hIcon)  FreeDiskObject(gGoLeft.hIcon);
    if (gGoUp.hIcon)    FreeDiskObject(gGoUp.hIcon);
    if (gGoRight.hIcon) FreeDiskObject(gGoRight.hIcon);
    if (gGrab.hIcon)    FreeDiskObject(gGrab.hIcon);
    if (gMakeTri.hIcon) FreeDiskObject(gMakeTri.hIcon);
    if (gReverse.hIcon) FreeDiskObject(gReverse.hIcon);
    if (gRotAnti.hIcon) FreeDiskObject(gRotAnti.hIcon);
    if (gRotClock.hIcon)FreeDiskObject(gRotClock.hIcon);
    if (gZoomIn.hIcon)  FreeDiskObject(gZoomIn.hIcon);
    if (gZoomOut.hIcon) FreeDiskObject(gZoomOut.hIcon);
}

HGDIOBJ hAddImage(HWND hWnd, char *sName, int iX, int iY, int iWidth, int iHeight)
{
    HGDIOBJ hObject = NewDTObject(sName,
    	DTA_SourceType,	    DTST_FILE,
        GA_RelVerify,     	TRUE,
        GA_Immediate,       TRUE,
        GA_Left,	        iX,
        GA_Top,		        iY,
        GA_Width,           iWidth,
        GA_Height,          iHeight,
        ICA_TARGET,	        ICTARGET_IDCMP,
	    TAG_DONE);

    if (!hObject)
        return NULL;

    AddDTObject(hWnd, NULL, hObject, -1);
    return hObject;
}

void vMoveImage(HWND hWnd, HGDIOBJ hObject, int iX, int iY)
{
    RemoveDTObject(hWnd, hObject);
    SetDTAttrs(hObject, hWnd, NULL,
        GA_Left,	    iX,
        GA_Top,		    iY,
        TAG_DONE);
    AddDTObject(hWnd, NULL, hObject, -1);
}

void vRefreshImage(HWND hWnd, HGDIOBJ hObject)
{
    RefreshDTObjectA(hObject, hWnd, NULL, NULL);
}

void vRemoveImage(HWND hWnd, HGDIOBJ hObject)
{
    RemoveDTObject(hWnd, hObject);
    DisposeDTObject(hObject);
}

//###############################################################################
// Protos non mappés
void vAddTextOutline(void)
{
}

DWORD GetTickCount(void)
{
    return (DWORD) 0;
}

void ShowWindow(HWND hWnd, int iI)
{
}     

void PostQuitMessage(int iI)
{
}


