/*===========================================================================
FILE: 
   CoreUtilities.h

DESCRIPTION:
   Collection of various utility methods

PUBLIC CLASSES AND METHODS:
   StringToLONG
   StringToULONG
   StringToLONGLONG
   StringToULONGLONG

   ParseCommandLine()
   ParseTokens()
   ParseFormatSpecifier()

   FromString( CHAR )
   FromString( UCHAR )
   FromString( SHORT )
   FromString( USHORT )
   FromString( int )
   FromString( UINT )
   FromString( LONG )
   FromString( ULONG )
   FromString( LONGLONG )
   FromString( ULONGLONG )

   ToString( CHAR )
   ToString( UCHAR )
   ToString( SHORT )
   ToString( USHORT )
   ToString( int )
   ToString( UINT )
   ToString( LONG )
   ToString( ULONG )
   ToString( LONGLONG )
   ToString( ULONGLONG )

   GetProgramPath()
   EnumerateFolders()

   ContainerToCSVString()
   CSVStringToContainer()
   CSVStringToValidatedContainer()

   SetDiff()
   SetIntersection()
   SetUnion()

Copyright (c) 2011, Code Aurora Forum. All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of Code Aurora Forum nor
      the names of its contributors may be used to endorse or promote
      products derived from this software without specific prior written
      permission.


THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
===========================================================================*/

//---------------------------------------------------------------------------
// Pragmas
//---------------------------------------------------------------------------
#pragma once

//---------------------------------------------------------------------------
// Include Files
//---------------------------------------------------------------------------
#include <vector>
#include <set>

//---------------------------------------------------------------------------
// Definitions
//---------------------------------------------------------------------------

// Suggested size of an argument buffer to ToString() for any key, each
// ToString() should not write more than this size to the passed in buffer
const ULONG SUGGESTED_BUFFER_LEN = 64;

/*=========================================================================*/
// Prototypes
/*=========================================================================*/

// Replacement/front end for _tcstol
bool StringToLONG( 
   LPCSTR                    pStr,
   int                        base,
   LONG &                     val );

// Replacement/front end for _tcstoul
bool StringToULONG( 
   LPCSTR                    pStr,
   int                        base,
   ULONG &                    val );

// Replacement/front end for _tcstoi64
bool StringToLONGLONG( 
   LPCSTR                    pStr,
   int                        base,
   LONGLONG &                 val );

// Replacement/front end for _tcstoui64
bool StringToULONGLONG( 
   LPCSTR                    pStr,
   int                        base,
   ULONGLONG &                val );

// Parse a command line to tokens (a command line is a string of
// space delimited values where a value that contains space is
// enclosed in text)
void ParseCommandLine(
   std::string                commandLine,
   std::vector <std::string> &    tokens );

// Parse a line into individual tokens
void ParseTokens(
   LPCSTR                    pSeparator,
   LPSTR                     pLine,
   std::vector <LPSTR> &     tokens );

// Parse a format specifier into individual format type tokens
bool ParseFormatSpecifier( 
   LPCSTR                    pFmt,
   ULONG                      fmtLen,
   std::vector <CHAR> &      fmtTypes );

// Convert a string to a value
bool FromString( 
   LPCSTR                    pStr,
   CHAR &                     theType );

// Convert a string to a value
bool FromString( 
   LPCSTR                    pStr,
   UCHAR &                    theType );

// Convert a string to a value
bool FromString( 
   LPCSTR                    pStr,
   SHORT &                    theType );

// Convert a string to a value
bool FromString( 
   LPCSTR                    pStr,
   USHORT &                   theType );

// Convert a string to a value
bool FromString( 
   LPCSTR                    pStr,
   int &                      theType );

// Convert a string to a value
bool FromString( 
   LPCSTR                    pStr,
   UINT &                     theType );

// Convert a string to a value
bool FromString( 
   LPCSTR                    pStr,
   LONG &                     theType );

// Convert a string to a value
bool FromString( 
   LPCSTR                    pStr,
   ULONG &                    theType );

// Convert a string to a value
bool FromString( 
   LPCSTR                    pStr,
   LONGLONG &                 theType );

// Convert a string to a value
bool FromString( 
   LPCSTR                    pStr,
   ULONGLONG &                theType );


// Convert a value to a string
bool ToString( 
   CHAR                       val,
   LPSTR                      pStr );

// Convert a value to a string
bool ToString( 
   UCHAR                      val,
   LPSTR                      pStr );

// Convert a value to a string
bool ToString( 
   SHORT                      val,
   LPSTR                      pStr );

// Convert a value to a string
bool ToString( 
   USHORT                     val,
   LPSTR                      pStr );

// Convert a value to a string
bool ToString( 
   int                        val,
   LPSTR                      pStr );

// Convert a value to a string
bool ToString( 
   UINT                       val,
   LPSTR                      pStr );

