#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>
#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"

#define RT_CURSOR       1
#define RT_BITMAP       2
#define RT_ICON         3
#define RT_MENU         4
#define RT_DIALOG       5
#define RT_STRING       6
#define RT_FONTDIR      7
#define RT_FONT         8
#define RT_ACCELERATOR  9
#define RT_RCDATA       10
#define RT_MESSAGETABLE 11
#define RT_GROUP_CURSOR 12
#define RT_GROUP_ICON   14
#define RT_VERSION      16
#define RT_DLGINCLUDE   17
#define RT_PLUGPLAY     19
#define RT_VXD          20
#define RT_ANICURSOR    21
#define RT_ANIICON      22
#define RT_HTML         23

static const XDC_IS_ORDINAL = 0;
static const XDC_IS_NAME    = 1;
#define XDC_C_FACTOR  3/2

typedef union 
{
	UWORD	 wOrd;
	char	szName[80];
} OrdinalOrName;

typedef struct
{
	DWORD dwDataSize;
	DWORD dwHeaderSize;
	DWORD dwDataVersion;
    UWORD wMemoryFlags;
    UWORD wLanguageId;
    DWORD dwVersion;  
	DWORD dwCharacteristics; 
	LPCTSTR lpType;
	LPCTSTR lpName;
	UINT  uResTypeTag;
	OrdinalOrName ResType;
	UINT  uResNameTag;
	OrdinalOrName ResName;
	void	*Data;
}  ResEntry;

// Globales
static unsigned char *amsResBasePtr;   // Base des ressources
static unsigned long amsResSize;	   // Tailles des ressources

Window *CreateWindow(ULONG x, ULONG y, ULONG largeur, ULONG hauteur, char *titre)
{
    Gadget *gadtemp, **gadlist = &gadtemp;

    CreateContext(gadlist);

    return OpenWindowTags(NULL,
                WA_Left,        x,
                WA_Top,         y,
                WA_Width,       largeur * XDC_C_FACTOR + hInst->WBorLeft + hInst->WBorRight,
                WA_Height,      hauteur * XDC_C_FACTOR + hInst->WBorTop + hInst->BarHeight + hInst->WBorBottom,
                WA_Title,       titre,
                WA_Gadgets,     gadtemp,
                WA_CustomScreen,(ULONG) hInst,
                WA_DepthGadget, TRUE,
                WA_CloseGadget, TRUE,
                WA_DragBar,     TRUE,
                WA_IDCMP,       IDCMP_CLOSEWINDOW);
}

void AddGad(Window *win, ULONG type, ULONG x, ULONG y, ULONG largeur, ULONG hauteur, char *label)
{
    struct Gadget *gad, *temp;
    static struct NewGadget gadtemp;
    char *text = (char *)malloc(strlen(label) + 1);
    memcpy(text, label, strlen(label) + 1);

    gadtemp.ng_LeftEdge = x * XDC_C_FACTOR + win->BorderLeft;
    gadtemp.ng_TopEdge = y * XDC_C_FACTOR + win->WScreen->BarHeight;
    gadtemp.ng_Width = largeur * XDC_C_FACTOR;
    gadtemp.ng_Height = (type == TEXT_KIND) ? 16 : hauteur * XDC_C_FACTOR;
    gadtemp.ng_GadgetText = ((type == CHECKBOX_KIND) /*|| (type == TEXT_KIND)*/) ? NULL : text;
    gadtemp.ng_TextAttr = NULL;
    gadtemp.ng_GadgetID = 0;
    gadtemp.ng_Flags = PLACETEXT_IN;
    gadtemp.ng_VisualInfo = GetVisualInfoA(win->WScreen,NULL);
    gadtemp.ng_UserData = 0;

    temp = win->FirstGadget;
    while(temp->NextGadget)
        temp = temp->NextGadget;

    switch(type)
    {
        case BUTTON_KIND:
            gad = CreateGadget(type, temp, &gadtemp, TAG_DONE);
            break;
        case TEXT_KIND:
            gad = CreateGadget(type, temp, &gadtemp,/* GTTX_Text, text, GTTX_Border, FALSE, GTTX_Justification, GTJ_LEFT, GTTX_Clipped, TRUE,*/ TAG_DONE);
            break;
        case STRING_KIND:
            gad = CreateGadget(type, temp, &gadtemp, GTST_String, label, TAG_DONE);
            break;
        case SCROLLER_KIND:
            gad = CreateGadget(type, temp, &gadtemp, GTSC_Total, 200, TAG_DONE);
            break;
        case CHECKBOX_KIND:
            gad = CreateGadget(type, temp, &gadtemp, GTCB_Scaled, TRUE, TAG_DONE);
            break;

        default:
            gad = CreateGadget(type, temp, &gadtemp, TAG_DONE);
    }
}

