/* INIT.C
   Resident Code Segment      // Tweak: make non-resident

   Initialization and destroy routines

   InitFrost()
   CloseFrost()

   Frosting: Master Theme Selector for Windows '95
   Copyright (c) 1994-1999 Microsoft Corporation. All rights reserved.
*/

// ---------------------------------------------
// Brief file history:
// Alpha:
// Beta:
// Bug fixes
// ---------
//
// ---------------------------------------------

#include "windows.h"
#include "frost.h"
#include "global.h"
#include "schedule.h"
#include "htmlprev.h"

// Local Routines
BOOL InitInstance(HINSTANCE, HINSTANCE, int);
BOOL GetRotateTheme(LPTSTR);

// Globals
TCHAR szPlus_CurTheme[] = TEXT("Software\\Microsoft\\Plus!\\Themes\\Current");
TCHAR szPlus_CBs[] = TEXT("Software\\Microsoft\\Plus!\\Themes\\Apply");

#define bThemed (*szCurThemeFile)

// Reg paths for finding potential Theme directories
#define PLUS95_KEY        TEXT("Software\\Microsoft\\Plus!\\Setup")
#define PLUS95_PATH       TEXT("DestPath")
#define PLUS98_KEY        TEXT("Software\\Microsoft\\Plus!98")
#define PLUS98_PATH       TEXT("Path")
#define KIDS_KEY          TEXT("Software\\Microsoft\\Microsoft Kids\\Kids Plus!")
#define KIDS_PATH         TEXT("InstallDir")
#define PROGRAMFILES_KEY  TEXT("Software\\Microsoft\\Windows\\CurrentVersion")
#define PROGRAMFILES_PATH TEXT("ProgramFilesDir")

