/*****************************************************************************
* Copyright (c) 1990-98 Digi International Inc., All Rights Reserved
* 
* This software contains proprietary and confidential information of Digi
* International Inc.  By accepting transfer of this copy, Recipient agrees
* to retain this software in confidence, to prevent disclosure to others,
* and to make no use of this software other than that for which it was
* delivered.  This is an unpublished copyrighted work of Digi International
* Inc.  Except as permitted by federal law, 17 USC 117, copying is strictly
* prohibited.
* 
* Restricted Rights Legend
*
* Use, duplication, or disclosure by the Government is subject to
* restrictions set forth in sub-paragraph (c)(1)(ii) of The Rights in
* Technical Data and Computer Software clause at DFARS 252.227-7031 or
* subparagraphs (c)(1) and (2) of the Commercial Computer Software -
* Restricted Rights at 48 CFR 52.227-19, as applicable.
*
* Digi International Inc. 11001 Bren Road East, Minnetonka, MN 55343
*
*****************************************************************************/


// SerialCommNameHelpers.h:
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "SerialCommNameHelpers.h"


#define WINNT_REGKEY_HARDWARE_DEVICEMAP_SERIALCOMM TEXT("HARDWARE\\DEVICEMAP\\SERIALCOMM")


struct SSerialCommNameInfo
{
   SSerialCommNameInfo() { strRegValueName = TEXT(""); eState = NORMAL; }

   CString strRegValueName;

   enum
   {
      NORMAL,
      DIRTY,
      DELETED,
   };

   int eState;
};

typedef CMap<CString, TCHAR const * const, SSerialCommNameInfo, SSerialCommNameInfo&> CMapSerialCommNameCache;

static BOOL g_fSerialCommCacheInited = FALSE;
static CMapSerialCommNameCache g_mapSerialCommNameCache;


