/* Copyright (c) 1998 Microsoft Corporation */
/*
 * @Doc DMusic
 *
 * @Module DMusic16.c - Startup code |
 *
 * 16-bit Dll for DirectMusic sequencing on legacy devices (Win95/Win98 non-WDM drivers)
 *
 * This Dll is the 16-bit thunk peer for DMusic32.Dll
 *
 * @globalv HINSTANCE | ghInst | The instance handle for the DLL.
 *
 */

#include <windows.h>
#include <windowsx.h>
#include <mmsystem.h>

#include "dmusic16.h"
#include "debug.h"

HINSTANCE ghInst;
HDRVR ghDrvr;
UINT guReferenceCount = 0;

/* @func LibMain system entry point
 *
 * @comm
 *
 * This entry point is called when the DLL is first loaded (NOT every time).
 *
 * Saves the global instance handle and initializes all the other modules.
 *
 */
int PASCAL
LibMain(
    HINSTANCE   hInst,              /* @parm Instance handle for the DLL */
    WORD        cbHeap,             /* @parm Initial size of the local heap */
    LPSTR       lpszCmdLine)        /* @parm Command-line parameters */
{
    UINT        uLev;
    char        szFilename[260];

    if (GetModuleFileName(hInst, szFilename, sizeof(szFilename)))
    {
        DPF(2, "%s", (LPSTR)szFilename);
    }

    ghDrvr = OpenDriver(szFilename, NULL, 0L);
               
    DPF(1, "DMusic16.DLL task %04X hdrvr %04X", GetCurrentTask(), (WORD)ghDrvr);


    ghInst = hInst;
    uLev = DbgInitialize(TRUE);
    DPF(0, "DMusic16: Debug level is %u", uLev);

    if (uLev > 2)
    {
        DPF(0, "DMusic16: Break in LibMain");
        DebugBreak();
    }
    
    DeviceOnLoad();
    AllocOnLoad();
    MidiOutOnLoad();

#if 0
    // This causes problems at terminate time. Find out later if we really need it.
    //
    if (!CreateTimerTask())
    {
        DPF(0, "CreateTimerTask() failed");
    }


    if (NULL == (LoadLibrary("dmusic16")))
    {
        DPF(0, "Could not LoadLibrary ourselves!");
    }
#endif

    return 1;
}

/* @func LibExit system call
 *
 * @comm
 *
 * This entry point is called just before the DLL is unloaded.
 *
 * Uninitialize all the other modules
 */

VOID PASCAL __loadds
LibExit(VOID)
{
    DPF(2, "LibExit start");
#if 0
    DestroyTimerTask();
#endif
    MidiOutOnExit();
    AllocOnExit();
    DPF(2, "LibExit end, going away now.");
}

extern BOOL FAR PASCAL dmthunk_ThunkConnect16(LPCSTR, LPCSTR, HINSTANCE, DWORD);
STATIC char pszDll16[] = "DMUSIC16.DLL";
STATIC char pszDll32[] = "DMUSIC.DLL";

/* @func DLLEntryPoint system entry point
 *
 * @comm
 *
 * This entry point is called each time the DLL is loaded or unloaded
 *
 * It is used here to initialize the peer connection for the thunk layer.
 */
#define PROCESS_DETACH          0
#define PROCESS_ATTACH          1

BOOL WINAPI
DllEntryPoint(
    DWORD       dwReason,           /* @parm Is the DLL being loaded or unloaded? */
    HINSTANCE   hi,                 /* @parm The instance handle */
    HGLOBAL     hgDS,               /* @parm The global handle of the DLL's (shared) DS */
    WORD        wHeapSize,          /* @parm The initial size of the local heap */
    LPCSTR      lszCmdLine,         /* @parm The command line (always NULL) */
    WORD        wCmdLine)           /* @parm Unused */
{
    // DllEntryPoint is called before LibEntry in a 4.x dll, so we have to LocalInit here if we're
    // going to use LocalAlloc
    //
    if (guReferenceCount == 0 && wHeapSize)
    {
        LocalInit(0, 0, wHeapSize);
    }

    switch(dwReason)
    {
        case PROCESS_ATTACH:
            DPF(2, "ProcessAttach task %04X", GetCurrentTask());
            ++guReferenceCount;
            dmthunk_ThunkConnect16(pszDll16, pszDll32, ghInst, 1);
            break;

        case PROCESS_DETACH:
            DPF(2, "ProcessDetach task %04X", GetCurrentTask());

            /* Clean up after them if they didn't close handles. We must do this here as well as
             * in DriverProc because on the last exit, we will go away before the DriverProc cleanup
             * gets called if the process termination is normal.
             */
            CloseDevicesForTask(GetCurrentTask());
            
            /* NOTE: We close on reference count of 1 since the initial OpenDriver call
               causes one more PROCESS_ATTACH to happen. */
            if (1 == --guReferenceCount)
            {
                CloseDriver(ghDrvr, 0, 0);
            }
            
            break;
    }

    return TRUE;
}

        
/* @func DriverProc entry point for ourselves as a loadable driver.
 *
 * @comm This entry points allows us to know when a task has gone away and therefore to clean
 * up after it even though we don't properly get notified that our thunk peer has gone away.
 */
LRESULT WINAPI DriverProc(
    DWORD               dwID,
    HDRVR               hdrvr,
    UINT                umsg,
    LPARAM              lParam1,
    LPARAM              lParam2)
{
    //
    //  NOTE DS is not valid here.
    //
    switch (umsg) 
    {
        case DRV_LOAD:
            return(1L);

        case DRV_FREE:
            return(0L);

        case DRV_OPEN:
        case DRV_CLOSE:
            return(1L);

        case DRV_EXITAPPLICATION:
            DPF(2, "Cleaning up handles for task %04X", GetCurrentTask());
            CloseDevicesForTask(GetCurrentTask());
            break;

        default:
            return(DefDriverProc(dwID, hdrvr, umsg, lParam1, lParam2));
    }
} //** DriverProc()