//  ProcessCmdLine
//
//  Very basic commandline: "/s themefile" to apply theme
//                          "themefile" to open app with theme selected
//                                      and curdir to be that file's dir
//
//  Return: TRUE if we should exit the app
//          FALSE to continue execution
//
BOOL ProcessCmdLine(LPTSTR lpCmdLine)
{
    extern TCHAR szFrostSection[], szMagic[], szVerify[];
    BOOL fSetTheme = FALSE;
    BOOL fRotateTheme = FALSE;
    LPTSTR lpszThemeFile;
    BOOL fStopOnQuote = FALSE;
    WIN32_FIND_DATA wfdFind;
    HANDLE hFind;
    TCHAR szRotateTheme[MAX_PATH];
    // TCHAR szThemesExe[MAX_PATH];
    DWORD dwRet;
    HKEY hKey;
    extern TCHAR szPlus_CurTheme[];

    Assert(0, TEXT("Cmd line: '"));
    Assert(0, lpCmdLine);
    Assert(0, TEXT("'\n"));

    //  Skip leading whitespace.
    while (*lpCmdLine && (*lpCmdLine != TEXT('/')))
        lpCmdLine = CharNext(lpCmdLine);

    //  Check for special switches.
    if (*lpCmdLine == TEXT('/')) {
        lpCmdLine = CharNext(lpCmdLine);
        switch (*lpCmdLine) {
            //  "Set" theme as active.
            case TEXT('s'):
            case TEXT('S'):
                fSetTheme = TRUE;
                lpCmdLine = CharNext(lpCmdLine);
                break;
            case TEXT('r'):
            case TEXT('R'):
                fRotateTheme = TRUE;
                lpCmdLine = CharNext(lpCmdLine);
                break;
            default:
                return FALSE;       // invalid flag so continue execution EXIT
        }
        while (*lpCmdLine == TEXT(' '))
            lpCmdLine = CharNext(lpCmdLine);
    }

    //  Handle quoted long file names.  Even if the name isn't quoted, we'll
    //  still parse to the end of the commandline.
    if (*lpCmdLine == TEXT('\"')) {
        fStopOnQuote = TRUE;
        lpCmdLine = CharNext(lpCmdLine);
    }

    lpszThemeFile = lpCmdLine;

    //  Parse until end of line or we find the closing quote.
    while (*lpCmdLine && !(fStopOnQuote && *lpCmdLine == TEXT('\"')))
        lpCmdLine = CharNext(lpCmdLine);

    //  Back up to the last non-whitespace character.
    while (lpCmdLine > lpszThemeFile && *(lpCmdLine-1) == TEXT(' '))
        lpCmdLine = CharPrev(lpszThemeFile, lpCmdLine);

    //  If there's anything after the closing quote, so what?
    *lpCmdLine = TEXT('\0');

    //  This is our chance to fake a theme rotation "/r" into the
    //  "/s" paradigm.  Let's get a string to the next theme in the
    //  rotation.

    if (fRotateTheme) {
        if (GetRotateTheme(szRotateTheme)) {
           fSetTheme = TRUE;
           lpszThemeFile = szRotateTheme;           
        }
        // GetRotateTheme failed.  If we fall through we're likely
        // to display the main dialog and we don't want to do that
        // in the "/r" case.  Just exit the app.
        else return TRUE;
    }

    //  After all that work, nothing was specified on the commandline.
    if (lpszThemeFile == lpCmdLine)
        return FALSE;               // no filename so continue execution EXIT

    //
    // OK: you've got a theme file. if it is for real, save it for use.
    // first check existence
    hFind = FindFirstFile(lpszThemeFile, (LPWIN32_FIND_DATA)&wfdFind);
    if (INVALID_HANDLE_VALUE == hFind)
       return (TRUE);              // file doesn't exist so continue execution EXIT
                                   // PLUS98 BUG 1293 -- this used to return FALSE
                                   // and open the Theme applet if /s had an invalid
                                   // file.  Now we return TRUE which closes Themes

    // then check for magic string
    GetPrivateProfileString((LPTSTR)szFrostSection,
                            (LPTSTR)szMagic,
                            (LPTSTR)szNULL,
                            (LPTSTR)szMsg, MAX_MSGLEN,
                            lpszThemeFile);

    // magic str check
    if (lstrcmp((LPTSTR)szMsg, (LPTSTR)szVerify)) {
       // No magic string so this must not be a real theme file.
       // If this was from a "/r" rotate we want to return TRUE and
       // bail out.  Otherwise return FALSE so we open the Theme
       // app. (this was the existing Plus 95 behavior):
       if (fRotateTheme) return (TRUE);
       else  return (FALSE);
    }

   //
   // get the confirmed theme into the necessary globals

   // save theme file using the long filename
   lstrcpy(szCurThemeFile, lpszThemeFile);      // actual pathname verified
   // save cur dir (not theme dir)
   lstrcpy(szCurDir, szCurThemeFile);
   *(FileFromPath(szCurDir)) = 0;
   // save theme name
   lstrcpy(szCurThemeName, wfdFind.cFileName);  // make sure long filename
   TruncateExt(szCurThemeName);

   // now maybe we just want to apply the theme and exit
   if (fSetTheme) {
         // Make sure there is even enough space to do theme
         // But don't complain, if there isn't
      if (! CheckSpace (NULL, FALSE))   // definition in Regutils.c
         return FALSE;

      //  Make the apply code write out everything.
      InitCheckboxes();

      // Simulate a left shift keypress to wake up the screensaver.
      // Otherwise if the SS is running and we apply a new SS you
      // can wind up with dueling savers...
      keybd_event(VK_LSHIFT, 0, 0, 0);
      keybd_event(VK_LSHIFT, 0, KEYEVENTF_KEYUP, 0);

      ApplyThemeFile(lpszThemeFile);
      // APPCOMPAT -- Plus95 Themes was not updating the Current Theme
      // setting in the registry on this codepath.  Need to fix that
      // here.

      dwRet = RegOpenKeyEx(HKEY_CURRENT_USER, szPlus_CurTheme,
                          (DWORD)0, KEY_SET_VALUE, (PHKEY)&hKey );
      if (dwRet != ERROR_SUCCESS) {
         DWORD dwDisposition;
         Assert(FALSE, TEXT("couldn't RegOpenKey save theme file\n"));
         dwRet = RegCreateKeyEx( HKEY_CURRENT_USER, szPlus_CurTheme,
                            (DWORD)0, (LPTSTR)szNULL, REG_OPTION_NON_VOLATILE,
                            KEY_SET_VALUE, (LPSECURITY_ATTRIBUTES)NULL,
                            (PHKEY)&hKey, (LPDWORD)&dwDisposition );
      }
      // if open or create worked
      if (dwRet == ERROR_SUCCESS) {
         dwRet = RegSetValueEx(hKey, (LPTSTR)NULL, // default value
                             0,
                             (DWORD)REG_SZ,
                             (LPBYTE)lpszThemeFile,
                             (DWORD)( SZSIZEINBYTES((LPTSTR)szCurThemeFile) + 1 ));
          RegCloseKey(hKey);
       }

       Assert(dwRet == ERROR_SUCCESS, TEXT("couldn't open, set or create Registry save theme file\n"));
       return TRUE;                // set the theme so now just exit app EXIT
   } // end if fSetTheme

   return FALSE;                   // continue execution
}