BOOL DLGInit(char *sResFileName)
{
	BPTR hFileLock = Lock(sResFileName, SHARED_LOCK);

	if(hFileLock != NULL)
	{
		struct FileInfoBlock sFib;
		BOOL bOk = FALSE;

		if(Examine(hFileLock,&sFib) && sFib.fib_DirEntryType < 0 && sFib.fib_Size > 0)
		{
			amsResSize = sFib.fib_Size;
			amsResBasePtr = (unsigned char *) AllocVec(amsResSize,MEMF_ANY|MEMF_PUBLIC);
			if(amsResBasePtr != NULL)
			{
				BPTR hFileHandle;

				hFileHandle = Open(sResFileName,MODE_OLDFILE);
				if(hFileHandle != NULL)
				{
					if(Read(hFileHandle,amsResBasePtr, amsResSize) == amsResSize)
						bOk = TRUE;

					Close(hFileHandle);
				}
			}
		}
		UnLock(hFileLock);

		if(!bOk)
		{
			FreeVec(amsResBasePtr);
			amsResBasePtr = NULL;
			amsResSize = 0;
            return FALSE;
	    }
        return TRUE;
    }
    else
        return FALSE;
}

void DLGClose(void)
{
    if (amsResBasePtr)
    {
        FreeVec( amsResBasePtr );
        amsResBasePtr = NULL;
    }
}

static BOOL IsIntel(void)
{
	static long IsIntel = 1;

	if( *((char *)&IsIntel) == 1 )	// Intel
		return TRUE;
    return FALSE;					// Motorola
}

static UWORD IntelWord(UWORD *addr)
{
	unsigned char B1, B2;

	if( IsIntel() )
		return *addr;

	B1 = *((unsigned char *) addr );
	B2 = *(((unsigned char *) addr ) + 1);
	return (UWORD) B1 | ((UWORD) B2 << 8);
}
static DWORD IntelLong(DWORD *addr)
{
	UWORD W1, W2;

	if( IsIntel() )
	{
		return *addr;
	}
	else
	{
		W1 = IntelWord( (UWORD*) addr );
		W2 = IntelWord( ((UWORD*) addr) + 1 );
		return (ULONG) W1 | ((ULONG) W2 << 16);
	}
}

// Lit un objet encodé sous la forme name ou ordinal
static int ReadNameOrOrdinal( unsigned char **pos, UWORD *wOrdinal, char *Name )
{
	unsigned char *carPtr;

	carPtr = *pos;

	if( (carPtr[0] == 0xFF) && ( carPtr[1] == 0xFF) )
	{
		// C'est un ordinal (UWORD suivant 0xFFFF)
		*wOrdinal = IntelWord( (UWORD*)&carPtr[2] );
		*pos = *pos + 4;
		return XDC_IS_ORDINAL;
	}
	else
	{
		while( *Name++ = *carPtr ) carPtr += 2;
		
		*pos = carPtr + 2;
		return XDC_IS_NAME;
	}
}

static unsigned char *ReadResEntry(unsigned char *pos, ResEntry *res)
{
	DWORD dwTaille;

	dwTaille = res->dwDataSize = IntelLong( ((DWORD*) pos)++  );
	res->dwHeaderSize = IntelLong( ((DWORD*) pos)++  );
	res->uResTypeTag = ReadNameOrOrdinal( &pos, &(res->ResType.wOrd), res->ResType.szName  );
	res->uResNameTag = ReadNameOrOrdinal( &pos, &(res->ResName.wOrd), res->ResName.szName );

	if( res->uResTypeTag == 0) 
		res->lpType = MAKEINTRESOURCE(res->ResType.wOrd);
	else
		res->lpType = (LPCTSTR)res->ResType.szName;

	if( res->uResNameTag == 0) 
		res->lpName = MAKEINTRESOURCE(res->ResName.wOrd);
	else
		res->lpName  = (LPCTSTR)res->ResName.szName;

	res->dwDataVersion     = IntelLong( ((DWORD*) pos)++ );
	res->wMemoryFlags      = IntelWord( ((UWORD*) pos)++ );
	res->wLanguageId       = IntelWord( ((UWORD*) pos)++ );
	res->dwVersion         = IntelLong( ((DWORD*) pos)++ );
	res->dwCharacteristics = IntelLong( ((DWORD*) pos)++ );
	res->Data = pos;

	// Skip DataSize multiple de 4
	if ((dwTaille & 0x3) != 0) dwTaille = (dwTaille & 0xFFFFFFFC) + 4;
	return pos + dwTaille;
}

