#include "mmcpl.h"
#include "utils.h"

/*
 ***************************************************************
 *  Typedefs
 ***************************************************************
 */
typedef struct _DYNLOAD_INFO
{
    LPCTSTR  pszLib;
    HMODULE hLib;
    short   iRefCnt;
}
DYNLOAD_INFO, *PDYNLOAD_INFO;

/*
 ***************************************************************
 * File Globals
 ***************************************************************
 */
static SZCODE aszMSACM32[] = TEXT("MSACM32.DLL");
static SZCODE aszAVIFIL32[] = TEXT("AVIFIL32.DLL");
static SZCODE aszMSVFW32[] = TEXT("MSVFW32.DLL");
static SZCODE aszVERSION[] = TEXT("VERSION.DLL");


DYNLOAD_INFO DynLoadInfo[] =
{
    aszMSACM32,   0, 0,
    aszAVIFIL32,  0, 0,
    aszMSVFW32,   0, 0,
    aszVERSION,   0, 0,
    NULL,         0, 0
};

static const char cszTacmFormatDetailsW[] = "acmFormatDetailsW";
static const char cszTacmFormatTagDetailsW[] = "acmFormatTagDetailsW";
static const char cszTacmDriverDetailsW[] = "acmDriverDetailsW";
static const char cszTacmDriverMessage[]  = "acmDriverMessage";
static const char cszTacmDriverAddW[]     = "acmDriverAddW";
static const char cszTacmDriverEnum[]     = "acmDriverEnum";
static const char cszTacmDriverPriority[] = "acmDriverPriority";
static const char cszTacmDriverRemove[]   = "acmDriverRemove";
static const char cszTacmMetrics[]        = "acmMetrics";
static const char cszTacmFormatChooseW[]  = "acmFormatChooseW";

PROC_INFO ACMProcs[] =
{
    cszTacmFormatDetailsW,    0,
    cszTacmFormatTagDetailsW, 0,
    cszTacmDriverDetailsW,    0,
    cszTacmDriverMessage,     0,
    cszTacmDriverAddW,        0,
    cszTacmDriverEnum,        0,
    cszTacmDriverPriority,    0,
    cszTacmDriverRemove,      0,
    cszTacmMetrics,           0,
    cszTacmFormatChooseW,     0,

    NULL, 0
};

static const char cszICClose[]       = "ICClose";
static const char cszICGetInfo[]     = "ICGetInfo";
static const char cszICLocate[]      = "ICLocate";
static const char cszMCIWndCreateW[] = "MCIWndCreateW";

PROC_INFO VFWProcs[] =
{
    cszICClose,             0,
    cszICGetInfo,           0,
    cszICLocate,            0,
    cszMCIWndCreateW,       0,

    NULL, 0
};

static const char cszAVIFileRelease[]         = "AVIFileRelease";
static const char cszAVIStreamRelease[]       = "AVIStreamRelease";
static const char cszAVIStreamSampleToTime[]  = "AVIStreamSampleToTime";
static const char cszAVIStreamStart[]         = "AVIStreamStart";
static const char cszAVIStreamLength[]        = "AVIStreamLength";
static const char cszAVIStreamReadFormat[]    = "AVIStreamReadFormat";
static const char cszAVIStreamInfoW[]         = "AVIStreamInfoW";
static const char cszAVIFileGetStream[]       = "AVIFileGetStream";
static const char cszAVIFileOpenW[]           = "AVIFileOpenW";
static const char cszAVIFileInit[]            = "AVIFileInit";
static const char cszAVIFileExit[]            = "AVIFileExit";


PROC_INFO AVIProcs[] =
{
    cszAVIFileRelease,          0,
    cszAVIStreamRelease,        0,
    cszAVIStreamSampleToTime,   0,
    cszAVIStreamStart,          0,
    cszAVIStreamLength,         0,
    cszAVIStreamReadFormat,     0,
    cszAVIStreamInfoW,          0,
    cszAVIFileGetStream,        0,
    cszAVIFileOpenW,            0,
    cszAVIFileInit,             0,
    cszAVIFileExit,             0,

    NULL, 0
};

