/*
 * boot - Dialog box property sheet for "boot-time parameters"
 */

#include "tweakui.h"

#ifdef _X86_

#pragma BEGIN_CONST_DATA

#ifdef BOOTMENUDEFAULT
ConstString(c_tszNetwork,	"Network");
ConstString(c_tszBootMenuDefault, "BootMenuDefault");
#endif

typedef BYTE MSIOT;		/* msdos.sys ini option type */
#define msiotUint		0
#define msiotBool		1
#define msiotCombo		2

typedef const struct _MSIO {	/* msdos.sys ini option */
    const TCH CODESEG *ptszName;
    WORD id;			/* Dialog id */
    MSIOT msiot;		/* Data type */
    BYTE uiDefault;		/* The default value */
} MSIO;
typedef MSIO CODESEG *PMSIO;

MSIO CODESEG rgmsio[] = {
    { c_tszBootKeys,	 IDC_BOOTKEYS,		msiotBool,  1 },
    { c_tszBootDelay,	 IDC_BOOTDELAY, 	msiotUint,  2 },
    { c_tszBootGUI,	 IDC_BOOTGUI,		msiotBool,  1 },
    { c_tszBootMenu,	 IDC_BOOTMENU,		msiotBool,  0 },
    { c_tszBootMenuDelay,IDC_BOOTMENUDELAY,	msiotUint, 30 },
    { c_tszLogo,	 IDC_LOGO,		msiotBool,  1 },
    { c_tszBootMulti,	 IDC_BOOTMULTI, 	msiotBool,  0 },
    { c_tszAutoScan,	 IDC_SCANDISK,		msiotCombo, 1 },
};

#define pmsioMax (&rgmsio[cA(rgmsio)])

const static DWORD CODESEG rgdwHelp[] = {
	IDC_BOOTGROUP1,		IDH_GROUP,
	IDC_BOOTKEYS,		IDH_BOOTKEYS,
	IDC_BOOTDELAYTEXT,	IDH_BOOTKEYS,
	IDC_BOOTDELAY,		IDH_BOOTKEYS,
	IDC_BOOTDELAYUD,	IDH_BOOTKEYS,
	IDC_BOOTDELAYTEXT2,	IDH_BOOTKEYS,
	IDC_BOOTGUI,		IDH_BOOTGUI,
	IDC_LOGO,		IDH_LOGO,
	IDC_BOOTMULTI,		IDH_BOOTMULTI,

	IDC_BOOTMENUGROUP,	IDH_GROUP,
	IDC_BOOTMENU,		IDH_BOOTMENU,
	IDC_BOOTMENUDELAYTEXT,	IDH_BOOTMENUDELAY,
	IDC_BOOTMENUDELAY,	IDH_BOOTMENUDELAY,
	IDC_BOOTMENUDELAYUD,	IDH_BOOTMENUDELAY,
	IDC_BOOTMENUDELAYTEXT2,	IDH_BOOTMENUDELAY,
	IDC_SCANDISKTEXT,	IDH_AUTOSCAN,
	IDC_SCANDISK,		IDH_AUTOSCAN,
	IDC_RESET,		IDH_RESET,
	0,			0,
};

#pragma END_CONST_DATA

/*****************************************************************************
 *
 *  Boot_fLogo
 *
 *	Nonzer if this machine should have the logo enabled by default.
 *
 *	The answer is yes, unless the display driver is xga.drv,
 *	because XGA cards aren't really VGA compatible, and the logo
 *	code uses VGA mode X.
 *
 *****************************************************************************/

BOOL PASCAL
Boot_fLogo(void)
{
    TCH tsz[12];

    return !(GetPrivateProfileString(c_tszBoot, c_tszDisplayDrv, c_tszNil,
				     tsz, cA(tsz), c_tszSysIni) &&
		lstrcmpi(tsz, c_tszXgaDrv) == 0);
}

/*****************************************************************************
 *
 *  Boot_GetOption
 *
 *****************************************************************************/

