/*
File:		main.cpp
Project:	Universal Joystick Control Panel OLE Client
Author:	Brycej
Date:		02/28/95
Comments:

Copyright (c) 1995, Microsoft Corporation
*/

//#pragma pack (8)

#include <afxcmn.h>
#include <cpl.h>

#include "cpanel.h"
#include "resource.h"
#include "creditst.h" // for the Credit Dialog!

#define JOYSTICK_CPL	0
#define MAX_CPL_PAGES 6

HINSTANCE ghInstance;

extern WCHAR *pwszTypeArray[MAX_DEVICES];
extern WCHAR *pwszGameportDriverArray[MAX_GLOBAL_PORT_DRIVERS];
extern WCHAR *pwszGameportBus[MAX_BUSSES];  // List of enumerated gameport buses 

extern BYTE nGamingDevices;  // Gaming Devices Enumeration Counter
extern BYTE nGameportDriver; // Global Port Driver Enumeration Counter
extern BYTE nGameportBus;    // Gameport Bus Enumeration Counter
extern short nFlags;              // State Flags the CPL defined in CPANEL.H

static void AddPage(LPPROPSHEETHEADER ppsh, short nTemplateID, int nTabID, DLGPROC pfn);

static void DoProperties(HWND hWnd, UINT nStartPage);
void WINAPI ShowJoyCPL(HWND);
void ParseArgs(HWND hDlg, LPTSTR lpArgList);
BOOL WINAPI SplashDialogProc(HWND hDlg, ULONG uMsg, WPARAM wParam, LPARAM lParam);

// From AppMan.cpp
//extern HRESULT AppManInit();

// From Retrocfg.cpp
extern HRESULT  DVoiceCPLInit();
extern INT_PTR CALLBACK  RetrofitProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);
extern INT_PTR RetrofitDestroyHandler(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam);


BYTE nID, nStartPageDef, nStartPageCPL;


BOOL WINAPI DllMain(HANDLE  hModule, ULONG  uReason, LPVOID pv)
{
    switch( uReason )
    {
    case DLL_PROCESS_ATTACH:
        ghInstance = (HINSTANCE)hModule;

        // needed because CEdit will Assert without this line!!!
        afxCurrentInstanceHandle = ghInstance;
        afxCurrentResourceHandle = ghInstance;
        break;

//	case DLL_PROCESS_DETACH:
//		ClearArrays();

    case DLL_THREAD_ATTACH:
        DisableThreadLibraryCalls((HMODULE)hModule);
    case DLL_THREAD_DETACH:
        break;
    }
    return(TRUE);
}

/*
function:	CPlApplet()
comments:
    Entry point for control panel applets.
*/
LONG WINAPI CPlApplet(HWND  hWnd, UINT  uMsg, LPARAM  lParam1, LPARAM   lParam2)
{
    switch( uMsg )
    {
    case CPL_INIT:
        return(1);

    case CPL_GETCOUNT:
        return(1);

    case CPL_INQUIRE:
        ((LPCPLINFO)lParam2)->idIcon = IDI_CPANEL; 
        ((LPCPLINFO)lParam2)->idName = IDS_GEN_CPANEL_TITLE; 
        ((LPCPLINFO)lParam2)->idInfo = IDS_GEN_CPANEL_INFO; 
        ((LPCPLINFO)lParam2)->lData  = 0;
        return(1);

    case CPL_DBLCLK:
        nID = (NUMJOYDEVS<<1);

        // Applet icon double clicked -- invoke property sheet with
        // The first property sheet page on top.
        DoProperties(hWnd, 0);
        break;

        /*
         * This function requires Windows 2000. Not available on Win9x.
         */
    case CPL_STARTWPARMS:
        // Same as CPL_DBLCLK, but lParam2 is a long pointer to
        // a string of extra directions that are to be supplied to
        // the property sheet that is to be initiated.
        // The arguments are as follows:
        // @nCPLDialog Index, nStartPageCPL, nStartPageDef

        // Don't do anything if there are no arguments!
        if( *(LPTSTR)lParam2 )
            ParseArgs(hWnd, (LPTSTR)lParam2);
        return(TRUE);  // return non-zero to indicate message handled

    case CPL_EXIT:
    case CPL_STOP:
        break;
    }

    return(0);
}

