/*******************************************************************************
*
*  (C) COPYRIGHT MICROSOFT CORP., 1996
*
*  TITLE:       HIBERNAT.C
*
*  VERSION:     2.0
*
*  AUTHOR:      ReedB
*
*  DATE:        17 Oct, 1996
*
*  DESCRIPTION:
*   Support for hibernate page of PowerCfg.Cpl.
*
*******************************************************************************/

#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>

#include <windows.h>
#include <commctrl.h>
#include <help.h>
#include <powercfp.h>

#include "powercfg.h"
#include "pwrresid.h"
#include "PwrMn_cs.h"

// Private functions implemented in HIBERNAT.C

VOID SetNumberMB(LPTSTR, DWORD);
VOID InsertSeparators(LPTSTR);
UINT UpdateFreeSpace(HWND, UINT);
UINT UpdatePhysMem(void);

/*******************************************************************************
*
*                     G L O B A L    D A T A
*
*******************************************************************************/

// This structure is filled in by the Power Policy Manager at CPL_INIT time.
extern SYSTEM_POWER_CAPABILITIES g_SysPwrCapabilities;
extern BOOL g_bRunningUnderNT;

// A systary change requires PowerSchemeDlgProc re-init.
extern BOOL g_bSystrayChange;

// Persistant storage of this data is managed by POWRPROF.DLL API's.
extern GLOBAL_POWER_POLICY  g_gpp;

// Power button power action string ID's. With and without hibernate.
UINT g_uiPwrActIDs[] =
{
    IDS_NONE,       PowerActionNone,
    IDS_PROMPT,     PowerActionNone,
    IDS_STANDBY,    PowerActionSleep,
    IDS_HIBERNATE,  PowerActionHibernate,
    IDS_POWEROFF,   PowerActionShutdownOff,
    0,              0
};

// Lid action string ID's. With and without hibernate.
UINT g_uiLidActIDs[] =
{
    IDS_NONE,       PowerActionNone,
    IDS_STANDBY,    PowerActionSleep,
    IDS_HIBERNATE,  PowerActionHibernate,
    IDS_POWEROFF,   PowerActionShutdownOff,
    0,              0
};

// UI state variables
TCHAR   g_szRequiredSpace[128];
DWORD   g_dwShowHibernate;
DWORD   g_dwShowNoDiskSpace;
DWORD   g_dwShowDiskSpace;
DWORD   g_dwTrueFlag = (DWORD) TRUE;
BOOLEAN g_bHibernate;

// Globals for DoHibernateApply:
BOOL    g_bHibernateDirty;
HWND    g_hwndHibernateDlg;
UINT    g_uiRequiredMB;

// Hibernate policies dialog controls descriptions:

#define NUM_HIBERNATE_POL_CONTROLS 7

// Handy indicies into our g_pcHibernatePol array:
#define ID_REQUIREDSPACE    0
#define ID_NOTENOUGHSPACE   1
#define ID_HIBERNATE        2

POWER_CONTROLS g_pcHibernatePol[NUM_HIBERNATE_POL_CONTROLS] =
{// Control ID              Control Type    Data Address        Data Size               Parameter Pointer    Enable/Visible State Pointer
    IDC_REQUIREDSPACE,      EDIT_TEXT_RO,   &g_szRequiredSpace, 0,                      NULL,                &g_dwShowDiskSpace,
    IDC_NOTENOUGHSPACE,     STATIC_TEXT,    NULL,               0,                      NULL,                &g_dwShowNoDiskSpace,
    IDC_HIBERNATE,          CHECK_BOX,      &g_bHibernate,      sizeof(g_bHibernate),   &g_dwTrueFlag,       &g_dwShowHibernate,
    IDC_DISKSPACEGROUPBOX,  STATIC_TEXT,    NULL,               0,                      NULL,                &g_dwShowDiskSpace,
    IDC_FREESPACETEXT,      STATIC_TEXT,    NULL,               0,                      NULL,                &g_dwShowDiskSpace,
    IDC_REQUIREDSPACETEXT,  STATIC_TEXT,    NULL,               0,                      NULL,                &g_dwShowDiskSpace,
    IDC_FREESPACE,          STATIC_TEXT,    NULL,               0,                      NULL,                &g_dwShowDiskSpace,
};