int PASCAL
Boot_GetOption(LPCTSTR ptszName, UINT uiDefault)
{
    return GetPrivateProfileInt(c_tszOptions, ptszName,
			        uiDefault, g_tszMsdosSys);
}

#ifdef BOOTMENUDEFAULT
/*****************************************************************************
 *
 *  Boot_GetDefaultBootMenuDefault
 *
 *	Get the default boot menu default.  This is 3 if no network,
 *	or 4 if network.
 *
 *****************************************************************************/

UINT PASCAL
Boot_GetDefaultBootMenuDefault(void)
{
    return 3 + Boot_GetOption(c_tszNetwork, 0);
}

/*****************************************************************************
 *
 *  Boot_GetBootMenuDefault
 *
 *	Get the boot menu default.
 *
 *****************************************************************************/

int PASCAL
Boot_GetBootMenuDefault(void)
{
    return Boot_GetOption(c_tszBootMenuDefault,
			  Boot_GetDefaultBootMenuDefault());
}
#endif

/*****************************************************************************
 *
 *  Boot_FindMsdosSys
 *
 *	Search the hard drives for the file X:\MSDOS.SYS.
 *
 *	There is no "Get boot drive" function in Win32, so this is the best
 *	I can do.
 *
 *	(MS-DOS function 3305h returns the boot drive.)
 *
 *****************************************************************************/

void PASCAL
Boot_FindMsdosSys(void)
{
    TCH tsz[2];			/* scratch */
    char szRoot[4];		/* Root directory thing */
    (*(LPDWORD)szRoot) = 0x005C3A40; /* @:\ */

    for (szRoot[0] = 'A'; szRoot[0] <= 'Z'; szRoot[0]++) {
	if (GetDriveTypeA(szRoot) == DRIVE_FIXED) {
	    DWORD fl;
	    if (GetVolumeInformation(szRoot, 0, 0, 0, 0, &fl, 0, 0) &&
		!(fl & FS_VOL_IS_COMPRESSED)) {
		g_tszMsdosSys[0] = (TCH)szRoot[0];
		if (GetPrivateProfileString(c_tszPaths, c_tszWinDir, 0,
					    tsz, cA(tsz), g_tszMsdosSys)) {
		    return;
		}
	    }
	}
    }
    g_tszMsdosSys[0] = TEXT('\0');
}

/*****************************************************************************
 *
 *  Boot_WriteOptionUint
 *
 *	Write an option unsigned integer to the msdos.sys file.
 *
 *****************************************************************************/

void PASCAL
Boot_WriteOptionUint(LPCTSTR ptszName, UINT ui, UINT uiDefault)
{
    if (GetPrivateProfileInt(c_tszOptions, ptszName,
			     uiDefault, g_tszMsdosSys) != ui) {
	TCH tsz[32];
	wsprintf(tsz, c_tszPercentU, ui);
	WritePrivateProfileString(c_tszOptions, ptszName, tsz, g_tszMsdosSys);
OutputDebugString(ptszName);
OutputDebugString(tsz);
    }
}

/*****************************************************************************
 *
 *  Boot_SetOptionBool
 *
 *	Propagate an option boolean to the msdos.sys file.
 *
 *****************************************************************************/

void PASCAL
Boot_SetOptionBool(HWND hdlg, PMSIO pmsio)
{
    Boot_WriteOptionUint(pmsio->ptszName, IsDlgButtonChecked(hdlg, pmsio->id),
			 pmsio->uiDefault);
}

/*****************************************************************************
 *
 *  Boot_SetOptionUint
 *
 *	Propagate an option unsigned integer to the msdos.sys file.
 *
 *****************************************************************************/

void PASCAL
Boot_SetOptionUint(HWND hdlg, PMSIO pmsio)
{
    UINT ui;
    BOOL f;

    ui = (int)GetDlgItemInt(hdlg, pmsio->id, &f, 0);
    if (f) {
        Boot_WriteOptionUint(pmsio->ptszName, ui, pmsio->uiDefault);
    }

}