BOOL InitSerialCommNameCache()
{
   BOOL fSuccess = FALSE;

   try
   {
      if (!g_fSerialCommCacheInited)
      {
         AffirmThrow(RefreshSerialCommNameCache());
         g_fSerialCommCacheInited = TRUE;
      }

      fSuccess = TRUE;
   }
   catch(CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL RefreshSerialCommNameCache()
{
   BOOL fSuccess = FALSE;

   try
   {
      HKEY hSerialComm = NULL;
      AffirmThrow(RegCreateKey(HKEY_LOCAL_MACHINE, WINNT_REGKEY_HARDWARE_DEVICEMAP_SERIALCOMM, &hSerialComm, REG_OPTION_VOLATILE));    // use create because this key may not exist
      AffirmThrow(hSerialComm != NULL);

      try
      {
         CMapRegStrValues mapSerialComm;
         AffirmThrow(RegEnumStrValues(hSerialComm, mapSerialComm));

         g_mapSerialCommNameCache.RemoveAll();

         POSITION pos = mapSerialComm.GetStartPosition();
         CString strValueName;
         CString strName;

         while (pos != NULL)
         {
            mapSerialComm.GetNextAssoc(pos, strValueName, strName);
            g_mapSerialCommNameCache[strName].strRegValueName = strValueName;
            g_mapSerialCommNameCache[strName].eState = SSerialCommNameInfo::NORMAL;
         }
      }
      catch (CAffirmException ae)
      {
      }

      RegCloseKey(hSerialComm);

      fSuccess = TRUE;
   }
   catch(CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FlushSerialCommNameCache()
{
   BOOL fSuccess = FALSE;

   try
   {
      HKEY hSerialComm = NULL;
      AffirmThrow(RegCreateKey(HKEY_LOCAL_MACHINE, WINNT_REGKEY_HARDWARE_DEVICEMAP_SERIALCOMM, &hSerialComm, REG_OPTION_VOLATILE));    // use create because this key may not exist
      AffirmThrow(hSerialComm != NULL);

      try
      {
         POSITION pos = g_mapSerialCommNameCache.GetStartPosition();
         CString strName;
         SSerialCommNameInfo scni;

         while (pos != NULL)
         {
            g_mapSerialCommNameCache.GetNextAssoc(pos, strName, scni);

            switch (scni.eState)
            {
               case SSerialCommNameInfo::NORMAL: break;

               case SSerialCommNameInfo::DIRTY:
               {
                  TRACE("Writing serial comm value %s = %s\n", scni.strRegValueName, strName);
#if defined(_DEBUG)
                  if (RegValueExists(hSerialComm, scni.strRegValueName)) TRACE("Overwriting serial comm value %s with %s\n", scni.strRegValueName, strName);
#endif
                  AffirmThrow(RegWriteStrValue(hSerialComm, scni.strRegValueName, strName));
                  break;
               }

               case SSerialCommNameInfo::DELETED:
               {
                  TRACE("Deleting serial comm value %s = %s\n", scni.strRegValueName, strName);
                  RegDelValue(hSerialComm, scni.strRegValueName);
                  break;
               }

               default: Affirm(FALSE); break;
            }
         }
      }
      catch (CAffirmException ae)
      {
      }

      RegCloseKey(hSerialComm);

      AffirmThrow(RefreshSerialCommNameCache());

      fSuccess = TRUE;
   }
   catch(CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL EnumerateInUseSerialCommNames(POSITION & pos, CString & strName)
{
   BOOL fSuccess = FALSE;

   CString strTempName;
   SSerialCommNameInfo scni;

   try
   {
      if (NULL == pos)
         pos = g_mapSerialCommNameCache.GetStartPosition();

      while ( (NULL != pos) && (!fSuccess))
      {
         g_mapSerialCommNameCache.GetNextAssoc(pos, strTempName, scni);
         switch (scni.eState)
         {
            case SSerialCommNameInfo::DIRTY: // Wasn't taken, but is now
            case SSerialCommNameInfo::NORMAL: // Taken
               strName = strTempName;
               fSuccess = TRUE; // Found one that's in use
               break;

            case SSerialCommNameInfo::DELETED: // Was taken, but isn't now
               break;

            default: Affirm(FALSE); break;
         }

      }
   }
   catch(CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL FindSerialCommName(CString const & strName)
{
   BOOL fFound = FALSE;

   try
   {
      AffirmThrow(g_fSerialCommCacheInited);
      AffirmThrow(!strName.IsEmpty());

      SSerialCommNameInfo scni;
      fFound = g_mapSerialCommNameCache.Lookup(strName, scni);
   }
   catch (CAffirmException ae)
   {
   }

   return fFound;
}

BOOL IsUniqueSerialCommName(CString const & strName)
{
   BOOL fUnique = FALSE;

   try
   {
      AffirmThrow(g_fSerialCommCacheInited);
      AffirmThrow(!strName.IsEmpty());

      fUnique = (!FindSerialCommName(strName) || (g_mapSerialCommNameCache[strName].eState == SSerialCommNameInfo::DELETED));
   }
   catch (CAffirmException ae)
   {
   }

   return fUnique;
}

BOOL CreateUniqueSerialCommName(CString & strName)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(g_fSerialCommCacheInited);

      static CString const strPrefix = TEXT("COM");

      // Hard coded limits - very arbitrary
      static int const nStartAt = 3;
      static int const nStopAt = 10003;

      int nSuffix = nStartAt;
      CString strUniqueName;

      BOOL fUniqueNameFound = FALSE;
      while(!fUniqueNameFound && (nSuffix <= nStopAt))
      {
         strUniqueName.Format(TEXT("%s%d"), strPrefix, nSuffix);
         fUniqueNameFound = IsUniqueSerialCommName(strUniqueName);
         nSuffix++;
      }

      AffirmThrow(fUniqueNameFound);

      strName = strUniqueName;
      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL WriteSerialCommName(CString const & strName)
{
   BOOL fSuccess = FALSE;

   try
   {
      AffirmThrow(g_fSerialCommCacheInited);
      AffirmThrow(!strName.IsEmpty());

      CString strValueName = ((FindSerialCommName(strName)) ? g_mapSerialCommNameCache[strName].strRegValueName : (TEXT("DigiTemporary") + strName));

      g_mapSerialCommNameCache[strName].strRegValueName = strValueName;
      g_mapSerialCommNameCache[strName].eState = SSerialCommNameInfo::DIRTY;

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}

BOOL RemoveSerialCommName(CString const & strName)
{
   BOOL fSuccess = FALSE;

   try
   {
      if (!strName.IsEmpty() && FindSerialCommName(strName))
         g_mapSerialCommNameCache[strName].eState = SSerialCommNameInfo::DELETED;

      fSuccess = TRUE;
   }
   catch (CAffirmException ae)
   {
   }

   return fSuccess;
}