static const char cszVerQueryValueW[]          = "VerQueryValueW";
static const char cszGetFileVersionInfoW[]     = "GetFileVersionInfoW";
static const char cszGetFileVersionInfoSizeW[] = "GetFileVersionInfoSizeW";

PROC_INFO VERSIONProcs[] =
{
    cszVerQueryValueW,          0,
    cszGetFileVersionInfoW,     0,
    cszGetFileVersionInfoSizeW, 0,

    NULL, 0
};

/*
 ***************************************************************
 ***************************************************************
 */
STATIC BOOL LoadLibraryAndProcs(LPCTSTR pLibrary, PPROC_INFO pProcInfo)
{
    HMODULE    hLibrary;
    PPROC_INFO p;
	PDYNLOAD_INFO pLib;
	BOOL	fPrevLoaded = FALSE;

#ifdef DEBUG_BUILT_LINKED
	return TRUE;
#endif

	if (pProcInfo->Address)	//Already loaded
	{
		fPrevLoaded = TRUE;
		goto UpdateDynLoadInfo;
	}	
    hLibrary = LoadLibrary(pLibrary);

    if (hLibrary == NULL)
    {
		DPF("LoadLibrary failed for %s \r\n", pLibrary);
		return FALSE;
    }

    p = pProcInfo;

    while (p->Name)
    {
        p->Address = GetProcAddress(hLibrary, p->Name);

        if (p->Address == NULL)
        {
			DPF("GetProcAddress failed for %s \r\n", p->Name);
			FreeLibrary(hLibrary);
			return FALSE;
        }

        p++;
    }

UpdateDynLoadInfo:
	pLib = DynLoadInfo;

	while (pLib->pszLib)
	{
		if (!lstrcmpi(pLib->pszLib, pLibrary))
		{
			pLib->iRefCnt++;
			if (!fPrevLoaded)
			{
				pLib->hLib = hLibrary;
			}
			break;
		}
		pLib++;
	}


    return TRUE;
}

STATIC BOOL FreeLibraryAndProcs(LPCTSTR pLibrary, PPROC_INFO pProcInfo)
{
	PDYNLOAD_INFO p;

#ifdef DEBUG_BUILT_LINKED    
	return TRUE;
#endif

	p = DynLoadInfo;

	while (p->pszLib)
	{
		if (!lstrcmpi(p->pszLib, pLibrary))
		{
		    PPROC_INFO ppi;

			p->iRefCnt--;
			if (p->iRefCnt > 0)
				return TRUE;
			if (!p->hLib)
				return FALSE;
			DPF("Freeing Library %s \r\n",p->pszLib);
			FreeLibrary(p->hLib);
			p->hLib = 0;
			
			ppi = pProcInfo;
			while (ppi->Name)
			{
				ppi->Address = 0;
				ppi++;
			}
			return TRUE;
		}
		p++;
	}
	return FALSE;
}

BOOL LoadACM()
{
	DPF("***LOADING ACM***\r\n");
	return LoadLibraryAndProcs(aszMSACM32, ACMProcs);	
}

BOOL FreeACM()
{
	DPF("***FREEING ACM***\r\n");
	return FreeLibraryAndProcs(aszMSACM32, ACMProcs);	
}


BOOL LoadAVI()
{
	DPF("***LOADING AVI***\r\n");
	return LoadLibraryAndProcs(aszAVIFIL32, AVIProcs);	
}

BOOL FreeAVI()
{
	DPF("***FREEING AVI***\r\n");
	return FreeLibraryAndProcs(aszAVIFIL32, AVIProcs);	
}

BOOL LoadVFW()
{
	DPF("***LOADING VFW***\r\n");
	return LoadLibraryAndProcs(aszMSVFW32, VFWProcs);	
}

BOOL FreeVFW()						 
{
	DPF("***FREEING VFW***\r\n");
	return FreeLibraryAndProcs(aszMSVFW32, VFWProcs);	
}

BOOL LoadVERSION()
{
	DPF("***LOADING VERSION***\r\n");
	return LoadLibraryAndProcs(aszVERSION, VERSIONProcs);	
}

BOOL FreeVERSION()
{
	DPF("***FREEING VERSION***\r\n");
	return FreeLibraryAndProcs(aszVERSION, VERSIONProcs);	
}