/*****************************************************************************
 *
 *  Boot_SetOptionCombo
 *
 *	Propagate a combo option to the msdos.sys file.
 *
 *	Not propagating the value that is already there means that we don't
 *	set AutoScan if not running OPK2.
 *
 *****************************************************************************/

void PASCAL
Boot_SetOptionCombo(HWND hdlg, PMSIO pmsio)
{
    Boot_WriteOptionUint(pmsio->ptszName,
			 SendDlgItemMessage(hdlg, pmsio->id,
					    CB_GETCURSEL, 0, 0),
			 pmsio->uiDefault);
}


/*****************************************************************************
 *
 *  Boot_FlushIniCache
 *
 *  Make sure all changes are committed to disk.
 *
 *****************************************************************************/

INLINE void
Boot_FlushIniCache(void)
{
    WritePrivateProfileString(0, 0, 0, g_tszMsdosSys);
}

/*****************************************************************************
 *
 *  Boot_Apply
 *
 *	Write the changes to the msdos.sys file.
 *
 *****************************************************************************/

BOOL PASCAL
Boot_Apply(HWND hdlg)
{
    DWORD dwAttr = GetFileAttributes(g_tszMsdosSys);
    if (dwAttr != 0xFFFFFFFF &&
	SetFileAttributes(g_tszMsdosSys, FILE_ATTRIBUTE_NORMAL)) {
	PMSIO pmsio;
	for (pmsio = rgmsio; pmsio < pmsioMax; pmsio++) {
            HWND hwnd = GetDlgItem(hdlg, pmsio->id);
            if (hwnd) {
                switch (pmsio->msiot) {
                case msiotUint:
                    Boot_SetOptionUint(hdlg, pmsio);
                    break;

                case msiotBool:
                    Boot_SetOptionBool(hdlg, pmsio);
                    break;

                case msiotCombo:
                    Boot_SetOptionCombo(hdlg, pmsio);
                    break;
                }
            }
	}
#ifdef BOOTMENUDEFAULT
	Boot_WriteOptionUint(c_tszBootMenuDefault,
	    (UINT)SendDlgItemMessage(hdlg, IDC_BOOTMENUDEFAULT, CB_GETCURSEL,
				     0, 0L) + 1);
#endif
	Boot_FlushIniCache();
	SetFileAttributes(g_tszMsdosSys, dwAttr);
    } else {
	MessageBoxId(hdlg, IDS_ERRMSDOSSYS, g_tszName, MB_OK);
    }
    return 1;
}

/*****************************************************************************
 *
 *  Boot_OnBootKeysChange
 *
 *	When IDC_BOOTKEYS changes, enable or disable the boot delay,
 *	BootMulti, and BootMenu.
 *
 *      NOTE:  BootKeys is meaningless if BootMenu is on -- if you
 *      have the BootMenu enabled, then the menu just comes up even without
 *      pressing a key.  Fortunately, nobody yet has complained about the
 *      interaction so I'm not gonna try to expose it in the UI.
 *
 *****************************************************************************/

void PASCAL
Boot_OnBootKeysChange(HWND hdlg)
{
    BOOL f = IsDlgButtonChecked(hdlg, IDC_BOOTKEYS);
    HWND hwnd;

    hwnd = GetDlgItem(hdlg, IDC_BOOTDELAY);
    if (hwnd) {
        EnableWindow(hwnd, f);
    }
    EnableDlgItem(hdlg, IDC_BOOTMULTI, f);
    EnableDlgItem(hdlg, IDC_BOOTMENU, f);
    EnableDlgItem(hdlg, IDC_BOOTMENUDELAY, f);
}

/*****************************************************************************
 *
 *  Boot_SetDlgOption
 *
 *	Set the value of a dialog box item.
 *
 *****************************************************************************/

