// StringProperty.cpp: implementation of the CStringProperty class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "StringProperty.h"
#include "utils.h"

#define TRACE 0?0:

CDictionary CStringPropertySection::m_Dictionary;

CStringPropertySection::CStringPropertySection()
{
    m_UsedItems=0;  // make this an array and re-alloc it - like the CDPA ! Gosh.
    m_ItemSize=RUN_SIZE;
    m_Items=new LINKSTRING[m_ItemSize];
}

//
// Finds the Value from the KEY, returns NULL if the KEY isn't used.
//
LPCTSTR CStringPropertySection::Get(LPCTSTR szPropID)
{
    int iEntry;
	if( Find( szPropID, &iEntry ) )
		return (LPCTSTR)m_Items[iEntry].pszValue;
	return NULL;
}

#define _MEM_DEBUG
#ifdef _MEM_DEBUG
DWORD g_KeyAlloc;
DWORD g_ValueAlloc;
#endif

//
// Associats a value (perhaps new) to a given key.
//
BOOL CStringPropertySection::Set(LPCTSTR szPropID, LPCTSTR szValue)
{
    int iEntry;
	if( Find( szPropID, &iEntry ) == FALSE )
	{
        if( m_UsedItems == m_ItemSize )
        {
            // re allocate the buffer bigger.
            m_ItemSize*=2;
            PLINKSTRING pNew=new LINKSTRING[m_ItemSize];
            CopyMemory(pNew, m_Items, sizeof(LINKSTRING) * m_UsedItems );
            delete m_Items;
            m_Items=pNew;
        }

        iEntry=m_UsedItems++;
		m_Items[iEntry].idKey=m_Dictionary.GetID( szPropID );
	}
	else
    {
		delete m_Items[iEntry].pszValue;
    }

	DWORD dwLen=lstrlen(szValue)+1;
#ifdef _MEM_DEBUG
    g_ValueAlloc+=dwLen;
#endif
	m_Items[iEntry].pszValue=new TCHAR[dwLen];
	CopyMemory( m_Items[iEntry].pszValue, szValue, dwLen*sizeof(TCHAR) );
	return TRUE;
}

//
// Finds a string in the table.
// The KEY of the thing you are finding
// An OUT pointer to the LINKSTRING structure
// which item in the LINKSTRING structure can be used.
//
BOOL CStringPropertySection::Find( LPCTSTR szPropID, int * pEntry)
{
    UINT uiPropID = m_Dictionary.GetID( szPropID ); // this is the ID for this string.
    for(UINT i=0;i<m_UsedItems;i++)
    {
        if( uiPropID == m_Items[i].idKey )
        {
            *pEntry=i;
            return TRUE;
        }
    }
    *pEntry=0;
	return FALSE;
}

CStringPropertySection::~CStringPropertySection()
{
    Purge();
    delete m_Items;
    m_Items=NULL;
}

void CStringPropertySection::Purge()
{
	for(UINT i=0;i<m_UsedItems;i++)
	{
        m_Items[i].idKey=0;
		delete m_Items[i].pszValue;
        m_Items[i].pszValue=NULL;
	}
    m_UsedItems=0;
}

//
// Looks for the existance of a property szPropID
// if present and set to YES, returns dwResult (default of TRUE).
// otherwise returns defValue
//
DWORD CStringPropertySection::YesNo(LPCTSTR szPropID, DWORD dwNotPresent, DWORD dwYes)
{
    return YesNo( szPropID, dwNotPresent, FALSE, dwYes );
}

DWORD CStringPropertySection::YesNo(LPCTSTR szPropID, DWORD dwNotPresent, DWORD dwNo, DWORD dwYes)
{
   	LPCTSTR req=(LPCTSTR)Get(szPropID);
    if( req == NULL )
        return dwNotPresent;

    if( req && lstrcmpi(req,TEXT("YES"))==0)
        return dwYes;
    return dwNo;
}