// Convert a value to a string
bool ToString( 
   LONG                       val,
   LPSTR                      pStr );

// Convert a value to a string
bool ToString( 
   ULONG                      val,
   LPSTR                      pStr );

// Convert a value to a string
bool ToString( 
   LONGLONG                   val,
   LPSTR                      pStr );

// Convert a value to a string
bool ToString( 
   ULONGLONG                  val,
   LPSTR                      pStr );

// Return the special folder used for storing program files
std::string GetProgramPath();

// Enumerate the subfolders of the given folder (recursive)
void EnumerateFolders( 
   const std::string &           baseFolder,
   std::vector <std::string> &   foundFolders );

/*=========================================================================*/
// Free Methods
/*=========================================================================*/

/*===========================================================================
METHOD:
   ContainerToCSVString (Free Method)

DESCRIPTION:
   Convert the contents of a container to a CSV string

   NOTE: ToString() must be defined for the container value type
  
PARAMETERS:   
   cont        [ I ] - The container
   sep         [ I ] - The character separating tokens
   csv         [ O ] - The resulting comma separated string

RETURN VALUE:
   None
===========================================================================*/
template <class Container>
void ContainerToCSVString( 
   const Container &          cont,
   CHAR                       sep,
   std::string &              csv )
{
   csv = "";
   if ((ULONG)cont.size() > (ULONG)0)
   {
      CHAR keyBuf[SUGGESTED_BUFFER_LEN];

      typename Container::const_iterator pIter = cont.begin();
      while (pIter != cont.end())
      {
         const typename Container::value_type & theKey = *pIter;
         bool bOK = ToString( theKey, &keyBuf[0] );

         if (bOK == true && keyBuf[0] != 0)
         {
            if (pIter != cont.begin())
            {
               csv += sep;
            }

            csv += (LPCSTR)&keyBuf[0];
         }

         pIter++;
      }
   }
}

/*===========================================================================
METHOD:
   CSVStringToContainer (Free Method)

DESCRIPTION:
   Populate a container from a parsed CSV string

   NOTE: FromString() must be defined for the container value type 
   NOTE: The container is emptied before this operation is attempted

PARAMETERS:   
   pSeparator  [ I ] - The characters separating tokens
   pCSV        [ I ] - The comma separated string (will be modified)
   cont        [ O ] - The resulting container
   bClear      [ I ] - Clear the container first?  NOTE: if the container
                       is not cleared first then insertions may fail for
                       duplicate keys

RETURN VALUE:
   None
===========================================================================*/
template <class Container>
void CSVStringToContainer( 
   LPCSTR                     pSeparator, 
   LPSTR                      pCSV,
   Container &                cont,
   bool                       bClear = true )
{
   if (pCSV != 0 && *pCSV != 0)
   {
      // Remove a leading quote?
      if (*pCSV == '\"')
      {
         pCSV++;
      }

      // Remove a trailing quote?
      ULONG len = (ULONG)strlen( pCSV );
      if (len > 0)
      {
         if (pCSV[len - 1] == '\"')
         {
            pCSV[len - 1] = 0;
         }
      }

      // Clear the container first?
      if (bClear == true)
      {
         cont.clear();
      }

      std::vector <LPSTR> tokens;
      ParseTokens( pSeparator, pCSV, tokens );

      std::vector <LPSTR>::const_iterator pIter = tokens.begin();
      while (pIter != tokens.end())
      {
         LPCSTR pTok  = *pIter;
  
         typename Container::value_type theKey;
         bool bOK = ::FromString( pTok, theKey );

         if (bOK == true)
         {      
            std::insert_iterator <Container> is( cont, cont.end() );
            *is = theKey;
         }
       
         pIter++;
      }
   }
}

/*===========================================================================
METHOD:
   CSVStringToValidatedContainer (Free Method)

DESCRIPTION:
   Populate a container from a parsed CSV string

   NOTE: FromString() and IsValid() must be defined for the container 
         value type (the later need not do anything but return true)

   NOTE: The container is emptied before this operation is attempted
  
PARAMETERS:   
   pSeparator  [ I ] - The characters separating tokens
   pCSV        [ I ] - The comma separated string (will be modified)
   cont        [ O ] - The resulting container

RETURN VALUE:
   None
===========================================================================*/
template <class Container>
void CSVStringToValidatedContainer( 
   LPCSTR                     pSeparator, 
   LPSTR                      pCSV,
   Container &                cont )
{
   cont.clear();
   if (pCSV != 0 && *pCSV != 0)
   {
      // Remove a leading quote?
      if (*pCSV == '\"')
      {
         pCSV++;
      }

      // Remove a trailing quote?
      ULONG len = (ULONG)strlen( pCSV );
      if (len > 0)
      {
         if (pCSV[len - 1] == '\"')
         {
            pCSV[len - 1] = 0;
         }
      }

      cont.clear();

      std::vector <LPSTR> tokens;
      ParseTokens( pSeparator, pCSV, tokens );

      std::vector <LPSTR>::const_iterator pIter = tokens.begin();
      while (pIter != tokens.end())
      {
         LPCSTR pTok  = *pIter;

         typename Container::value_type theKey;
         bool bOK = ::FromString( pTok, theKey );

         if (bOK == true)
         {
            bool bValid = IsValid( theKey );
            if (bValid == true)
            {
               std::insert_iterator <Container> is( cont, cont.end() );
               *is = theKey;
            }
         }
      
         pIter++;
      }
   }
}