void PASCAL
Boot_SetDlgOption(HWND hdlg, PMSIO pmsio, UINT ui)
{
    HWND hwnd = GetDlgItem(hdlg, pmsio->id);
    if (hwnd) {
        switch (pmsio->msiot) {
        case msiotUint:
            SetDlgItemInt(hdlg, pmsio->id, ui, 0);
            break;

        case msiotBool:
            CheckDlgButton(hdlg, pmsio->id, ui);
            break;

        case msiotCombo:
            ComboBox_SetCurSel(hwnd, ui);
        }
    }
}

/*****************************************************************************
 *
 *  Boot_FactoryReset
 *
 *	Restore to original factory settings.
 *
 *	The weird one is IDC_BOOTLOGO, which
 *	varies depending on the system configuration.
 *
 *****************************************************************************/

BOOL PASCAL
Boot_FactoryReset(HWND hdlg)
{
    PMSIO pmsio;
    for (pmsio = rgmsio; pmsio < pmsioMax; pmsio++) {
	Boot_SetDlgOption(hdlg, pmsio, pmsio->uiDefault);
    }

    CheckDlgButton(hdlg, IDC_LOGO, Boot_fLogo());

#ifdef BOOTMENUDEFAULT
    SendDlgItemMessage(hdlg, IDC_BOOTMENUDEFAULT, CB_SETCURSEL,
		       (WPARAM)Boot_GetDefaultBootMenuDefault() - 1, 0L);
#endif

    Boot_OnBootKeysChange(hdlg);

    Common_SetDirty(hdlg);
    return 1;
}

/*****************************************************************************
 *
 *  Boot_OnCommand
 *
 *	Ooh, we got a command.
 *
 *****************************************************************************/

BOOL PASCAL
Boot_OnCommand(HWND hdlg, int id, UINT codeNotify)
{
    switch (id) {
    case IDC_RESET:	/* Reset to factory default */
	if (codeNotify == BN_CLICKED) return Boot_FactoryReset(hdlg);
	break;

    case IDC_BOOTKEYS:
	if (codeNotify == BN_CLICKED) Boot_OnBootKeysChange(hdlg);
	/* FALLTHROUGH */

    case IDC_BOOTGUI:
    case IDC_BOOTMENU:
    case IDC_LOGO:
    case IDC_BOOTMULTI:
	if (codeNotify == BN_CLICKED) Common_SetDirty(hdlg);
	break;

    case IDC_BOOTDELAY:
    case IDC_BOOTMENUDELAY:
	if (codeNotify == EN_CHANGE) Common_SetDirty(hdlg);
	break;

#ifdef BOOTMENUDEFAULT
    case IDC_BOOTMENUDEFAULT:
	if (codeNotify == CBN_SELCHANGE) Common_SetDirty(hdlg);
	break;
#endif

    case IDC_SCANDISK:
	if (codeNotify == CBN_SELCHANGE) Common_SetDirty(hdlg);
	break;
    }

    return 0;
}

/*****************************************************************************
 *
 *  Boot_OnNotify
 *
 *	Ooh, we got a notification.
 *
 *****************************************************************************/

BOOL PASCAL
Boot_OnNotify(HWND hdlg, NMHDR FAR *pnm)
{
    switch (pnm->code) {
    case PSN_APPLY:
	Boot_Apply(hdlg);
	break;
    }
    return 0;
}

/*****************************************************************************
 *
 *  Boot_InitDlgInt
 *
 *	Initialize a paired edit control / updown control.
 *
 *	hdlg is the dialog box itself.
 *
 *	idc is the edit control identifier.  It is assumed that idc+didcUd is
 *	the identifier for the updown control.
 *
 *	iMin and iMax are the limits of the control.
 *
 *****************************************************************************/

