#include "header.h"
#include "sitemap.h"


        // Static Data
        // ******************

static INFOTYPE curr_bit;   // used for getting the next IT in a category
static int curr_cat;
static int curr_offset;     // There are 32 info type bits per offset.


// ********************************************************
//
// Methods to operate on CSitemap Categories of Information Types
//
// ********************************************************

void CSiteMap::AddITtoCategory(int cat, int type)
{
int       offset;
INFOTYPE *pInfoType;

    if ( cat < 0 )
        return;
    m_itTables.m_aCategories[cat].c_Types++;
    offset = type / 32;
    pInfoType = m_itTables.m_aCategories[cat].pInfoType + offset;
    *pInfoType |=  1<<type;

    if ( m_itTables.m_max_categories == (cat+1) )
    {
        m_itTables.m_aCategories = (CATEGORY_TYPE*) lcReAlloc( m_itTables.m_aCategories, (m_itTables.m_max_categories+5) * sizeof(CATEGORY_TYPE) );

        for(int i=m_itTables.m_max_categories; i < m_itTables.m_max_categories+5; i++)
        m_itTables.m_aCategories[i].pInfoType = (INFOTYPE*)lcCalloc(InfoTypeSize());

        m_itTables.m_max_categories += 5;
    }
}



void CSiteMap::DeleteITfromCat(int cat, int type)
{
int       offset;
INFOTYPE *pInfoType;

    if ( cat < 0 )
        return;
    m_itTables.m_aCategories[cat].c_Types--;
    offset = type / 32;
    pInfoType = m_itTables.m_aCategories[cat].pInfoType + offset;
    *pInfoType &= ~(1<<type);

}



int CSiteMap::GetFirstCategoryType( int pos ) const
{

    curr_offset = -1;    // gets incermented to 0 before first IT comparison.
    curr_cat = pos;      // keep the position of the category for calls to GetNextITinCagetory.
    curr_bit =1;         // bit zero of offset zero is reserved. But check it any way.

    if ( pos < 0 )
        return -1;
    int bitpos = GetITfromCat(&curr_offset,
                        &curr_bit,
                        m_itTables.m_aCategories[curr_cat].pInfoType,
                        InfoTypeSize()*8);
    if( (bitpos!=-1) && (IsDeleted(bitpos)) )
        bitpos = GetNextITinCategory();
    return bitpos;

}



int CSiteMap::GetNextITinCategory( void ) const
{
    if ( curr_cat < 0 )
        return -1;  // must call GetFirstCategoryType() before calling this fn.

    int pos = GetITfromCat(&curr_offset,
                        &curr_bit,
                        m_itTables.m_aCategories[curr_cat].pInfoType,
                        InfoTypeSize()*8);
    while ( (pos!=-1) && (IsDeleted(pos)))
        pos = GetITfromCat(&curr_offset,
                        &curr_bit,
                        m_itTables.m_aCategories[curr_cat].pInfoType,
                        InfoTypeSize()*8);
    return pos;
}



int CSiteMap::GetLastCategoryType(int pos) const
{   int cat;
    int offset;
    INFOTYPE bit;

    cat = pos;      // The category
    if ( cat < 0 )
        return -1;
    offset = -1;
    bit = 1;

    for (int i=0; i<m_itTables.m_aCategories[cat].c_Types-1; i++)
        if ( GetITfromCat(&offset,
                          &bit,
                          m_itTables.m_aCategories[cat].pInfoType,
                          InfoTypeSize()*8 ) == -1 )
            return -1;

    return  GetITfromCat(&offset,
                         &bit,
                         m_itTables.m_aCategories[cat].pInfoType,
                         InfoTypeSize() * 8 );
}

static int BitToDec( int binary )
{
register int count=0;
register int bit=1;

//    if ( binary < 1 )
//        return -1;
    if ( binary == 1 )
        return 1;

    while ( bit != binary )
    {
        bit = bit<<1;
        count++;
    }
    return count;
}


// ***********************************************
//
// Generic methods to operate on Categories.
//
// ***********************************************

/*
    GetITfromCat        A helper function used to convert a bit position to it's to a decimal number.
                        Given a Category and offset, and a bit field with on bit set returns the
                        next bit set in the category.  ie. a bit corresponds to an information
                        type.
    Parameters:
            int const cat     <I>       The category to work on
            int* offset       <I/O>     The offset into the infotype bits, an offset is 4 bytes long
                                        must be -1 for the first offset, 0 for second offset ...
            int* bit          <I/O>     The first bit position to test.

    Return Value        The decimal number of the bit position.  ie. bit position 5 returns 5.
*/

int GetITfromCat(int* offset, INFOTYPE* bit, INFOTYPE* pInfoType, int cTypes)
{
   INFOTYPE InfoType;

   if ( *offset < 0 )
       *offset = 0;
   memcpy(&InfoType, pInfoType + *offset, sizeof(INFOTYPE) );

   for(int i=BitToDec(*bit)+(*offset*32); i < cTypes; i++)
   {              // offset: 0      1      2
        if ( i % 32 == 0 )// 0-31, 32-63, 64-95 ...
        {
            (*offset)++;
            *bit = 1;   // bit 0, 32, 64 ... depending on the offset.
            memcpy(&InfoType, pInfoType + *offset, sizeof(INFOTYPE) );
        }
        if ( *bit & InfoType )
        {
            int ret = (int)((*offset*32)+(BitToDec(*bit)));   // return the decimal value of the bit position.
            *bit = *bit << 1;
            return ret;
        }
        else
            *bit = *bit << 1;
    }
    return -1;  // There are no infotypes in this category.
}