/*
   Function: DoProperties(HWND hWnd, UINT nStartPage)
   Arguements: hWnd       - Handle to Main Window
               nStartPage - Page number to start
*/
static void DoProperties(HWND hWnd, UINT nStartPage)
{
    static HWND hPrevHwnd;
    static HANDLE hMutex = CreateMutex(NULL, TRUE, MUTEX_NAME);

    if( GetLastError() == ERROR_ALREADY_EXISTS )
    {
        SetForegroundWindow(hPrevHwnd); 
    } else
    {
        hPrevHwnd = hWnd;

        nFlags = (GetVersion() < 0x80000000) ? ON_NT : 0;

        HPROPSHEETPAGE  *pPages = new (HPROPSHEETPAGE[MAX_CPL_PAGES]);
        ASSERT (pPages);

        LPPROPSHEETHEADER ppsh = new (PROPSHEETHEADER);
        ASSERT(ppsh);

        ZeroMemory(ppsh, sizeof(PROPSHEETHEADER));

        ppsh->dwSize     = sizeof(PROPSHEETHEADER);
        ppsh->dwFlags    = PSH_NOAPPLYNOW | PSH_USEICONID;
        ppsh->hwndParent = hWnd;
        ppsh->hInstance  = ghInstance;
        ppsh->pszCaption = MAKEINTRESOURCE(IDS_GEN_CPANEL_TITLE);
        ppsh->pszIcon     = MAKEINTRESOURCE(IDI_CPANEL);
        ppsh->nStartPage = nStartPage;
        ppsh->phpage     = pPages;

        AddPage(ppsh, IDD_CPANEL,   IDS_GENERAL_TAB,  (DLGPROC)CPanelProc);
        AddPage(ppsh, IDD_ADVANCED, IDS_ADVANCED_TAB, (DLGPROC)AdvancedProc);

		if( SUCCEEDED ( DVoiceCPLInit() )) {
		  AddPage(ppsh, IDD_PROP_RETROFIT, IDS_DVOICE_TAB, (DLGPROC) RetrofitProc);
		}

//		if( SUCCEEDED ( AppManInit() ) ) {
//			AddPage(ppsh, IDD_APPMAN,   IDS_APPMAN_TAB, (DLGPROC)AppManProc);
//			AddPage(ppsh, IDD_APPMAN_LOCKING, IDS_APPMANLOCK_TAB, (DLGPROC) AppManLockProc);
//		}

#ifdef SYMANTIC_MAPPER
        AddPage(ppsh, IDD_SMAPPER, ID_SMAPPER_TAB, (DLGPROC)SMapperProc);
#endif // SYMANTIC_MAPPER

        // trap for return...
        VERIFY(PropertySheet(ppsh) != -1);

        if( pPages )
            delete[] (pPages);

        if( ppsh )
            delete (ppsh);

        ReleaseMutex(hMutex);
        CloseHandle(hMutex);

        // Ensure voice is always cleaned up
        RetrofitDestroyHandler( NULL, 0, 0, 0 );

        ClearArrays();
    }
}

static void AddPage(LPPROPSHEETHEADER ppsh, short nTemplateID, int nTabID, DLGPROC pfn)
{
    if( ppsh->nPages < MAX_CPL_PAGES )
    {
        LPPROPSHEETPAGE ppsp = new (PROPSHEETPAGE);
        ASSERT(ppsp);

        ZeroMemory(ppsp, sizeof(PROPSHEETPAGE));

        ppsp->dwSize      = sizeof(PROPSHEETPAGE);
        ppsp->pszTitle    = MAKEINTRESOURCE(nTabID);
        ppsp->hInstance   = ghInstance;
        ppsp->pfnDlgProc  = pfn;
        ppsp->pszTemplate = MAKEINTRESOURCE(nTemplateID);

        ppsh->phpage[ppsh->nPages] = CreatePropertySheetPage(ppsp);

        if( ppsp )
            delete (ppsp);

        if( ppsh->phpage[ppsh->nPages] )
            ppsh->nPages++;
    }
}  // AddPage