// InitFrost
// Since there are no window classes to register, this routine just loads
// the strings and, since there's only one instance, calls InitInstance().
//
// Returns: success of initialization

BOOL FAR InitFrost(hPrevInstance, hInstance, lpCmdLine, nCmdShow)
HINSTANCE hPrevInstance;
HINSTANCE hInstance;                   /* Current instance identifier.       */
LPTSTR lpCmdLine;
int nCmdShow;                          /* Param for first ShowWindow() call. */
{
   LPTSTR lpScan, lpTemp;
   WNDCLASS WndClass;
   BOOL bret;
   BOOL bGotThemeDir;
   HDC hdc;

   //
   // Get initial strings
   // (Gauranteed enough mem at app init for these loads)
   InitNoMem(hInstance);
   LoadString(hInstance, STR_APPNAME, szAppName, MAX_STRLEN);
   LoadString(hInstance, STR_CURSET, szCurSettings, MAX_STRLEN);
   LoadString(hInstance, STR_PREVSET, szPrevSettings, MAX_STRLEN);
   LoadString(hInstance, STR_PREVSETFILE, szPrevSettingsFilename, MAX_STRLEN);
   LoadString(hInstance, STR_OTHERTHM, szOther, MAX_STRLEN);
   LoadString(hInstance, STR_THMEXT, szExt, MAX_STRLEN);
   LoadString(hInstance, STR_PREVIEWTITLE, szPreviewTitle, MAX_STRLEN);
   LoadString(hInstance, STR_HELPFILE, szHelpFile, MAX_STRLEN);
   LoadString(hInstance, STR_HELPFILE98, szHelpFile98, MAX_STRLEN);
   LoadString(hInstApp, STR_SUGGEST, szNewFile, MAX_STRLEN);
   LoadString(hInstApp, STR_SAVETITLE, szSaveTitle, MAX_STRLEN);
   LoadString(hInstApp, STR_OPENTITLE, szOpenTitle, MAX_STRLEN);
   LoadString(hInstApp, STR_FILETYPE, szFileTypeDesc, MAX_STRLEN);

   WndClass.style = CS_DBLCLKS | CS_BYTEALIGNWINDOW | CS_GLOBALCLASS;
   WndClass.lpfnWndProc = DefDlgProc;
   WndClass.cbClsExtra = 0;
   WndClass.cbWndExtra = DLGWINDOWEXTRA;
   WndClass.hInstance = hInstApp;
   WndClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(FROST_ICON));
   WndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
   WndClass.hbrBackground = (HBRUSH) (COLOR_3DFACE + 1);
   WndClass.lpszMenuName = NULL;
   WndClass.lpszClassName = szClassName;

   if (!RegisterClass(&WndClass))
      return FALSE;

   // Initialize our g_bGradient flag
   hdc = GetDC(NULL);
   g_bGradient = (BOOL)(GetDeviceCaps(hdc, BITSPIXEL) > 8);
   ReleaseDC(NULL, hdc);

   //
   // build file search string
   lpScan = (LPTSTR)szFileTypeDesc;
   while (*lpScan) {
      if (2 == CompareString(LOCALE_USER_DEFAULT, NORM_IGNOREWIDTH,
                             (LPTSTR)TEXT("|"), 1, lpScan, 1)) {
         lpTemp = lpScan;
         lpScan = CharNext(lpScan);
         *lpTemp = 0;
      }
      else
         lpScan = CharNext(lpScan);
   }

   //
   // init directory strings

   // theme directory - for now it is subdir of EXE dir
   GetModuleFileName(hInstApp, (LPTSTR)szThemeDir, MAX_PATHLEN);
   *(FileFromPath((LPTSTR)szThemeDir)) = 0;   // leaves trailing '\'

   // Save this path momentarily -- if we can't find any good Themes
   // directory we're going to fall back to this directory.
   lstrcpy(szCurDir, szThemeDir);

   // Tack a "Themes" onto the path
   LoadString(hInstance, STR_THEMESUBDIR,     // includes trailing '\'
              (LPTSTR)(szThemeDir + lstrlen((LPTSTR)szThemeDir)), MAX_STRLEN);

   // Find out if this ThemeDir even exists.  If it doesn't we need
   // to get creative in finding our ThemeDir by searching the
   // following locales:
   //
   //   Plus!98 Install Path\Themes
   //   Plus!95 Install Path\Themes
   //   Kids for Plus! Install Path\Themes
   //   Program Files\Plus!\Themes

   bGotThemeDir = FALSE;

   Assert(0, TEXT("Themes.exe\\Themes path: "));
   Assert(0, szThemeDir);
   Assert(0, TEXT("\n"));
   if (0xFFFFFFFF != GetFileAttributes(szThemeDir)) bGotThemeDir = TRUE;

   if (!bGotThemeDir)
   {
      // Haven't found ThemeDir yet.  Try the Plus!98 location

      bret = HandGet(HKEY_LOCAL_MACHINE, PLUS98_KEY,
                     PLUS98_PATH, (LPTSTR)szThemeDir);

      if (bret)
      {
         // Tack a "Themes" onto the path
         lstrcat(szThemeDir, TEXT("\\"));
         LoadString(hInstance, STR_THEMESUBDIR,
                    (szThemeDir + lstrlen((LPTSTR)szThemeDir)), MAX_STRLEN);

         Assert(0, TEXT("Plus! 98 path: "));
         Assert(0, szThemeDir);
         Assert(0, TEXT("\n"));
         if (0xFFFFFFFF != GetFileAttributes(szThemeDir)) bGotThemeDir = TRUE;
      }
   }

   if (!bGotThemeDir)
   {
      // Still haven't found ThemeDir.  Try the Plus! 95 location

      bret = HandGet(HKEY_LOCAL_MACHINE, PLUS95_KEY,
                      PLUS95_PATH, (LPTSTR)szThemeDir);

      if (bret)
      {
         // Tack on a "Themes" onto the path
         // Plus!95 DestPath has "Plus!.dll" on the end so get rid
         // of that.
         *(FileFromPath(szThemeDir)) = 0;
         LoadString(hInstance, STR_THEMESUBDIR,
         (szThemeDir + lstrlen((LPTSTR)szThemeDir)), MAX_STRLEN);

         Assert(0, TEXT("Plus! 95 path: "));
         Assert(0, szThemeDir);
         Assert(0, TEXT("\n"));

         if (0xFFFFFFFF != GetFileAttributes(szThemeDir)) bGotThemeDir = TRUE;
      }
   }

   if (!bGotThemeDir)
   {
      // Still haven't found ThemeDir.  Try the Kids for Plus! location

      bret = HandGet(HKEY_LOCAL_MACHINE, KIDS_KEY,
                     KIDS_PATH, (LPTSTR)szThemeDir);

      if (bret)
      {
         // Tack a "\Plus! for Kids\Themes" onto the path
         lstrcat(szThemeDir, TEXT("\\Plus! for Kids\\"));
         LoadString(hInstance, STR_THEMESUBDIR,
                    (szThemeDir + lstrlen((LPTSTR)szThemeDir)), MAX_STRLEN);

         Assert(0, TEXT("Kids Plus! path: "));
         Assert(0, szThemeDir);
         Assert(0, TEXT("\n"));

         if (0xFFFFFFFF != GetFileAttributes(szThemeDir)) bGotThemeDir = TRUE;
      }
   }

   if (!bGotThemeDir)
   {
      // Still haven't found ThemeDir.  Try the Program Files\Plus! location

      bret = HandGet(HKEY_LOCAL_MACHINE, PROGRAMFILES_KEY,
                     PROGRAMFILES_PATH, (LPTSTR)szThemeDir);

      if (bret)
      {
         // Tack a "Plus!\Themes" onto the path
         lstrcat(szThemeDir, TEXT("\\Plus!\\"));
         LoadString(hInstance, STR_THEMESUBDIR,
                   (szThemeDir + lstrlen((LPTSTR)szThemeDir)), MAX_STRLEN);

         Assert(0, TEXT("Program Files path: "));
         Assert(0, szThemeDir);
         Assert(0, TEXT("\n"));

         if (0xFFFFFFFF != GetFileAttributes(szThemeDir)) bGotThemeDir = TRUE;
      }
   }

   if (!bGotThemeDir)
   {
      // After all that searching we still haven't found a good Theme
      // dir.  So we'll just use the directory where Themes.exe is
      // located -- we saved this in szCurDir before we started this
      // search process.

      lstrcpy(szThemeDir, szCurDir);

      Assert(0, TEXT("Themes.exe path: "));
      Assert(0, szThemeDir);
      Assert(0, TEXT("\n"));

   }

   // default current dir
   lstrcpy((LPTSTR)szCurDir, (LPTSTR)szThemeDir);

   // Windows directory
   GetWindowsDirectory((LPTSTR)szWinDir, MAX_PATHLEN);
   if (TEXT('\\') != szWinDir[lstrlen((LPTSTR)szWinDir)-1])
      lstrcat((LPTSTR)szWinDir, (LPTSTR)TEXT("\\"));

   //
   // see if there is a previous theme file to return to
   bret = HandGet(HKEY_CURRENT_USER, szPlus_CurTheme,
                  (LPTSTR)NULL, (LPTSTR)szMsg);
   Assert(bret, TEXT("no previous theme file in Registry!\n"));
   if (bret) {

      // get init cur dir from prev theme file
      lstrcpy(szCurDir, szMsg);
      *(FileFromPath(szCurDir)) = 0;

      // Used to actually set the cur theme file/name to prev theme.
      // Now we just use the directory if there is a prev theme.
      // No longer care if it is a real file or a theme file or not.
      #ifdef REVERT
      WIN32_FIND_DATA wfdFind;
      HANDLE hFind;
      extern TCHAR szFrostSection[], szMagic[], szVerify[];
      TCHAR szMagicTest[MAX_STRLEN+1];

      // first check if it is for real: existence
      hFind = FindFirstFile(szMsg, (LPWIN32_FIND_DATA)&wfdFind);
      if (INVALID_HANDLE_VALUE != hFind) {

         // then check if it is for real: theme file magic string
         GetPrivateProfileString((LPTSTR)szFrostSection,
                                 (LPTSTR)szMagic,
                                 (LPTSTR)szNULL,
                                 (LPTSTR)szMagicTest, MAX_STRLEN,
                                 szMsg);
         if (!lstrcmp((LPTSTR)szMagicTest, (LPTSTR)szVerify)) {

            //
            // now you're convinced that the prev theme file is legit
            //

            // then use it to set some globals
            lstrcpy(szCurThemeFile, szMsg);
            // save cur dir (not theme dir)
            lstrcpy(szCurDir, szCurThemeFile);
            *(FileFromPath(szCurDir)) = 0;
            // save theme name
            lstrcpy(szCurThemeName, FileFromPath(szCurThemeFile));
            TruncateExt(szCurThemeName);
         }
      }
      #endif   // REVERT
   }

   // then look if there is a command line that may override things
   if (ProcessCmdLine(lpCmdLine))
      return TRUE;                  // don't run app EXIT, hWndApp==NULL as flag

   // Single-instance application can call InitInstance() here
   // Return success of inst initialization
   return(InitInstance(hPrevInstance, hInstance, nCmdShow));
}