DWORD   CStringPropertySection::ValueOf(LPCTSTR szPropID, DWORD dwDefault)
{
   	LPCTSTR req=(LPCTSTR)Get(szPropID);
	// return req?_ttoi( req ):dwDefault;
    return req?StringToInt(req):dwDefault;
}



////////////////////////////////////////////////////////////////////////////////////////////////
//
// Dictionary.
// maps strings to UINTS, in a case insensitive manner.
//
////////////////////////////////////////////////////////////////////////////////////////////////

CDictionary::CDictionary()
{
	for(int i=0;i<DIC_TABLE_SIZE;i++)
		m_Table[i]=NULL;
}

UINT CDictionary::GetID(LPCTSTR szPropID)
{
    //
    // Has the string, find the bucket.
    //
	DWORD dwStrLen=lstrlen(szPropID)+1;
	DWORD hash=Hash(szPropID, dwStrLen);

    PLINKDICSTRING * pFoundPointer = &m_Table[hash];
    PLINKDICSTRING pFound=*pFoundPointer;
    int iNumber=0+1;
	while( pFound )
	{
        int used=pFound->cbUsed;
        for( int i=0;i<used;i++)
        {
            iNumber++;
		    if( lstrcmpi( pFound->pszKey[i], szPropID) == 0 )
                return pFound->ID[i];
        }

        //
        // If we have space in this run, use it up.
        //
        if( used < DIC_RUN_SIZE )
            break;

        // Next bucket
        pFoundPointer=&pFound->pNext;
        pFound=*pFoundPointer;
	}

    if( pFound == NULL )
    {
		pFound = new LINKDICSTRING;
        ZeroMemory( pFound, sizeof(LINKDICSTRING) );
        *pFoundPointer=pFound;
        TRACE(TEXT("Dic at 0x%08x\n"), pFound );
    }

    //
    // Make the ID out of the bucket we are in, and the item we're adding.
    //
    int iEntry=pFound->cbUsed++;
    pFound->ID[iEntry]=hash<<8 | iNumber;

#ifdef _MEM_DEBUG
    g_ValueAlloc+=dwStrLen;
#endif

	pFound->pszKey[iEntry]=new TCHAR[dwStrLen];
	CopyMemory( pFound->pszKey[iEntry], szPropID, dwStrLen*sizeof(TCHAR) );

    return pFound->ID[iEntry];
}

LPCTSTR CDictionary::GetString(UINT ID)
{
    int iBucket=ID >> 8;
    // for( int iBucket=0;iBucket<DIC_TABLE_SIZE;iBucket++)
    {
        PLINKDICSTRING pLinkString=m_Table[iBucket];
        int i=0;
	    while( pLinkString )
	    {
            int used=pLinkString->cbUsed;
            for( int i=0;i<used;i++)
            {
		        if( pLinkString->ID[i]  == ID )
                    return pLinkString->pszKey[i];
            }

            pLinkString=( pLinkString->pNext );
	    }
    }
	return 0;
}


//
// Case insensitive hash.
//
DWORD CDictionary::Hash( LPCTSTR szString, DWORD dwStrLen )
{
	TCHAR szChar=toupper( *szString );
	return (dwStrLen * szChar) % DIC_TABLE_SIZE;
}


CDictionary::~CDictionary()
{
    Purge();
}

void CDictionary::Purge()
{
	for(int i=0;i<DIC_TABLE_SIZE;i++)
	{
		PLINKDICSTRING pNext;
		PLINKDICSTRING pEntry = m_Table[i];
		while( pEntry )
		{
            for(int ti=0;ti<pEntry->cbUsed;ti++)
            {
                pEntry->ID[ti]=0;
			    delete pEntry->pszKey[ti];
                pEntry->pszKey[ti]=NULL;
            }
			pNext= pEntry->pNext;
			delete pEntry;
            TRACE(TEXT("Dic @ 0x%08x\n"), pEntry );
			pEntry=pNext;
		}
        m_Table[i]=NULL;
	}
}