// DO NOT REMOVE THIS!!!
// This is here because the games group loads the CPL from the exported function
// If you remove this Hellbender, Monster Truck Maddness, CART, etc will fail to
// load the Joystick CPL!!!
// DO NOT REMOVE THIS!!!
void WINAPI ShowJoyCPL(HWND hWnd)
{
    nID = (NUMJOYDEVS<<1);

    DoProperties(hWnd, 0);
}

void LaunchExtention(HWND hWnd)
{
    // These are defined in CPANEL.CPP
    extern LPDIRECTINPUT lpDIInterface;
    extern IDirectInputJoyConfig* pDIJoyConfig;

    HRESULT hr =  DirectInputCreate(ghInstance, DIRECTINPUT_VERSION, &lpDIInterface, NULL);

    if( FAILED(hr) ) return;

    // Call CreateJoyConfigInterface!
    if( SUCCEEDED(lpDIInterface->QueryInterface(IID_IDirectInputJoyConfig, (LPVOID*)&pDIJoyConfig)) )
    {
        // Create a pJoy and populate it's GUID from GetTypeInfo
        PJOY pJoy = new (JOY);
        ASSERT (pJoy);

        pJoy->ID = nID;

        DIJOYCONFIG_DX5 JoyConfig;

        JoyConfig.dwSize = sizeof (DIJOYCONFIG_DX5);

        if( SUCCEEDED(pDIJoyConfig->GetConfig(nID, (LPDIJOYCONFIG)&JoyConfig, DIJC_REGHWCONFIGTYPE)) )
        {
            LPDIJOYTYPEINFO_DX5 pdiJoyTypeInfo = new (DIJOYTYPEINFO_DX5);
            ASSERT (pdiJoyTypeInfo);

            pdiJoyTypeInfo->dwSize = sizeof (DIJOYTYPEINFO_DX5);

            hr = pDIJoyConfig->GetTypeInfo(JoyConfig.wszType, (LPDIJOYTYPEINFO)pdiJoyTypeInfo, DITC_CLSIDCONFIG);

            if( !IsEqualIID(pdiJoyTypeInfo->clsidConfig, GUID_NULL) )
                pJoy->clsidPropSheet = pdiJoyTypeInfo->clsidConfig;

            if( pdiJoyTypeInfo )
                delete (pdiJoyTypeInfo);

        }

        if( SUCCEEDED(hr) )
            Launch(hWnd, pJoy, nStartPageDef);

        if( pJoy )
            delete (pJoy);

        // release the DI JoyConfig interface pointer
        if( pDIJoyConfig )
        {
            pDIJoyConfig->Release();
            pDIJoyConfig = 0;
        }
    }

    // release the DI Device interface pointer
    if( lpDIInterface )
    {
        lpDIInterface->Release();
        lpDIInterface = 0;
    }
}