// "Hibernate" Dialog Box (IDD_HIBERNATE == 105) help array:

const DWORD g_HibernateHelpIDs[]=
{
    IDC_HIBERNATE,          IDH_105_1400,   // Hibernate: "After going on standby, &hibernate." (Button)
    IDC_FREESPACE,          IDH_105_1401,   // Hibernate: "Free space" (Static)
    IDC_REQUIREDSPACE,      IDH_105_1402,   // Hibernate: "Required space to hibernate" (Static)
    IDC_NOTENOUGHSPACE,     IDH_105_1403,   // Hibernate: "You must free up some disk space before your computer can hibernate." (Static)
    IDC_DISKSPACEGROUPBOX,  IDH_105_1402,
    IDC_FREESPACETEXT,      IDH_105_1401,
    IDC_REQUIREDSPACETEXT,  IDH_105_1402,
    IDC_HIBERNATEGROUPBOX,  IDH_105_1400,
    IDI_HIBERNATE,          NO_HELP,
    IDC_NO_HELP_6,          NO_HELP,
    IDI_PWRMNG,             NO_HELP,
    0, 0
};

/*******************************************************************************
*
*               P U B L I C   E N T R Y   P O I N T S
*
*******************************************************************************/

/*******************************************************************************
*
*  MapPwrAct
*
*  DESCRIPTION:
*   Map power action to one of a lesser number of UI supported actions.
*   Depends on state of hibernate so implemented here.
*
*  PARAMETERS:
*
*******************************************************************************/

BOOL MapPwrAct(
    PPOWER_ACTION   ppa,
    BOOL            bNone
)
{
    switch (*ppa) {
        case PowerActionNone:
            if (bNone) {
                *ppa = PowerActionNone;
                break;
            }
        case PowerActionReserved:
        case PowerActionSleep:
            *ppa = PowerActionSleep;
            break;

        case PowerActionHibernate:
            if (g_SysPwrCapabilities.HiberFilePresent) {
                *ppa = PowerActionHibernate;
            }
            else {
                *ppa = PowerActionSleep;
            }
            break;

        case PowerActionShutdown:
        case PowerActionShutdownReset:
        case PowerActionShutdownOff:
            *ppa = PowerActionShutdownOff;
            break;

        default:
            DebugPrint( "MapPwrAct, unknown power action: %X", *ppa);
            *ppa = PowerActionShutdownOff;
            return FALSE;
    }
    return TRUE;
}

/*******************************************************************************
*
*  DoHibernateApply
*
*  DESCRIPTION:
*   Handle the WM_NOTIFY, PSN_APPLY message for HibernateDlgProc. Updates
*   global hibernate state.
*
*  PARAMETERS:
*
*******************************************************************************/

void DoHibernateApply(void)
{
    NTSTATUS    status;

    // Only handle if hibernate page is dirty.
    if (g_bHibernateDirty) {
        // Fetch data from dialog controls.
        GetControls(g_hwndHibernateDlg,
                    NUM_HIBERNATE_POL_CONTROLS,
                    g_pcHibernatePol);

        status = CallNtPowerInformation(SystemReserveHiberFile,
                                        &g_bHibernate,
                                        sizeof(g_bHibernate),
                                        NULL,
                                        0);
        if (status != STATUS_SUCCESS) {
            ErrorMsgBox(g_hwndHibernateDlg,
#ifdef WINNT
                        RtlNtStatusToDosError(status),
#else
                        NO_ERROR,
#endif
                        IDS_UNABLETOSETHIBER);
        }

        // Get the current hibernate state from the PPM.
        if (GetPwrCapabilities(&g_SysPwrCapabilities)) {
            g_bHibernate = g_SysPwrCapabilities.HiberFilePresent;

            // Map power actions to allowed UI values.
            MapPwrAct(&g_gpp.user.LidCloseDc.Action, TRUE);
            MapPwrAct(&g_gpp.user.PowerButtonDc.Action, FALSE);
            MapPwrAct(&g_gpp.user.SleepButtonDc.Action, FALSE);
        }
        SetControls(g_hwndHibernateDlg, 1, &g_pcHibernatePol[ID_HIBERNATE]);
        UpdateFreeSpace(g_hwndHibernateDlg, g_uiRequiredMB);
        g_bHibernateDirty = FALSE;
    }
}