// InitInstance
// Create main window; assign globals for instance and window handles.
//
// Returns: success of initialization

BOOL InitInstance(hPrevInstance, hInstance, nCmdShow)
HINSTANCE hPrevInstance;
HINSTANCE hInstance;                   /* Current instance identifier.       */
int nCmdShow;                          /* Param for first ShowWindow() call. */

{
   RECT rWndApp, rWorkArea;
   TCHAR szThemesExe[MAX_PATH];

   // assign global instance handle
   hInstApp = hInstance;

   // create main window and assign global window handle
   bNoMem = FALSE;                     // clear out of mem flag
   hWndApp = CreateDialog( hInstApp,
                           MAKEINTRESOURCE(DLG_MAIN),
                           (HWND)NULL,
                           PreviewDlgProc
                         );
   if (!hWndApp || bNoMem)          // if problems, then bail
      return (FALSE);

   InitHTMLBM(); // Init the IThumbnail interface for HTML wallpaper support

   // have to init preview painting stuff after create dlg
   if (!PreviewInit()) {
      DestroyWindow(hWndApp);
      return (FALSE);               // low mem on dc/bmp create EXIT
   }


   // this doesn't work from WM_INITDIALOG, so added here, too
   InitCheckboxes();                // init stored values
   RestoreCheckboxes();             // init actual cbs from stored values
   if (bThemed)                     // kinda kludgy to put here but it works
      EnableWindow(GetDlgItem(hWndApp, PB_SAVE), FALSE);

   // also, can now make init Preview bmp for init paint
   BuildPreviewBitmap((LPTSTR)szCurThemeFile);

   // center main window on screen
   GetWindowRect(hWndApp, (LPRECT)&rWndApp);
   SystemParametersInfo(SPI_GETWORKAREA, 0, (PVOID)(LPRECT)&rWorkArea, 0);
   MoveWindow(hWndApp,
              ((rWorkArea.right-rWorkArea.left) - (rWndApp.right-rWndApp.left))/2,
              ((rWorkArea.bottom-rWorkArea.top) - (rWndApp.bottom-rWndApp.top))/2,
//              (GetSystemMetrics(SM_CXSCREEN) - (rWndApp.right-rWndApp.left))/2,
//              (GetSystemMetrics(SM_CYSCREEN) - (rWndApp.bottom-rWndApp.top))/2,
              rWndApp.right-rWndApp.left, rWndApp.bottom-rWndApp.top,
              FALSE);               // don't repaint

   // Make the main window visible; update its client area
   ShowWindow(hWndApp, nCmdShow);   /* Show the window */
   UpdateWindow(hWndApp);           /* Sends WM_PAINT message */

// Check to see if CB_SCHEDULE is checked and verify that Task
// Scheduler is running and the Themes task exists as appropriate

   if (bCBStates[FC_SCHEDULE]) {
      if (IsTaskSchedulerRunning()) {
         if (!IsThemesScheduled()) {
            GetModuleFileName(hInstApp, (LPTSTR)szThemesExe, MAX_PATH);
            if (!AddThemesTask(szThemesExe, TRUE /*Show errors*/)) {
               // Couldn't add task -- ATT gave error but need to clear
               // SCHEDULE CB
               bCBStates[FC_SCHEDULE] = FALSE;
               RestoreCheckboxes();
               SaveStates();  // Write checkbox states to registry
            }
         } // Themes.job exists so do nothing
      }
      else {
         // Zonk -- Task Scheduler isn't running.  Start it.
         if (StartTaskScheduler(TRUE /*prompt user*/)) {
            // Task scheduler started -- check if themes task already
            // exists
            if (!IsThemesScheduled()) {
               GetModuleFileName(hInstApp, (LPTSTR)szThemesExe, MAX_PATH);
               if (!AddThemesTask(szThemesExe, TRUE /*Show errors*/)) {
                  // Couldn't add task -- ATT gave error but need to clear
                  // SCHEDULE CB
                  bCBStates[FC_SCHEDULE] = FALSE;
                  RestoreCheckboxes();
                  SaveStates();  // Write checkbox states to registry
               }
            } //TS is running now and Themes.job exists
         }
         else {
            // Couldn't start TS -- STS() gave error.  Need to clear
            // SCHEDULE CB and HandDelete any existing Themes.job.
            // CheckDlgButton(hdlg, CB_SCHEDULE, BST_UNCHECKED);
            bCBStates[FC_SCHEDULE] = FALSE;
            RestoreCheckboxes();
            SaveStates();  // Write checkbox states to the registry
            HandDeleteThemesTask();
         }
      } // END ELSE Task Scheduler isn't running
   }
   else { // bCBStates[FC_CHECKBOX] != TRUE
      // SCHEDULE Checkbox is clear -- should delete Themes task.
      if (IsTaskSchedulerRunning()) DeleteThemesTask();
      else HandDeleteThemesTask();
   }

   // cleanup
   return (TRUE);                   // success
}