// Compare deux LPCTSTR
static int CheckLPCT( LPCTSTR lp1, LPCTSTR lp2 )
{
	// On ne peut comparer que des types compatibles
	if( (((long)lp1 & 0xFFFF0000) == 0) && (((long)lp2 & 0xFFFF0000) == 0))
	{
		// Ordinal
		if( (long)lp1 == (long)lp2 ) return -1;
		return 0;
	}

	if( (((long)lp1 & 0xFFFF0000) != 0) && (((long)lp2 & 0xFFFF0000) != 0))
	{
		if( strcmp( (char*)lp1, (char*)lp2 ) == 0 ) return -1;
		return 0;
	}

	return 0;
}

// Récupère une WSTRING dans la ressource et en fait une string non unicode
static unsigned char *myGetString( unsigned char *ucChar, char *szString )
{
	while( *szString++ = *ucChar  ) ucChar += 2;

	ucChar += 2;

	return ucChar;
}

// Appelé en boucle pour chaque item de la structure de la dialog pour ajouter
// l'item à la fenêtre
static unsigned char *myLoadDialogItem(HWND hWnd, unsigned char *ucData )
{
	DWORD	dwHelpID;
	DWORD	dwStyle;
	DWORD	dwExtendedStyle; 
	UWORD	 wID;
	short	x, y, cx, cy; 
	UWORD	 wExtraBytes;
	UWORD	 wTagNom, wTagClasse;
	OrdinalOrName leNom;
	OrdinalOrName laClasse;

	// DialogItem est alignée sur un DWORD
	if( (long)ucData & 3 ) 
		ucData = (unsigned char*) (((long)ucData & 0xFFFFFFFC) + 4);

	// Lecture des champs
	dwHelpID		= IntelLong( ((DWORD*) ucData)++ );
	dwExtendedStyle = IntelLong( ((DWORD*) ucData)++ );
	dwStyle			= IntelLong( ((DWORD*) ucData)++ );
	x				= IntelWord( ((UWORD*)  ucData)++ );
	y				= IntelWord( ((UWORD*)  ucData)++ );
	cx				= IntelWord( ((UWORD*)  ucData)++ );
	cy				= IntelWord( ((UWORD*)  ucData)++ );
	wID 			= IntelWord( ((UWORD*)  ucData)++ );

	ucData+=2;

	wTagClasse = ReadNameOrOrdinal( &ucData, &(laClasse.wOrd), laClasse.szName  );
	wTagNom = ReadNameOrOrdinal( &ucData, &(leNom.wOrd), leNom.szName  );
	wExtraBytes = IntelWord( ((UWORD*)ucData)++ );

	ULONG aType;
    switch( laClasse.wOrd )
	{
		case 0x0080:
            switch(dwStyle & 0xF)
            {
                case BS_CHECKBOX:
                case BS_AUTOCHECKBOX:
                    aType = CHECKBOX_KIND;  break;
                case BS_RADIOBUTTON:
                case BS_AUTORADIOBUTTON:
                    aType = MX_KIND;        break;
                case BS_GROUPBOX:
                    aType = TEXT_KIND;      break;
                default:
                    aType = BUTTON_KIND;    break;
            }
            break;
		case 0x0081: aType = STRING_KIND;   break;
		case 0x0082: aType = TEXT_KIND;     break;
		case 0x0083: aType = LISTVIEW_KIND; break;
		case 0x0084: aType = SCROLLER_KIND; break;
		case 0x0085: aType = CYCLE_KIND;    break;
        default : aType = ~0; break;
	}
    if (aType != ~0)
        AddGad(hWnd, aType, x, y, cx, cy, leNom.szName);

	return ucData + wExtraBytes;
}