/*===========================================================================
METHOD:
   SetDiff (Free Method)

DESCRIPTION:
   Given two sets return a third that contains everything in the first
   set but not the second set

PARAMETERS:
   setA        [ I ] - The first set
   setB        [ I ] - The second set
  
RETURN VALUE:
   std::set <T> - the difference
===========================================================================*/
template <class T>
std::set <T> SetDiff(
   const std::set <T> &       setA,
   const std::set <T> &       setB )
{
   std::set <T> retSet;

   if (setB.size() == 0)
   {
      // Everything that is in the first set but not the second
      // (empty) set is ... the first set!
      retSet = setA;
   }
   else if (setA.size() == 0)
   {
      // The first set is empty, hence the return set is empty
   }
   else
   {
      // Both sets have elements, therefore the iterators will
      // be valid and we can use the standard approach
      typename std::set <T>::const_iterator pIterA = setA.begin();
      typename std::set <T>::const_iterator pIterB = setB.begin();

      for ( ; pIterA != setA.end() && pIterB != setB.end(); )
      {
         if (*pIterA < *pIterB)
         {
            retSet.insert( *pIterA );
            pIterA++;
         }
         else if (*pIterB < *pIterA)
         {
            pIterB++;
         }
         else
         {
            pIterA++;
            pIterB++;
         }
      }

      while (pIterA != setA.end())
      {
         retSet.insert( *pIterA );
         pIterA++;
      }
   }

   return retSet;
}

/*===========================================================================
METHOD:
   SetIntersection (Free Method)

DESCRIPTION:
   Given two sets return a third that contains everything that is in both
   sets

PARAMETERS:
   setA        [ I ] - The first set
   setB        [ I ] - The second set
  
RETURN VALUE:
   std::set <T> - the union
===========================================================================*/
template <class T>
std::set <T> SetIntersection(
   const std::set <T> &       setA,
   const std::set <T> &       setB )
{
   std::set <T> retSet;

   // Neither set can be empty
   if (setA.size() != 0 && setA.size() != 0)
   {
      // Both sets have elements, therefore the iterators will
      // be valid and we can use the standard approach
      typename std::set <T>::const_iterator pIterA = setA.begin();
      typename std::set <T>::const_iterator pIterB = setB.begin();

      for ( ; pIterA != setA.end() && pIterB != setB.end(); )
      {
         if (*pIterA < *pIterB)
         {
            pIterA++;
         }
         else if (*pIterB < *pIterA)
         {
            pIterB++;
         }
         else
         {
            retSet.insert( *pIterA );
            pIterA++;
            pIterB++;
         }
      }
   }

   return retSet;
}

/*===========================================================================
METHOD:
   SetUnion (Free Method)

DESCRIPTION:
   Given two sets return a third that contains everything that is either
   in the first set or in the second set

PARAMETERS:
   setA        [ I ] - The first set
   setB        [ I ] - The second set
  
RETURN VALUE:
   std::set <T> - the union
===========================================================================*/
template <class T>
std::set <T> SetUnion(
   const std::set <T> &       setA,
   const std::set <T> &       setB )
{
   std::set <T> retSet;

   if (setB.size() == 0)
   {
      // Everything that is in the first (possibly empty) set or in 
      // the second (empty) set is ... the first set!
      retSet = setA;
   }
   else if (setA.size() == 0)
   {
      // Everything that is in the first (empty) set or in the 
      // second (possibly empty) set is ... the second set!
      retSet = setB;
   }
   else
   {
      // Both sets have elements, therefore the iterators will
      // be valid and we can use the standard approach
      typename std::set <T>::const_iterator pIterA = setA.begin();
      typename std::set <T>::const_iterator pIterB = setB.begin();

      for ( ; pIterA != setA.end() && pIterB != setB.end(); )
      {
         if (*pIterA < *pIterB)
         {
            retSet.insert( *pIterA );
            pIterA++;
         }
         else if (*pIterB < *pIterA)
         {
            retSet.insert( *pIterB );
            pIterB++;
         }
         else
         {
            retSet.insert( *pIterA );
            pIterA++;
            pIterB++;
         }
      }

      while (pIterA != setA.end())
      {
         retSet.insert( *pIterA );
         pIterA++;
      }

      while (pIterB != setB.end())
      {
         retSet.insert( *pIterB );
         pIterB++;
      }
   }

   return retSet;
}