/*******************************************************************************
*
*  HibernateDlgProc
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

INT_PTR CALLBACK HibernateDlgProc(
    HWND hWnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
)
{
   NMHDR FAR   *lpnm;
   LPTSTR      pszUPS;

   switch (uMsg) {
      case WM_INITDIALOG:

         // Save the hibernate dialog hwnd for use by DoHibernateApply.
         g_hwndHibernateDlg = hWnd;

         // Get the current hibernate state from the PPM.
         if (GetPwrCapabilities(&g_SysPwrCapabilities)) {
            g_bHibernate = g_SysPwrCapabilities.HiberFilePresent;
         }

         // Get the disk free and required space only under NT.
         if (g_bRunningUnderNT) {
            g_dwShowDiskSpace = CONTROL_ENABLE;

            // Get the required space from the power capabilities.
            g_uiRequiredMB = UpdatePhysMem();

            // Update the disk free space and enable/disable
            // disk space warning and hibernate time out.
            UpdateFreeSpace(hWnd, g_uiRequiredMB);

         } else {
            g_dwShowHibernate = CONTROL_ENABLE;
            g_dwShowDiskSpace = CONTROL_HIDE;
            g_dwShowNoDiskSpace = CONTROL_HIDE;
         }

         SetControls(hWnd, NUM_HIBERNATE_POL_CONTROLS, g_pcHibernatePol);

         //
         // Disable the checkbox is the user doesn't have permission to
         // change it. We do this by trying to set the same value we
         // retrieved earlier.
         //
         {
             NTSTATUS status;
             status = CallNtPowerInformation(SystemReserveHiberFile,
                                    &g_bHibernate,
                                    sizeof(g_bHibernate),
                                    NULL,
                                    0);
             if ( ERROR_SUCCESS != status )
             {
                 EnableWindow( GetDlgItem( hWnd, IDC_HIBERNATE ), FALSE );
             }
         }

         return TRUE;

      case WM_ACTIVATE:
         // If user switches away, check the disk space when they come back.
         if (g_bRunningUnderNT) {
            GetControls(hWnd, NUM_HIBERNATE_POL_CONTROLS, g_pcHibernatePol);
            UpdateFreeSpace(hWnd, g_uiRequiredMB);
            SetControls(hWnd, NUM_HIBERNATE_POL_CONTROLS-1, g_pcHibernatePol);
         }
         break;

      case WM_NOTIFY:
         lpnm = (NMHDR FAR *)lParam;
         switch (lpnm->code) {
            case PSN_APPLY:
               DoHibernateApply();
               break;
         }
         break;

      case WM_COMMAND:
         switch (LOWORD(wParam)) {
            case IDC_HIBERNATE:
               MarkSheetDirty(hWnd, &g_bHibernateDirty);
               break;
         }
         break;

      case PCWM_NOTIFYPOWER:
         // Notification from systray, user has changed a PM UI setting.
         g_bSystrayChange = TRUE;
         break;

      case WM_HELP:             // F1
         WinHelp(((LPHELPINFO)lParam)->hItemHandle, PWRMANHLP, HELP_WM_HELP, (ULONG_PTR)(LPTSTR)g_HibernateHelpIDs);
         return TRUE;

      case WM_CONTEXTMENU:      // right mouse click
         WinHelp((HWND)wParam, PWRMANHLP, HELP_CONTEXTMENU, (ULONG_PTR)(LPTSTR)g_HibernateHelpIDs);
         return TRUE;
   }
   return FALSE;
}


/*******************************************************************************
*
*                 P R I V A T E   F U N C T I O N S
*
*******************************************************************************/