// Charge le dialogbox à partir du pointeur sur les données de la dialog dans les ressources
// (qui a été renseigné par myFindRessource())
void myLoadDialog( unsigned char *ucData )
{
	DWORD	dwStyle;
	DWORD	dwExtendedStyle; 
	UWORD	 wNbItem;
	short	x, y, cx, cy; 
	BOOL	bExtended;
	char	szTitle[128], szFont[80];
	UWORD	 wTagMenu, wTagClasse;
	OrdinalOrName leMenu;
	OrdinalOrName laClasse;
    HWND hWnd;

	// Lire la signature
    if (IntelWord( ((UWORD*)  ucData) + 1 ) == 0xFFFF)
        bExtended = TRUE;
    else
        bExtended = FALSE;

	if( bExtended )
	{
		ucData += 8;
		dwExtendedStyle = IntelLong( ((DWORD*) ucData)++ );
		dwStyle			= IntelLong( ((DWORD*) ucData)++ );
	}
	else
	{
		dwStyle			= IntelLong( ((DWORD*) ucData)++ );
		dwExtendedStyle = IntelLong( ((DWORD*) ucData)++ );
	}

	wNbItem			= IntelWord( ((UWORD*)  ucData)++ );
	x				= IntelWord( ((UWORD*)  ucData)++ );
	y				= IntelWord( ((UWORD*)  ucData)++ );
	cx				= IntelWord( ((UWORD*)  ucData)++ );
	cy				= IntelWord( ((UWORD*)  ucData)++ );

	// Le menu & la Classe
	if( ( wTagMenu = IntelWord( ((UWORD*)ucData)++ )) != 0x0000 )	 // Il y a un menu
	{
		if( wTagMenu == 0xFFFF ) leMenu.wOrd = IntelWord( ((UWORD*)ucData)++ );
		else ucData = myGetString( ucData, leMenu.szName );
	}

	if( ( wTagClasse = IntelWord( ((UWORD*)ucData)++ )) != 0x0000 )	 // Il y a une classe
	{
		if( wTagClasse == 0xFFFF ) leMenu.wOrd = IntelWord( ((UWORD*)ucData)++ );
		else ucData = myGetString( ucData, laClasse.szName );
	}

	// Le nom
	ucData = myGetString( ucData, szTitle );

	if( (dwStyle & DS_SETFONT) == DS_SETFONT )
	{
		ucData += 6;
		ucData = myGetString( ucData, szFont );
	}

	vTrace("Dialog box '%s' @(%d,%d), taille (%d,%d), %d contrôles",szTitle, x, y,cx,cy, wNbItem);

    hWnd = CreateWindow(x, y, cx, cy, szTitle);

	// Lecture des Items
	while( wNbItem-- )
		ucData = myLoadDialogItem(hWnd, ucData );

    // Rafraîchir les gadgets
    RefreshGadgets(hWnd->FirstGadget, hWnd, NULL);

    // Attendre un message sur la fenêtre
    while(((IntuiMessage *)WaitPort(hWnd->UserPort))->Class != IDCMP_CLOSEWINDOW)
        GT_ReplyIMsg(GT_GetIMsg(hWnd->UserPort));

    // Fermer la fenêtre
    CloseWindow(hWnd);
}

// Trouve une ressource dans le fichier de ressource en fonction de son nom et de son type
static BOOL myFindResource( LPCTSTR lpTemplate, LPCTSTR lpType, ResEntry *res )
{
	unsigned char *ucBasePtr, *ucFin;
	
	ucBasePtr = amsResBasePtr;
	ucFin = ucBasePtr + amsResSize;

	while(ucBasePtr < ucFin)
	{
		ucBasePtr = ReadResEntry( ucBasePtr, res );

		// Est-ce le bon type de ressource et la bonne ressource
		if( CheckLPCT( lpType, res->lpType ) &&
				CheckLPCT( lpTemplate, res->lpName ) ) 
					return TRUE;
    }

	return FALSE;
}

int DialogBox(HINSTANCE hInst, LPCTSTR sRes, HWND hWnd, DLGPROC vVoid)
{
	ResEntry res;
    vTrace("Chargement dialog %d", sRes);
	if( myFindResource( sRes , (LPCTSTR) RT_DIALOG, &res) )
		myLoadDialog( (unsigned char *) res.Data );

	return NULL;
}

int DialogBoxParam(HINSTANCE hInst, LPCTSTR sRes, HWND hWnd, DLGPROC vVoid, LPARAM lParam)
{
	ResEntry res;
    vTrace("Chargement dialog %d (param. %ld)", sRes, lParam);
	if( myFindResource( sRes , (LPCTSTR) RT_DIALOG, &res) )
		myLoadDialog( (unsigned char *) res.Data );

	return NULL;
}