// CloseFrost
//
// Cleanup your objects, thunks, etc.
//

void FAR CloseFrost()
{
   // just extra tidy housekeeping
   hWndApp = NULL;
   hInstApp = NULL;

   PreviewDestroy();
}


// SaveStates
//
// Remember persistent states: currently checkboxes.
// Last applied theme is remembered at Apply time.
//
void FAR SaveStates()
{
   LONG lret;
   HKEY hKey;
   int iter;

   //
   // checkboxes
   //
   lret = RegOpenKeyEx(HKEY_CURRENT_USER, szPlus_CBs,
                       (DWORD)0, KEY_SET_VALUE, (PHKEY)&hKey );
   if (lret != ERROR_SUCCESS) {
      DWORD dwDisposition;
      Assert(FALSE, TEXT("couldn't RegOpenKey save checkboxes\n"));
      lret = RegCreateKeyEx( HKEY_CURRENT_USER, szPlus_CBs,
                             (DWORD)0, (LPTSTR)szNULL, REG_OPTION_NON_VOLATILE,
                             KEY_SET_VALUE, (LPSECURITY_ATTRIBUTES)NULL,
                             (PHKEY)&hKey, (LPDWORD)&dwDisposition );
   }
   // if open or create worked
   if (lret == ERROR_SUCCESS) {

      for (iter = 0; iter < MAX_FCHECKS; iter ++) {
         if (bThemed)
            RegSetValueEx(hKey, (LPTSTR)szCBNames[iter],
                        0,
                        (DWORD)REG_SZ,
                        (LPBYTE)(IsDlgButtonChecked(hWndApp, iCBIDs[iter]) ?
                                 TEXT("1") : TEXT("0")),
                        (DWORD)2*sizeof(TCHAR));
         else  // cur win settings --> real states saved in bCBStates[]
            RegSetValueEx(hKey, (LPTSTR)szCBNames[iter],
                        0,
                        (DWORD)REG_SZ,
                        (LPBYTE)(bCBStates[iter] ? TEXT("1") : TEXT("0")),
                        (DWORD)2*sizeof(TCHAR));
      }

      // close when finished
      RegCloseKey(hKey);
   }
   else {
      Assert(FALSE, TEXT("couldn't open or create Registry save checkboxes\n"));
   }
}