void ParseArgs(HWND hDlg, LPTSTR lpArgList)
{
    BOOL bShowCPL = TRUE;

    // Check for '-', as they may not want to show the CPL!
    if( *lpArgList == '-' )
    {
        bShowCPL = FALSE;
        *lpArgList++;
    }

    nStartPageCPL = nStartPageCPL = nStartPageDef = 0;

    // parse command line for nStartPageCPL!
    while( *lpArgList && (*lpArgList != ',') )
    {
        nStartPageCPL *= 10;
        nStartPageCPL += *lpArgList++ - '0';
    }

    // check to make sure nStartPageCPL is within range!
    if( bShowCPL ) {
        if( nStartPageCPL > MAX_CPL_PAGES )
        {
#ifdef _DEBUG
            OutputDebugString(TEXT("JOY.CPL: Command line requested an invalid start page, reset to default!\n"));
#endif      
            // NUMJOYDEVS is used to send the user to the Add dialog on start-up!
            if( nStartPageCPL != NUMJOYDEVS )
                nStartPageCPL = 0;
        }
    }

        // Only continue if you have something further to parse!
    if( *lpArgList == ',' )
    {
        *lpArgList++;

        nID = 0;

        // Parse for ID's
        while( *lpArgList && (*lpArgList != ',') )
        {
            nID *= 10;
            nID += *lpArgList++ - '0';
        }

        // Check for error cases!
        if( (nID < 1) || (nID > NUMJOYDEVS) )
        {
#ifdef _DEBUG
            OutputDebugString(TEXT("JOY.CPL: Command line Device ID out of range!\n"));
#endif
            nID = (NUMJOYDEVS<<1);

            return;
        }

        // Decrement to internal zero based ID
        nID--;

        // Don't parse what you don't have!
        if( *lpArgList == ',' )
        {
            *lpArgList++;

            // Lastly, parse for nStartPageDef!
            while( *lpArgList && (*lpArgList != ',') )
            {
                nStartPageDef *= 10;
                nStartPageDef += *lpArgList++ - '0';
            }
        }
    } else {
        nID = (NUMJOYDEVS<<1);
    }

    // Done with the parsing...
    // Time to get to work!

    // if we're not showing the CPL...
    if( !bShowCPL )
    {
        // check to make sure the next value is a 1
        // we may want to have further negative arguments :)
        switch( nStartPageCPL )
        {
        case 1:
            // Invalid ID...
            if( nID > NUMJOYDEVS ) return;
            LaunchExtention(hDlg);
            break;

#ifdef WE_CAN_HAVE_CREDITS
        case 60:
            // If they ask for the splash, they don't get the CPL!
            DialogBox( ghInstance, (PTSTR)IDD_SPLASH, hDlg, (DLGPROC)SplashDialogProc );
            break;
#endif
            
        }

    } else {
        DoProperties(NULL, nStartPageCPL); 
    }
}


#ifdef WE_CAN_HAVE_CREDITS
BOOL WINAPI SplashDialogProc(HWND hDlg, ULONG uMsg, WPARAM wParam, LPARAM lParam)
{
    static CCreditStatic *pStatic;

    switch( uMsg )
    {
    case WM_INITDIALOG:
        {
            pStatic = new (CCreditStatic);
            ASSERT (pStatic);

            CWnd *pCWnd = new (CWnd);
            ASSERT (pCWnd);

            pCWnd->Attach(hDlg);

            pStatic->SubclassDlgItem(IDC_MYSTATIC, pCWnd);

            if( pCWnd )
            {
                pCWnd->Detach();
                delete (pCWnd);
                pCWnd = 0;
            }

            LPTSTR lpStr = new (TCHAR[MAX_STR_LEN]);
            ASSERT (lpStr);

            // The Credits come in two lines!
            BYTE nStrLen = (BYTE)LoadString(ghInstance, IDS_SPLASH, lpStr, MAX_STR_LEN);
            LoadString(ghInstance, IDS_SPLASH1, &lpStr[nStrLen], MAX_STR_LEN-nStrLen);

            pStatic->SetCredits(lpStr);

            if( lpStr )
            {
                delete[] (lpStr);
                lpStr = 0;
            }

            pStatic->StartScrolling();
        }
        return(TRUE);  // return TRUE unless you set the focus to a control
        // EXCEPTION: OCX Property Pages should return FALSE}

    case WM_COMMAND:
        switch( LOWORD(wParam) )
        {
        case IDOK:
            EndDialog(hDlg, LOWORD(wParam));
            break;
        }
        break;

//		case WM_TIMER:
        //SendDlgItemMessage(hDlg, IDC_STATIC, WM_TIMER, 0, 0);
//			pStatic->OnTimer(150); //DISPLAY_TIMER_ID
//			break;

    case WM_DESTROY:
        if( pStatic )
        {
            delete (pStatic);
            pStatic = 0;
        }
        break;
    }     

    return(0);
}
#endif