/*******************************************************************************
*
*  SetNumberMB
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

VOID SetNumberMB(LPTSTR psz, DWORD dwValue)
{
    LPTSTR      pszNumber;
    TCHAR       szBuf[128];
    TCHAR       szBufLow[64];

    wsprintf(szBuf, TEXT("%u"), dwValue);
    InsertSeparators(szBuf);
    pszNumber = LoadDynamicString(IDS_MBYTES, szBuf);
    if (pszNumber) {
        lstrcpy(psz, pszNumber);
        LocalFree(pszNumber);
    }
}


/*******************************************************************************
*
*  InsertSeparators
*
*  DESCRIPTION:
*   Passed string must be large enough to hold seperators.
*
*  PARAMETERS:
*
*******************************************************************************/

VOID InsertSeparators(LPTSTR pszNumber)
{
    TCHAR szSeparator[10];
    TCHAR Separator;
    TCHAR szBuf[128];
    ULONG cchNumber;
    UINT  Triples;
    LPTSTR  pch;

    if (GetLocaleInfo(GetUserDefaultLCID(),
                      LOCALE_STHOUSAND,
                      szSeparator,
                      sizeof(szSeparator)/sizeof(TCHAR))) {
        Separator = szSeparator[0];
        cchNumber = lstrlen(pszNumber);
        Triples = 0;
        szBuf[127] = '\0';
        pch = &szBuf[126];
        while (cchNumber > 0) {
            *pch-- = pszNumber[--cchNumber];
            ++Triples;
            if ((0 == (Triples % 3)) && (cchNumber > 0)) {
                *pch-- = Separator;
            }
        }
        lstrcpy(pszNumber, pch + 1);
    }
}

/*******************************************************************************
*
*  UpdateFreeSpace
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

UINT UpdateFreeSpace(HWND hWnd, UINT uiRequiredMB)
{
   DWORD      dwSectorsPerCluster, dwBytesPerSector;
   DWORD      dwFreeClusters, dwTotalClusters;
   ULONGLONG  ullFreeBytes = 0;
   UINT       uiFreeMB = 0;
   TCHAR      szTmp[MAX_PATH];

   // Get the free space on the system drive.
   if (GetSystemDirectory(szTmp, sizeof(szTmp)/sizeof(TCHAR))) {
      szTmp[3] = '\0';
      if (GetDiskFreeSpace(szTmp,
                           &dwSectorsPerCluster,
                           &dwBytesPerSector,
                           &dwFreeClusters,
                           &dwTotalClusters)) {
         ullFreeBytes =  dwBytesPerSector * dwSectorsPerCluster;
         ullFreeBytes *= dwFreeClusters;
         uiFreeMB = (UINT) (ullFreeBytes /= 0x100000);
         SetNumberMB(szTmp, uiFreeMB);
         SetDlgItemText(hWnd, IDC_FREESPACE, szTmp);

         // Logic to enable/disable disk space warning and hibernate time out.
         if ((uiFreeMB >= uiRequiredMB) || g_bHibernate) {
            g_dwShowHibernate   = CONTROL_ENABLE;
            g_dwShowNoDiskSpace = CONTROL_HIDE;
         } else {
            if (g_bHibernate) {
               g_dwShowHibernate   = CONTROL_ENABLE;
            }
            else {
               g_dwShowHibernate   = CONTROL_DISABLE;
            }
            g_dwShowNoDiskSpace = CONTROL_ENABLE;
         }

      }
   }
   return uiFreeMB;
}

/*******************************************************************************
*
*  UpdatePhysMem
*
*  DESCRIPTION:
*
*  PARAMETERS:
*
*******************************************************************************/

UINT UpdatePhysMem(void)
{
   UINT           uiPhysMemMB;

#ifdef WINNT
   MEMORYSTATUSEX msex;

   msex.dwLength = sizeof(msex);

   GlobalMemoryStatusEx(&msex);
   uiPhysMemMB = (UINT) (msex.ullTotalPhys / 0x100000);

   if (msex.ullTotalPhys % 0x100000) {
      uiPhysMemMB++;
   }
#else
   MEMORYSTATUS ms;

   ms.dwLength = sizeof(ms);

   GlobalMemoryStatus(&ms);
   uiPhysMemMB = (UINT) (ms.dwTotalPhys / 0x100000);

   if (ms.dwTotalPhys % 0x100000) {
      uiPhysMemMB++;
   }
#endif

   SetNumberMB(g_szRequiredSpace, uiPhysMemMB);
   return uiPhysMemMB;
}

