extern "C" {
#include <afs/param.h>
#include <afs/stds.h>
}

#include <windows.h>
#include <winerror.h>
#include <WINNT/TaLocale.h>
#include <WINNT/settings.h>


/*
 * ROUTINES ___________________________________________________________________
 *
 */

BOOL RestoreSettings (HKEY hkParent,
                      LPCTSTR pszBase,
                      LPCTSTR pszValue,
                      PVOID pStructure,
                      size_t cbStructure,
                      WORD wVerExpected)
{
   BOOL rc = FALSE;

   size_t cbStored;
   if ((cbStored = GetRegValueSize (hkParent, pszBase, pszValue)) != 0)
      {
      if (cbStored >= sizeof(WORD)+cbStructure)
         {
         PVOID pStructureFinal;
         if ((pStructureFinal = (PVOID)Allocate (cbStored)) != NULL)
            {
            if (GetBinaryRegValue (hkParent, pszBase, pszValue, pStructureFinal, cbStored))
               {
               WORD wVerStored = *(LPWORD)pStructureFinal;

               if ( (HIBYTE(wVerStored) == HIBYTE(wVerExpected)) &&
                    (LOBYTE(wVerStored) >= LOBYTE(wVerExpected)) )
                  {
                  memcpy (pStructure, &((LPBYTE)pStructureFinal)[ sizeof(WORD) ], cbStructure);
                  rc = TRUE;
                  }
               }

            Free (pStructureFinal);
            }
         }
      }

   return rc;
}


BOOL StoreSettings (HKEY hkParent,
                    LPCTSTR pszBase,
                    LPCTSTR pszValue,
                    PVOID pStructure,
                    size_t cbStructure,
                    WORD wVersion)
{
   BOOL rc = FALSE;

   PVOID pStructureFinal;
   if ((pStructureFinal = (PVOID)Allocate (sizeof(WORD) + cbStructure)) != NULL)
      {
      *(LPWORD)pStructureFinal = wVersion;
      memcpy (&((LPBYTE)pStructureFinal)[ sizeof(WORD) ], pStructure, cbStructure);

      rc = SetBinaryRegValue (hkParent, pszBase, pszValue, pStructureFinal, sizeof(WORD) + cbStructure);

      Free (pStructureFinal);
      }

   return rc;
}


void EraseSettings (HKEY hkParent, LPCTSTR pszBase, LPCTSTR pszValue)
{
   HKEY hk;
   if (RegOpenKey (hkParent, pszBase, &hk) == 0)
      {
      RegDeleteValue (hk, pszValue);
      RegCloseKey (hk);
      }
}


BOOL GetBinaryRegValue (HKEY hk,
                        LPCTSTR pszBase,
                        LPCTSTR pszValue,
                        PVOID pData,
                        size_t cbData)
{
   BOOL rc = FALSE;

   HKEY hkFinal;
   if (RegOpenKey (hk, pszBase, &hkFinal) == ERROR_SUCCESS)
      {
      DWORD dwType;
      DWORD dwSize = (DWORD)cbData;

      if (RegQueryValueEx (hkFinal, pszValue, NULL, &dwType, (LPBYTE)pData, &dwSize) == ERROR_SUCCESS)
         rc = TRUE;

      RegCloseKey (hk);
      }

   return rc;
}


size_t GetRegValueSize (HKEY hk,
                        LPCTSTR pszBase,
                        LPCTSTR pszValue)
{
   size_t cb = 0;

   HKEY hkFinal;
   if (RegOpenKey (hk, pszBase, &hkFinal) == ERROR_SUCCESS)
      {
      DWORD dwType;
      DWORD dwSize = 0;

      if (RegQueryValueEx (hkFinal, pszValue, NULL, &dwType, NULL, &dwSize) == ERROR_SUCCESS)
         {
         cb = (size_t)dwSize;
         }

      RegCloseKey (hk);
      }

   return cb;
}


BOOL SetBinaryRegValue (HKEY hk,
                        LPCTSTR pszBase,
                        LPCTSTR pszValue,
                        PVOID pData,
                        size_t cbData)
{
   BOOL rc = FALSE;

   HKEY hkFinal;
   if (RegCreateKey (hk, pszBase, &hkFinal) == ERROR_SUCCESS)
      {
      DWORD dwSize = (DWORD)cbData;

      if (RegSetValueEx (hkFinal, pszValue, NULL, REG_BINARY, (LPBYTE)pData, dwSize) == ERROR_SUCCESS)
         rc = TRUE;

      RegCloseKey (hk);
      }

   return rc;
}


BOOL GetMultiStringRegValue (HKEY hk,
                             LPCTSTR pszBase,
                             LPCTSTR pszValue,
                             LPTSTR *pmszData)
{
   BOOL rc = FALSE;

   HKEY hkFinal;
   if (RegOpenKey (hk, pszBase, &hkFinal) == ERROR_SUCCESS)
      {
      DWORD dwSize = 0;
      DWORD dwType = 0;
      RegQueryValueEx (hkFinal, pszValue, NULL, &dwType, NULL, &dwSize);

      if ((dwType == REG_MULTI_SZ) && (dwSize != 0))
         {
         *pmszData = AllocateString (dwSize);

         if (RegQueryValueEx (hkFinal, pszValue, NULL, &dwType, (LPBYTE)*pmszData, &dwSize) == ERROR_SUCCESS)
            rc = TRUE;
         else
            {
            FreeString (*pmszData);
            *pmszData = NULL;
            }
         }

      RegCloseKey (hk);
      }

   return rc;
}


BOOL SetMultiStringRegValue (HKEY hk,
                             LPCTSTR pszBase,
                             LPCTSTR pszValue,
                             LPCTSTR mszData)
{
   BOOL rc = FALSE;

   HKEY hkFinal;
   if (RegCreateKey (hk, pszBase, &hkFinal) == ERROR_SUCCESS)
      {
      DWORD dwSize = sizeof(TCHAR);
      for (LPCTSTR psz = mszData; psz && *psz; psz += 1+lstrlen(psz))
         dwSize += sizeof(TCHAR) * (1+lstrlen(psz));

      if (RegSetValueEx (hkFinal, pszValue, NULL, REG_MULTI_SZ, (LPBYTE)mszData, dwSize) == ERROR_SUCCESS)
         rc = TRUE;

      RegCloseKey (hk);
      }

   return rc;
}


// Under Windows NT, RegDeleteKey() is not recursive--under Windows 95,
// it is.  Sigh.  This routine works recursively on either OS.
//
BOOL RegDeltreeKey (HKEY hk, LPTSTR pszKey)
{
   HKEY hkSub;
   if (RegOpenKey (hk, pszKey, &hkSub) == 0)
      {
      TCHAR szFound[ MAX_PATH ];
      while (RegEnumKey (hkSub, 0, szFound, MAX_PATH) == 0)
         {
         if (!RegDeltreeKey (hkSub, szFound))
            {
            RegCloseKey (hkSub);
            return FALSE;
            }
         }

      RegCloseKey (hkSub);
      }

   if (RegDeleteKey (hk, pszKey) != 0)
      return FALSE;

   return TRUE;
}