BOOL GetRotateTheme(LPTSTR lpszRotateTheme)
{
   BOOL bRet = FALSE;
   TCHAR szThemesdir[MAX_PATH];
   HANDLE hFF;
   WIN32_FIND_DATA FindData;

   // WARNING -- If "next" .Theme file is not valid theme file we'll never rotate!

   // See if there is a current theme.
   szThemesdir[0] = TEXT('\0');
   bRet = HandGet(HKEY_CURRENT_USER, szPlus_CurTheme,
                  (LPTSTR)NULL, (LPTSTR)szThemesdir);

   // Do we have a current Theme?
   hFF = INVALID_HANDLE_VALUE;
   if (szThemesdir[0]) {

      // Yes we do, so do a FindFirst on it and make sure we've
      // got the long filename.
      ZeroMemory(&FindData, sizeof(WIN32_FIND_DATA));
      hFF = FindFirstFile(szThemesdir, &FindData);
   }

   // Did we find the file on FindFirst?  If so, proceed with finding
   // the next Theme in that directory else assume that the registry's
   // Current Theme setting is bogus and look in the default dir.

   if (INVALID_HANDLE_VALUE != hFF) {
      // Copy long filename.
      lstrcpy(lpszRotateTheme, FindData.cFileName);
      FindClose(hFF);

      // We've got the longfilename to the current Theme
      // Now do a FF/FN on *.Theme in the Theme dir until
      // we find the current theme.

      // Strip the theme file off the path.
      *(FileFromPath(szThemesdir)) = 0;
      lstrcat(szThemesdir, TEXT("*\0"));
      LoadString(NULL, STR_THMEXT, (szThemesdir + lstrlen(szThemesdir)),
                                        MAX_PATH);

      ZeroMemory(&FindData, sizeof(WIN32_FIND_DATA));
      hFF = FindFirstFile(szThemesdir, &FindData);

      if (INVALID_HANDLE_VALUE == hFF) {
         // We shouldn't hit this since we just found our current
         // theme in this directory.  If we do, just bail out.
         lpszRotateTheme[0] = TEXT('\0');
         return FALSE;
      }

      // Step through the .Theme files in this dir until we find our
      // current theme file.
      bRet = TRUE;
      while (lstrcmp(FindData.cFileName, lpszRotateTheme) && bRet) {
            bRet = FindNextFile(hFF, &FindData);
      }

      if (lstrcmp(FindData.cFileName, lpszRotateTheme)) {
         // We ran out of files before finding the current theme!?!?
         // Not likely...  Bail out.
         FindClose(hFF);
         lpszRotateTheme[0] = TEXT('\0');
         return FALSE;
      }

      // Assume the FindNext file is our current theme.  Let's
      // get the Next .Theme file -- that's what we're rotating to...

      if (FindNextFile(hFF, &FindData)) {
         *(FileFromPath(szThemesdir)) = 0;
         lstrcat(szThemesdir, FindData.cFileName);
         lstrcpy(lpszRotateTheme, szThemesdir);
         FindClose(hFF);
         return TRUE;
      }

      // OK, we failed to find another Theme file in the current dir
      // so assume our current Theme is the last in the list.  Go
      // back to the beginning and use the first Theme file we find
      // as our "rotate to" theme.

      else {
         FindClose(hFF);
         ZeroMemory(&FindData, sizeof(WIN32_FIND_DATA));
         hFF = FindFirstFile(szThemesdir, &FindData);

         if (INVALID_HANDLE_VALUE == hFF) {
            // This shouldn't fail since we just found our current
            // theme in this dir.  If it does, bail out.
            lpszRotateTheme[0] = TEXT('\0');
            return FALSE;
         }

         // This is our new/rotate file
         *(FileFromPath(szThemesdir)) = 0;
         lstrcat(szThemesdir, FindData.cFileName);
         lstrcpy (lpszRotateTheme, szThemesdir);
         FindClose(hFF);
         return TRUE;
      }
   }

   // No current theme applied or the Current Theme setting in the
   // registry is bogus so use the global szThemeDir setting and
   // look for a theme in that directory.
   else {
      lstrcpy(szThemesdir, szThemeDir);
      lstrcat(szThemesdir, TEXT("*\0"));
      LoadString(NULL, STR_THMEXT, (szThemesdir + lstrlen(szThemesdir)),
                                        MAX_PATH);

      ZeroMemory(&FindData, sizeof(WIN32_FIND_DATA));
      hFF = FindFirstFile(szThemesdir, &FindData);

      if (INVALID_HANDLE_VALUE == hFF) {
         // Apparently there are no themes available in the default
         // Themes directory.  We're hosed -- no rotation today.
         lpszRotateTheme[0] = TEXT('\0');
         return FALSE;
       }

       // This is our new/rotate file
       *(FileFromPath(szThemesdir)) = 0;
       lstrcat(szThemesdir, FindData.cFileName);
       lstrcpy (lpszRotateTheme, szThemesdir);
       FindClose(hFF);
       return TRUE;
   }

   return FALSE;
}