// ******************************************************
//
// Methods to operate on CSiteMap Exclusive Information Types
//
// ******************************************************

static int ExclusiveOffset = -1;    // Gets incermented to zero before search begins
static INFOTYPE ExclusiveBit = 1;        // check bit 0 even though it is reserved.

int CSiteMap::GetFirstExclusive(int offset, INFOTYPE bit) const
{
    ExclusiveOffset = offset;
    ExclusiveBit = bit;

    int pos = GetITBitfromIT(m_itTables.m_pExclusive,
                          &ExclusiveOffset,
                          &ExclusiveBit,
                          InfoTypeSize()*8 );
    if( (pos!=-1) && (IsDeleted(pos)) )
        pos = GetNextExclusive();
    return pos;
}


int CSiteMap::GetNextExclusive(void) const
{
    if ( ExclusiveOffset < -1 || ExclusiveBit < 1 )
        return -1;
    int pos = GetITBitfromIT(m_itTables.m_pExclusive,
                          &ExclusiveOffset,
                          &ExclusiveBit,
                          InfoTypeSize()*8 );
    while ((pos!=-1) && (IsDeleted(pos)))
        pos = GetITBitfromIT(m_itTables.m_pExclusive,
                          &ExclusiveOffset,
                          &ExclusiveBit,
                          InfoTypeSize()*8 );
    return pos;
}


int CSiteMap::GetLastExclusive(void) const
{
    int i;
    int iLastExclusive=-1, temp;
    int offset=-1;
    INFOTYPE bit=1;

    for (i=0; i< HowManyInfoTypes(); i++)
        if ( (temp = GetITBitfromIT(m_itTables.m_pExclusive, &offset, &bit, InfoTypeSize()*8))  == -1 )
            return iLastExclusive;
        else
            iLastExclusive = temp;

    return -1;
}



// ******************************************************
//
// Methods to operate on CSiteMap Hidden Information Types
//
// ******************************************************

static int HiddenOffset=-1;
static INFOTYPE HiddenBit=1;

int CSiteMap::GetFirstHidden(int offset, INFOTYPE bit) const
{
    HiddenOffset = offset;
    HiddenBit = bit;

    int pos = GetITBitfromIT(m_itTables.m_pHidden,
                          &HiddenOffset,
                          &HiddenBit,
                          InfoTypeSize()*8 );
    if( (pos != -1) && (IsDeleted(pos)) )
        pos = GetNextHidden();
    return pos;
}


int CSiteMap::GetNextHidden(void) const
{
    if ( HiddenOffset < -1 || HiddenBit < 1 )
        return -1;
    int pos = GetITBitfromIT(m_itTables.m_pHidden,
                          &HiddenOffset,
                          &HiddenBit,
                          InfoTypeSize()*8 );
    while((pos!=-1) &&(IsDeleted(pos)) )
        pos = GetITBitfromIT(m_itTables.m_pHidden,
                          &HiddenOffset,
                          &HiddenBit,
                          InfoTypeSize()*8 );
    return pos;
}


int CSiteMap::GetLastHidden(void) const
{
    int i;
    int iLastHidden=-1, temp;
    int offset=-1;
    INFOTYPE bit=1;

    for (i=0; i<HowManyInfoTypes(); i++)
        if ( (temp = GetITBitfromIT(m_itTables.m_pHidden, &offset, &bit, InfoTypeSize()*8)) == -1)
            return iLastHidden;
        else
            iLastHidden = temp;
    return -1;
}


// ***************************************************************************
//
// Generic Methods to operate on Hidden and Exclusive Information Types
//
// ***************************************************************************

void DeleteIT(int const type, INFOTYPE *pIT )
{
int offset;
INFOTYPE *pInfoType;
INFOTYPE deleteIT;

    if ( type <= 0 || !pIT)
        return;

    deleteIT = 1<<type;
    offset = type / 32;
    pInfoType = pIT + offset;
    *pInfoType &= ~deleteIT;
}


void AddIT(int const type, INFOTYPE *pIT )
{
int offset;
INFOTYPE *pInfoType;

    if ( type <= 0 || !pIT)
        return;

    offset = type / 32;
    pInfoType = pIT + offset;
    *pInfoType |= 1<<type;
}


void CopyITBits(INFOTYPE** ITDst, INFOTYPE* ITSrc, int size)
{
    if ( !*ITDst || !ITSrc )
        return;

    lcFree( *ITDst );
    *ITDst = (INFOTYPE*)lcCalloc( size );

    memcpy( *ITDst, ITSrc, size);
}


int GetITBitfromIT(INFOTYPE const *IT, int* offset, INFOTYPE* bit, int cTypes)
{
   INFOTYPE InfoType;

   if ( *offset < 0 )
       *offset = 0;
   memcpy(&InfoType, IT + *offset, sizeof(INFOTYPE) );

   for(int i=BitToDec(*bit)+(*offset*32); i < cTypes; i++)   // we are checking bit 0 even though it is reserved.
   {               // offset: 0      1      2
        if ( i % 32 == 0 )// 0-31, 32-63, 64-95 ...
        {
            (*offset)++;
            *bit = 1;   // bit 0, 32, 64 ... depending on the offset.
            memcpy(&InfoType, IT + *offset, sizeof(INFOTYPE) );
        }
        if ( *bit & InfoType )
        {
            int ret = (int)((*offset*32)+(BitToDec(*bit)));   // return the decimal value of the bit position.
            *bit = *bit << 1;
            return ret;
        }
        else
            *bit = *bit << 1;
    }
    return -1;  // There are no infotype bits in this InfoType field.
}