void PASCAL
Boot_InitDlgInt(HWND hdlg, UINT idc, int iMin, int iMax)
{
    HWND hwnd = GetDlgItem(hdlg, idc + didcEdit);
    if (hwnd) {
        Edit_LimitText(hwnd, 2);
        SendDlgItemMessage(hdlg, idc+didcUd,
                           UDM_SETRANGE, 0, MAKELPARAM(iMax, iMin));
    }
}

/*****************************************************************************
 *
 *  Boot_OnInitDialog
 *
 *	Initialize the controls.
 *
 *****************************************************************************/

BOOL NEAR PASCAL
Boot_OnInitDialog(HWND hdlg)
{
    PMSIO pmsio;
    HWND hwnd;
    UINT dids;
    TCH tsz[96];

    /*
     *	Init the Scandisk gizmo.  We need to do this even if not OPK2,
     *	so that we don't confuse the Apply.  But show it only if OPK2.
     */
    hwnd = GetDlgItem(hdlg, IDC_SCANDISK);
    for (dids = 0; dids < 3; dids++) {
	LoadString(hinstCur, IDS_SCANDISKFIRST + dids, tsz, cA(tsz));
	ComboBox_AddString(hwnd, tsz);
    }
    if (g_fOPK2) {
	ShowWindow(hwnd, 1);
	ShowWindow(GetDlgItem(hdlg, IDC_SCANDISKTEXT), 1);
    }

    for (pmsio = rgmsio; pmsio < pmsioMax; pmsio++) {
	Boot_SetDlgOption(hdlg, pmsio,
			  Boot_GetOption(pmsio->ptszName, pmsio->uiDefault));
    }

#ifdef BOOTMENUDEFAULT
    hwnd = GetDlgItem(hdlg, IDC_BOOTMENUDEFAULT);
    for (ids = IDS_BOOTMENU; ids <= IDS_BOOTMENULAST; ids++) {
	if (ids != IDS_BOOTMENUSAFENET || Boot_GetOption(c_tszNetwork, 0)) {
	    LoadString(hinstCur, ids, tsz, cA(tsz));
	    ComboBox_AddString(hwnd, tsz);
	}
    }
    ComboBox_SetCurSel(hwnd, Boot_GetBootMenuDefault() - 1);
#endif

    Boot_InitDlgInt(hdlg, IDC_BOOTDELAY, 0, 99);
    Boot_InitDlgInt(hdlg, IDC_BOOTMENUDELAY, 0, 99);
    Boot_OnBootKeysChange(hdlg);

    if (g_fMemphis) {
        DestroyWindow(GetDlgItem(hdlg, IDC_BOOTDELAYTEXT));
        DestroyWindow(GetDlgItem(hdlg, IDC_BOOTDELAY));
        DestroyWindow(GetDlgItem(hdlg, IDC_BOOTDELAYUD));
        DestroyWindow(GetDlgItem(hdlg, IDC_BOOTDELAYTEXT2));
    }

    return 1;
}

/*****************************************************************************
 *
 *  Our window procedure.
 *
 *****************************************************************************/

/*
 * The HANDLE_WM_* macros weren't designed to be used from a dialog
 * proc, so we need to handle the messages manually.  (But carefully.)
 */

INT_PTR EXPORT
Boot_DlgProc(HWND hdlg, UINT wm, WPARAM wParam, LPARAM lParam)
{
    switch (wm) {
    case WM_INITDIALOG: return Boot_OnInitDialog(hdlg);

    case WM_COMMAND:
	return Boot_OnCommand(hdlg,
			       (int)GET_WM_COMMAND_ID(wParam, lParam),
			       (UINT)GET_WM_COMMAND_CMD(wParam, lParam));
    case WM_NOTIFY:
	return Boot_OnNotify(hdlg, (NMHDR FAR *)lParam);

    case WM_HELP: Common_OnHelp(lParam, &rgdwHelp[0]); break;

    case WM_CONTEXTMENU: Common_OnContextMenu(wParam, &rgdwHelp[0]); break;

    default: return 0;	/* Unhandled */
    }
    return 1;		/* Handled */
}
#endif      // _X86_ only
