/*TITLE implementation of block classes*/

/****keyword-flag*** "%v %f %n" */
/* "15 6-May-98,18:08:18 BLOCK.CPP" */

/****keyword-flag*** "%v %f %n" */
char program_version_info[] = "15 6-May-98,18:08:18 BLOCK.CPP";

#include "common.h"
#include "blocki.h"


//QuantumFile member functions

ArrayIndex QuantumFile::GetMainObjectBlockCount()
{
    return (m_MainObjectCount-1) / MainObjectEntriesPerBlock + 1;
}
    
ArrayIndex QuantumFile::QGetFreeSpaceListBlockCount() 
{
    return (m_FreeSpaceListCount-1) / FreeSpaceEntriesPerBlock+1;
}

    
void QuantumFile::SetModified(ArrayIndex p_BlockNumber)
{
    ArrayIndex BufferNumber;

    BufferNumber = FindBuffer(p_BlockNumber,
        (ArrayIndex *)m_BlockNumber.GetDataAddress(),
        m_BlockNumber.GetSize());

    qfassert(BufferNumber != BadBlockNumber);

    m_BufferModified[BufferNumber] = TRUE;
}

Block *QuantumFile::MakeBlockResident(ArrayIndex p_BlockNumber)
{
    Ulong TimeStamp;
    ArrayIndex i;
    ArrayIndex EarliestStamp;
    ArrayIndex BufferNumber;

    m_Counter ++;
    if (m_Counter == 0)  // thrash prevention
        {
        for (i = 0; i < m_BlockNumber.GetSize(); i ++)
            m_TimeStamp[i] = 0;
        }

    BufferNumber = FindBuffer(p_BlockNumber,
      (ArrayIndex *)m_BlockNumber.GetDataAddress(),
        m_BlockNumber.GetSize());

    if (BufferNumber != BadBlockNumber)
        {
        m_TimeStamp[BufferNumber] = m_Counter;
        return m_Block[BufferNumber];
        }

    TimeStamp = ULONG_MAX;
    EarliestStamp = 0; // in case we don't find anything to boot
    for (i = 0; i < m_TimeStamp.GetSize(); i ++)
        {
        if (m_TimeStamp[i] < TimeStamp) // find the earliest timestamp
            {
            TimeStamp = m_TimeStamp[i];
            EarliestStamp = i;
            }
        }

    if (m_BufferModified[EarliestStamp]) /* it needs to be written out */
        Write(EarliestStamp);

    // set block number of new occupant
    m_BlockNumber[EarliestStamp] = p_BlockNumber;

    m_BufferModified[EarliestStamp] = FALSE; // not modified yet
    m_TimeStamp[EarliestStamp] = m_Counter;
    Read(EarliestStamp);

    return m_Block[EarliestStamp];
}

void QuantumFile::Open(String p_FileName, ArrayIndex p_BufferCount)
{
    QuantumFileHeaderStruct QFHS;
    char *TempBlock;
    QuantumNumber *TempMainObjectBlock;
    QFreeSpaceEntry *TempFreeSpaceBlock;
    ArrayIndex i;
    int FileCreation;
    QFreeSpaceEntry TempFreeSpaceEntry;
    int ProgramVersion;
    int FileVersion;

    m_ReadCount = 0;
    m_WriteCount = 0;
    m_Block.SetSize(p_BufferCount);
    m_BlockNumber.SetSize(p_BufferCount);
    m_BufferModified.SetSize(p_BufferCount);
    m_TimeStamp.SetSize(p_BufferCount);

    if (p_FileName == "")
        {
        m_DosFilePtr = 0;
        return;
        }

    m_DosFilePtr = fopen(p_FileName,"r+b");

    if (m_DosFilePtr == NULL) /* file does not exist */
        {
        FileCreation = TRUE;
        m_DosFilePtr = fopen(p_FileName,"w+b"); /* create it */

        TempBlock = new char[BlockSize];
        TempMainObjectBlock = (QuantumNumber *)TempBlock;
        TempFreeSpaceBlock = (QFreeSpaceEntry *)TempBlock;

        memset((void *)&QFHS,0,sizeof(QuantumFileHeaderStruct));
        QFHS.main_object_index_count = MainObjectIndexCount;
        QFHS.free_space_list_count = MaxFileQuantumCount;
        strcpy(QFHS.quantum_version_info,program_version_info);

        QFHS.main_object_index_offset = BlockSize;
        QFHS.free_space_list_offset = QFHS.main_object_index_offset+
            BlockSize*((MainObjectIndexCount-1)/MainObjectEntriesPerBlock+1);
        QFHS.starting_quantum_offset = QFHS.free_space_list_offset+
            BlockSize*((MaxFileQuantumCount-1)/FreeSpaceEntriesPerBlock+1);

        fseek(m_DosFilePtr,0,SEEK_SET);
        memset(TempBlock,0,BlockSize);
        fwrite(TempBlock,1,BlockSize,m_DosFilePtr); // clear block 0

        fseek(m_DosFilePtr,0,SEEK_SET);
        fwrite(&QFHS,1,sizeof(QuantumFileHeaderStruct), m_DosFilePtr);

        /* clear the main object index */
        memset(TempMainObjectBlock,0,BlockSize);
        TempMainObjectBlock[0] = NoQuantum;
        fseek(m_DosFilePtr,QFHS.main_object_index_offset,SEEK_SET);
        fwrite(TempMainObjectBlock,BlockSize,1,m_DosFilePtr);

        /* clear the free space list */
        TempFreeSpaceEntry.m_FreeSpaceCode = AvailableQuantum;
        TempFreeSpaceEntry.m_ObjectNumber = 0;
        for (i = 0; i < FreeSpaceEntriesPerBlock; i ++)
            TempFreeSpaceBlock[i] = TempFreeSpaceEntry;
        TempFreeSpaceBlock[0].m_FreeSpaceCode = 0; /* Q #0 doesn't exist */
        ArrayIndex FreeSpaceCount = (QFHS.free_space_list_count-1)/
            FreeSpaceEntriesPerBlock+1;
        for (i = 0; i < FreeSpaceCount; i ++)
            {
            fseek(m_DosFilePtr,QFHS.free_space_list_offset+i*BlockSize,SEEK_SET);
            fwrite(TempFreeSpaceBlock,sizeof(QFreeSpaceEntry),
            FreeSpaceEntriesPerBlock,m_DosFilePtr);
            TempFreeSpaceBlock[0].m_FreeSpaceCode = AvailableQuantum;
            }
        delete [] TempBlock;
        }
    else
        {
        FileCreation = FALSE;
        fread(&QFHS,1,sizeof(QuantumFileHeaderStruct),m_DosFilePtr);
        ProgramVersion = atoi(program_version_info);
        FileVersion = atoi(QFHS.quantum_version_info);
        if (ProgramVersion != FileVersion)
            {
            printf("Program version %d does not match file version %d.\n",
            ProgramVersion,FileVersion);
//            exit(1);    // file is newer than program, so forget it
            }
        }

    m_MainObjectStartingBlock = QFHS.main_object_index_offset / BlockSize;
    m_FreeSpaceListStartingBlock = QFHS.free_space_list_offset / BlockSize;
    m_QuantumStartingBlock = QFHS.starting_quantum_offset / BlockSize;
    m_MainObjectCount = QFHS.main_object_index_count;
    m_FreeSpaceListCount = QFHS.free_space_list_count;
    
    for (i = 0; i < p_BufferCount; i ++)
        {
        m_Block[i] = new Block;
        m_BlockNumber[i] = BadBlockNumber;
        m_BufferModified[i] = FALSE;
        m_TimeStamp[i] = 0;
        }

    m_FreeSpaceArray = new FreeSpaceArrayPtr(this);
    m_MainObjectArray = new MainObjectArrayPtr(this);

    if (FileCreation == TRUE) // set up main object name object
        (*m_MainObjectArray)->CreateMainObject("Directory", MainObjectDirectory, 
            MainObjectIndexCount,MainObjectIndexCount);
}

QuantumFile::QuantumFile():
m_Block(ArrayIndex(0)),
m_BlockNumber(ArrayIndex(0)),
m_BufferModified(ArrayIndex(0)),
m_TimeStamp(ArrayIndex(0)),
m_FileName(""),
m_DosFilePtr(0),
m_CurrentLowestFreeBlock(0),
m_MainObjectStartingBlock(0),
m_FreeSpaceListStartingBlock(0),
m_QuantumStartingBlock(0),
m_MainObjectCount(0),
m_FreeSpaceListCount(0),
m_Counter(0),
m_FreeSpaceArray(0),
m_MainObjectArray(0)
{
}

QuantumFile::~QuantumFile()
{
    Close();
}

void QuantumFile::Close()
{
    Flush();
    
    fclose(m_DosFilePtr);
    m_DosFilePtr = 0;

    delete m_FreeSpaceArray;
    delete m_MainObjectArray;

    for (ArrayIndex i = 0; i < m_Block.GetSize(); i ++)
        delete m_Block[i];
}

void QuantumFile::Flush()
{
    if (m_DosFilePtr)
        {
        for (ArrayIndex i=0; i < m_Block.GetSize(); i ++)
            {
            if (m_BufferModified[i])
                Write(i);
            }
        }
}

void QuantumFile::Read(ArrayIndex p_BufferNumber)
{
    Position(m_BlockNumber[p_BufferNumber]); // position the file

    fread((char *)m_Block[p_BufferNumber],1,BlockSize,m_DosFilePtr);

    m_BufferModified[p_BufferNumber] = FALSE;

    m_ReadCount ++;
}

void QuantumFile::Write(ArrayIndex p_BufferNumber)
{
    Position(m_BlockNumber[p_BufferNumber]); // position the file

    fwrite((char *)m_Block[p_BufferNumber],1,BlockSize,m_DosFilePtr);

    m_BufferModified[p_BufferNumber] = FALSE;

    m_WriteCount ++;
}

void QuantumFile::Position(ArrayIndex p_BlockNumber)
{
    long DesiredPosition;

    DesiredPosition = (p_BlockNumber)*(long)BlockSize;

    fseek(m_DosFilePtr,DesiredPosition,SEEK_SET);
}


//FreeSpaceBlock member functions

LeafBlockPtr FreeSpaceBlockPtr::FreeSpaceBlock::MakeLeafBlockPtr(ArrayIndex p_Index)
{
    return m_BlockManager->MakeLeafBlockPtr(p_Index);
}

//MainObjectBlock member functions

MainObjectBlockPtr::MainObjectBlock *MainObjectBlockPtr::operator->()
{
    m_MainObjectBlock.m_Block = 
    m_MainObjectBlock.m_BlockManager->MakeBlockResident(
    m_MainObjectBlock.m_BlockNumber);

    return &m_MainObjectBlock;
}


//LittlePointerBlockPtr member functions

LittlePointerBlock *LittlePointerBlockPtr::operator->()
{
    m_LittlePointerBlock.m_Block =
    m_LittlePointerBlock.m_BlockManager->MakeBlockResident(
    m_LittlePointerBlock.m_BlockNumber);

    m_LittlePointerBlock.m_ItemIndex = 
    &m_LittlePointerBlock.m_Block->m_QuantumBlockData.m_ItemIndex[0];

    m_LittlePointerBlock.SetLittlePointerArray();

    return &m_LittlePointerBlock;
}


//LeafBlockPtr member functions

LeafBlock *LeafBlockPtr::operator->()
{
    m_LeafBlock.m_Block =
    m_LeafBlock.m_BlockManager->MakeBlockResident(
    m_LeafBlock.m_BlockNumber);

    m_LeafBlock.m_ItemIndex = 
    &m_LeafBlock.m_Block->m_QuantumBlockData.m_ItemIndex[0];

    return &m_LeafBlock;
}


ArrayIndex QuantumBlock::AddItem(void *p_ItemAddress, ArrayIndex p_ElementSize,
    ObjectType p_Type, ArrayIndex p_Index)
{
    ArrayIndex StartingOffset;
    ArrayIndex LastItemOffset;
    char *DataFrom;
    char *DataTo;
    ArrayIndex DataLength;
    ItemIndex TempItemIndex;
    int ItemFound;
    ArrayIndex ItemNumber;
    ArrayIndex ItemCount;

    SetModified();

    ItemFound = FindUnusedItem(&m_ItemIndex[0], GetItemCount());

    ArrayIndex FreeSpace = CalculateFreeSpace();

    qfassert(FreeSpace > p_ElementSize);

    if (ItemFound >= 0) // we can reuse an item
        {
        m_ItemIndex[ItemFound].m_Type = p_Type;
        m_ItemIndex[ItemFound].m_Index = p_Index;

        StartingOffset = m_ItemIndex[ItemFound].m_Offset;
        ItemCount = GetItemCount();
        LastItemOffset = m_ItemIndex[ItemCount-1].m_Offset;
        DataFrom = m_Block->m_Data + BlockSize - LastItemOffset;
        DataTo = DataFrom - p_ElementSize;
        DataLength = LastItemOffset - StartingOffset;
        BlockMove(DataTo,DataFrom,DataLength);

        AdjustOffset(&m_ItemIndex[ItemFound],ItemCount-ItemFound,
            p_ElementSize);

        DataTo = m_Block->m_Data + BlockSize - m_ItemIndex[ItemFound].m_Offset;
        BlockMove(DataTo,p_ItemAddress,p_ElementSize);
        ItemNumber = ItemFound + 1;
        }
    else    // must make a new item
        {
        if (GetItemCount() == 0)
            TempItemIndex.m_Offset = p_ElementSize;
        else
            {
            LastItemOffset = m_ItemIndex[GetItemCount()-1].m_Offset;
            TempItemIndex.m_Offset = LastItemOffset + p_ElementSize;
            }
        DataTo = m_Block->m_Data + BlockSize - TempItemIndex.m_Offset;
        BlockMove(DataTo,p_ItemAddress,p_ElementSize);

        TempItemIndex.m_Type = p_Type;
        TempItemIndex.m_Index = p_Index;
        m_ItemIndex[GetItemCount()] = TempItemIndex;
        SetItemCount(GetItemCount()+1);
        ItemNumber = GetItemCount(); // 1-based
        }
    UpdateFreeSpace();
    return ItemNumber;
}

void QuantumBlock::DeleteItem(ArrayIndex p_ItemNumber)
{
    ArrayIndex StartingOffset;
    int i; // must be signed for termination condition
    ArrayIndex LastItemOffset;
    char *DataFrom;
    char *DataTo;
    ArrayIndex DataLength;
    ArrayIndex Length;
    SetModified();

    qfassert (p_ItemNumber > 0 && p_ItemNumber <= GetItemCount());
    
    p_ItemNumber --; /* make it zero-based */
    if (p_ItemNumber == GetItemCount() - 1) // it's the last one
        {
        m_ItemIndex[p_ItemNumber].m_Type = UNUSED_ITEM;
        for (i = GetItemCount() - 1; i >= 0; i --)
            {
            if (m_ItemIndex[i].m_Type == UNUSED_ITEM)
                {
                m_ItemIndex[i].m_Offset = 0;
                m_ItemIndex[i].m_Index = 0;
                m_ItemIndex[i].m_Type = 0;
                }
            else
                break;
            }
        SetItemCount(i+1);
        if (i == -1) // we've cleared out the whole block
            Clear();
        UpdateFreeSpace();
        return;
        }

    StartingOffset = m_ItemIndex[p_ItemNumber].m_Offset;
    if (p_ItemNumber == 0) /* the first one goes to the end of the block */
        Length = StartingOffset;
    else
        Length = StartingOffset - m_ItemIndex[p_ItemNumber-1].m_Offset;

    LastItemOffset = m_ItemIndex[GetItemCount()-1].m_Offset;
    DataFrom = m_Block->m_Data + BlockSize - LastItemOffset;
    DataTo = DataFrom + Length;
    DataLength = LastItemOffset - StartingOffset;
    BlockMove(DataTo,DataFrom,DataLength);

    AdjustOffset(&m_ItemIndex[p_ItemNumber+1],
        GetItemCount()-(p_ItemNumber+1), -1*Length);

    m_ItemIndex[p_ItemNumber].m_Type = UNUSED_ITEM;

    //Must set up correct offset for length calculation of next item
    if (p_ItemNumber == 0)
        m_ItemIndex[p_ItemNumber].m_Offset = 0;
    else
        m_ItemIndex[p_ItemNumber].m_Offset =
            m_ItemIndex[p_ItemNumber-1].m_Offset;

    UpdateFreeSpace();
}


ModifiableElement QuantumBlock::GetModifiableItem(ArrayIndex p_ItemNumber)
{
    ModifiableElement TempItem;
    ArrayIndex StartingOffset;
    ArrayIndex Length;
    char *StartingLocation;

    qfassert (p_ItemNumber > 0 && p_ItemNumber <= GetItemCount());
    p_ItemNumber --; /* make it zero-based */
    StartingOffset = m_ItemIndex[p_ItemNumber].m_Offset;
    StartingLocation = m_Block->m_Data + BlockSize - StartingOffset;
    if (p_ItemNumber == 0) /* the first one goes to the end of the block */
        Length = StartingOffset;
    else
        Length = StartingOffset - m_ItemIndex[p_ItemNumber-1].m_Offset;
    TempItem = ModifiableElement(Length, StartingLocation);

    return TempItem;
}

ItemIndex QuantumBlock::GetItemIndexEntry(ArrayIndex p_ItemNumber)
{
    ModifiableElement TempItem;

    qfassert (p_ItemNumber > 0 && p_ItemNumber <= GetItemCount());
    p_ItemNumber --; /* make it zero-based */

    return m_ItemIndex[p_ItemNumber];
}


AccessVector<Ulong> QuantumBlock::GetAccessVectorUlong(ArrayIndex p_ItemNumber)
{
    AccessVector<Ulong> TempItem;
    ArrayIndex StartingOffset;
    ArrayIndex Length;
    char* StartingLocation;
    Ulong* StartingUlongLocation;

    qfassert (p_ItemNumber > 0 && p_ItemNumber <= GetItemCount());
    p_ItemNumber --; /* make it zero-based */
    StartingOffset = m_ItemIndex[p_ItemNumber].m_Offset;
    StartingLocation = m_Block->m_Data + BlockSize - StartingOffset;
    if (p_ItemNumber == 0) /* the first one goes to the end of the block */
        Length = StartingOffset;
    else
        Length = StartingOffset - m_ItemIndex[p_ItemNumber-1].m_Offset;

    if (Length % sizeof(Ulong) != 0)
        Length = 0;
    else
        Length = Length / sizeof(Ulong);

    StartingUlongLocation = (Ulong*)StartingLocation;

    TempItem = AccessVector<Ulong>(Length, StartingUlongLocation);

    return TempItem;
}


ArrayIndex QuantumBlock::CalculateFreeSpace()
{
    int FreeSpace;
    ArrayIndex LastItemOffset;
    ArrayIndex ItemCount = GetItemCount();

    qfassert (ItemCount < NoItem);

    if (ItemCount == NoItem-1) // can't add any new items to this block
        return 0;

    FreeSpace = BlockSize - (Margin + sizeof(QuantumBlockHeader));

    if (GetItemCount() == 0)
       {
       // Make adjustment to mark this quantum as "available"
       FreeSpace = FreeSpaceConversion * AvailableQuantum;
       return(FreeSpace);
       }

    LastItemOffset = m_ItemIndex[GetItemCount()-1].m_Offset;
    FreeSpace -= LastItemOffset;
    FreeSpace -= sizeof(ItemIndex) * GetItemCount();

    if (FreeSpace < 0)
      FreeSpace = 0;

    if (FreeSpace >= FreeSpaceConversion * AvailableQuantum)
      return FreeSpaceConversion * AvailableQuantum - 1;

    return(FreeSpace);
}

bool QuantumBlock::CheckQuantumBlock()
{
    ArrayIndex i;
    ArrayIndex ItemCount = GetItemCount();
    ArrayIndex ItemLength;
    ArrayIndex StartingOffset;
    ArrayIndex StartingLocation;
    bool Result = true;

    for (i = 0; i < ItemCount; i ++)
        {
        if (m_ItemIndex[i].m_Offset > BlockSize)
            {
            Result = false;
            }
        if (m_ItemIndex[i].m_Type > BIG_ARRAY_HEADER)
            {
            Result = false;
            }
        if (m_ItemIndex[i].m_Index > 1000000)
            {
            Result = false;
            }
        }

    for (i = 0; i < ItemCount; i ++)
        {
        StartingOffset = m_ItemIndex[i].m_Offset;
        StartingLocation = BlockSize - StartingOffset;
        if (i == 0) /* the first one goes to the end of the block */
            ItemLength = StartingOffset;
        else
            ItemLength = StartingOffset - m_ItemIndex[i-1].m_Offset;
        if (ItemLength > BlockSize)
            {
            Result = false;
            }
        }

    return Result;
}


void QuantumBlock::DumpQuantumBlock()
{
    ArrayIndex i;
    ArrayIndex j;
    ArrayIndex ItemCount = GetItemCount();
    ArrayIndex ItemLength;
    ArrayIndex StartingOffset;
    ArrayIndex StartingLocation;
    unsigned int OutputData;

    cout << endl;
    cout << "Quantum block display: " << endl;
    cout << "Number of items: " << ItemCount << endl;
    cout << "Item index: " << endl;

    for (i = 0; i < ItemCount; i ++)
        {
        cout << i << ": ";
        cout << "Offset:" << m_ItemIndex[i].m_Offset << " ";
        cout << "Type: " << m_ItemIndex[i].m_Type << " ";
        cout << "Index: " << m_ItemIndex[i].m_Index << " ";
        cout << endl;
        }

    cout << endl;
    cout << "Item listing" << endl;

    for (i = 0; i < ItemCount; i ++)
        {
        StartingOffset = m_ItemIndex[i].m_Offset;
        StartingLocation = BlockSize - StartingOffset;
        if (i == 0) /* the first one goes to the end of the block */
            ItemLength = StartingOffset;
        else
            ItemLength = StartingOffset - m_ItemIndex[i-1].m_Offset;
        cout << "Item #: " << i << endl;
        cout << "Length: " << ItemLength << endl;
        cout << "StartingOffset: " << StartingOffset << endl;

        long old_options = cout.flags();
        cout.flags(ios::hex);

        ArrayIndex OutputCount = 0;
        for (j = StartingLocation; j < StartingLocation+ItemLength; j ++)
            {
            OutputData = (unsigned char)m_Block->m_Data[j];
            cout << setfill('0') << setw(2) << OutputData;
            OutputCount ++;
            if (OutputCount % 2 == 0)
                cout << " ";
            }
        cout << endl;

        cout.flags(old_options);
        }
    cout << endl;
    cout << endl;
    cout.flush();
}

FreeSpaceBlockPtr::FreeSpaceBlockPtr(ArrayIndex p_BlockNumber,
QuantumFile *p_BlockManager)
{
    m_FreeSpaceBlock.m_BlockNumber = p_BlockNumber;
    m_FreeSpaceBlock.m_BlockManager = p_BlockManager;
    m_FreeSpaceBlock.m_Block = 0;
}

FreeSpaceBlockPtr::FreeSpaceBlock::FreeSpaceBlock(ArrayIndex p_BlockNumber,
QuantumFile *p_BlockManager)
{
    m_BlockNumber = p_BlockNumber;
    m_BlockManager = p_BlockManager;
    m_Block = 0;
}

MainObjectBlockPtr::MainObjectBlockPtr(ArrayIndex p_BlockNumber,
QuantumFile *p_BlockManager)
{
    m_MainObjectBlock.m_BlockNumber = p_BlockNumber;
    m_MainObjectBlock.m_BlockManager = p_BlockManager;
    m_MainObjectBlock.m_Block = 0;
}


MainObjectBlockPtr::MainObjectBlock::MainObjectBlock(ArrayIndex p_BlockNumber,
QuantumFile *p_BlockManager)
{
    m_BlockNumber = p_BlockNumber;
    m_BlockManager = p_BlockManager;
    m_Block = 0;
}

BigPointerBlockPtr::BigPointerBlockPtr(ArrayIndex p_BlockNumber, QuantumFile *p_BlockManager)
{
    m_BigPointerBlock.m_BlockNumber = p_BlockNumber;
    m_BigPointerBlock.m_BlockManager = p_BlockManager;
    m_BigPointerBlock.m_Block = 0;
}

LittlePointerBlockPtr::LittlePointerBlockPtr(ArrayIndex p_BlockNumber, 
  QuantumFile *p_BlockManager)
{
    m_LittlePointerBlock.m_BlockNumber = p_BlockNumber;
    m_LittlePointerBlock.m_BlockManager = p_BlockManager;
    m_LittlePointerBlock.m_Block = 0;
}

LeafBlockPtr::LeafBlockPtr(ArrayIndex p_BlockNumber, QuantumFile *p_BlockManager)
{
    m_LeafBlock.m_BlockNumber = p_BlockNumber;
    m_LeafBlock.m_BlockManager = p_BlockManager;
    m_LeafBlock.m_Block = 0;
}

void BigPointerBlock::SetBigArrayHeader()
{
    ArrayIndex StartingOffset;
    char *StartingLocation;

    StartingOffset = m_ItemIndex[0].m_Offset;
    StartingLocation = m_Block->m_Data + BlockSize - StartingOffset;
    m_BigArrayHeader = (BigArrayHeader *)(StartingLocation);
}

void BigPointerBlock::SetBigPointerArray()
{
    ArrayIndex StartingOffset;
    ArrayIndex Length;
    char *StartingLocation;
    ArrayIndex ElementCount;

    StartingOffset = m_ItemIndex[1].m_Offset;
    StartingLocation = m_Block->m_Data + BlockSize - StartingOffset;
    Length = StartingOffset - m_ItemIndex[0].m_Offset;
    ElementCount = Length/sizeof(QuantumNumber);
    m_BigPointerArray = BigPointerArray(ElementCount,
        (QuantumNumber *)StartingLocation);
}

void BigPointerBlock::DumpQuantumBlock()
{
    ArrayIndex i;

    QuantumBlock::DumpQuantumBlock();

    cout << endl;
    cout << "Big pointer block display: " << endl << endl;

    if (m_ItemIndex[1].m_Offset == 0)
        {
        cout << "Block not set up yet." << endl;
        return;
        }

    cout << "Big array header: " << endl << endl;

    cout << "Element count: " << m_BigArrayHeader->m_ElementCount << endl;
    cout << "Max element count: " << m_BigArrayHeader->m_MaxElementCount << endl;
    cout << "Last quantum added to: " << m_BigArrayHeader->m_LastQuantumAddedTo << endl;

    cout << "Big pointer array: " << endl;
    ArrayIndex ItemCount = m_BigPointerArray.GetSize();
    cout << "Element count: " << ItemCount << endl;
    cout << "Big pointer array elements: " << endl;

    long old_options = cout.flags();
    cout.flags(ios::hex);
    for (i = 0; i < ItemCount; i ++)
        {
        cout << setfill('0') << setw(4) << m_BigPointerArray[i] << ' ';
        }
    cout.flags(old_options);

    cout << endl;
    cout << endl;
    cout.flush();
}


void LittlePointerBlock::SetLittlePointerArray()
{
    ArrayIndex StartingOffset;
    ArrayIndex Length;
    char *StartingLocation;
    ArrayIndex ElementCount;

    StartingOffset = m_ItemIndex[0].m_Offset;
    StartingLocation = m_Block->m_Data + BlockSize - StartingOffset;
    Length = StartingOffset;
    ElementCount = Length/sizeof(ItemReference);
    m_LittlePointerArray = LittlePointerArray(ElementCount,(ItemReference *)StartingLocation);
}

FreeSpaceBlockPtr QuantumFile::MakeFreeSpaceBlockPtr(ArrayIndex p_Index)
{
    return FreeSpaceBlockPtr(p_Index+m_FreeSpaceListStartingBlock, this);
}

MainObjectBlockPtr QuantumFile::MakeMainObjectBlockPtr(ArrayIndex p_Index)
{
    return MainObjectBlockPtr(p_Index+m_MainObjectStartingBlock, this);
}

LittlePointerBlockPtr QuantumFile::MakeLittlePointerBlockPtr(ArrayIndex p_Index)
{
// note: there is no quantum number 0, so we must correct the index
    return LittlePointerBlockPtr(p_Index+m_QuantumStartingBlock-1, this);
}

BigPointerBlockPtr QuantumFile::MakeBigPointerBlockPtr(ArrayIndex p_Index)
{
// note: there is no quantum number 0, so we must correct the index
    return BigPointerBlockPtr(p_Index+m_QuantumStartingBlock-1, this);
}


LeafBlockPtr QuantumFile::MakeLeafBlockPtr(ArrayIndex p_Index)
{
// note: there is no quantum number 0, so we must correct the index
    return LeafBlockPtr(p_Index+m_QuantumStartingBlock-1, this);
}

void QuantumFile::SetFreeSpace(ArrayIndex p_QuantumNumber, QFreeSpaceEntry p_FreeSpaceEntry)
{
    (*m_FreeSpaceArray)->Set(p_QuantumNumber, p_FreeSpaceEntry);
}

QFreeSpaceEntry QuantumFile::QGetFreeSpace(ArrayIndex p_QuantumNumber)
{
    return (*m_FreeSpaceArray)->Get(p_QuantumNumber);
}

QuantumNumber QuantumFile::FindEmptyBlock()
{
    return (*m_FreeSpaceArray)->FindEmptyBlock();
}


QuantumNumber QuantumFile::FindSpaceForItem(ObjectNumber p_ObjectNumber, 
    ArrayIndex p_ItemSize)
{
    return (*m_FreeSpaceArray)->FindSpaceForItem(p_ObjectNumber, p_ItemSize);
}

FreeSpaceBlockPtr::FreeSpaceBlockPtr()
{
}

FreeSpaceBlockPtr::FreeSpaceBlock::FreeSpaceBlock()
{
    m_Block = 0;
}

QFreeSpaceEntry FreeSpaceBlockPtr::FreeSpaceBlock::Get(ArrayIndex p_Index)
{
    return m_Block->m_FreeSpaceData[p_Index];
}

void FreeSpaceBlockPtr::FreeSpaceBlock::Set(ArrayIndex p_Index, QFreeSpaceEntry p_Value)
{
    m_BlockManager->SetModified(m_BlockNumber);
    m_Block->m_FreeSpaceData[p_Index] = p_Value;
}

MainObjectBlockPtr::MainObjectBlockPtr()
{
}

MainObjectBlockPtr::MainObjectBlock::MainObjectBlock()
{
    m_Block = 0;
}

MainObjectEntry MainObjectBlockPtr::MainObjectBlock::Get(ArrayIndex p_Index)
{
    return m_Block->m_MainObjectData[p_Index];
}

void MainObjectBlockPtr::MainObjectBlock::Set(ArrayIndex p_Index, MainObjectEntry p_Value)
{
    m_BlockManager->SetModified(m_BlockNumber);
    m_Block->m_MainObjectData[p_Index] = p_Value;
}

BigPointerBlockPtr::BigPointerBlockPtr()
{
}

LittlePointerBlockPtr::LittlePointerBlockPtr()
{
}

LeafBlockPtr::LeafBlockPtr()
{
}

ArrayIndex BigPointerBlock::GetBigArrayMaxElementCount()
{
    return m_BigArrayHeader->m_MaxElementCount;
}

ArrayIndex BigPointerBlock::GetBigArrayElementCount()
{
    return m_BigArrayHeader->m_ElementCount;
}

void BigPointerBlock::SetBigArrayElementCount(ArrayIndex p_ElementCount)
{
    if (m_BigArrayHeader->m_ElementCount != p_ElementCount)
        {
        m_BigArrayHeader->m_ElementCount = p_ElementCount;
        SetModified();
        }
}
QuantumNumber BigPointerBlock::GetBigArrayElement(ArrayIndex p_Index)
{
    return m_BigPointerArray[p_Index];
}

void BigPointerBlock::SetBigArrayElement(ArrayIndex p_Index, QuantumNumber p_Element)
{
    m_BigPointerArray[p_Index] = p_Element;
    SetModified();
}

QuantumNumber BigPointerBlock::GetLastQuantumAddedTo()
{
    return m_BigArrayHeader->m_LastQuantumAddedTo;
}

void BigPointerBlock::SetLastQuantumAddedTo(QuantumNumber p_QuantumNumber)
{
    m_BigArrayHeader->m_LastQuantumAddedTo = p_QuantumNumber;
    SetModified();
}


void LittlePointerBlock::ClearLittleArray()
{
    ItemReference NullItemReference;
    ArrayIndex i;

    NullItemReference.SetItemNumber(NoItem);
    NullItemReference.SetQuantumNumber(NoQuantum);
    for (i = 0; i < GetLittleArraySize(); i ++)
        m_LittlePointerArray[i] = NullItemReference;
}

void QuantumBlock::SetModified()
{
    m_BlockManager->SetModified(m_BlockNumber);
}

ArrayIndex QuantumBlock::AddItem(ModifiableElement p_Element, ObjectType p_Type, 
    ArrayIndex p_Index)
{
    ArrayIndex ItemSize = p_Element.GetSize();
    void *ItemAddress = p_Element.GetDataAddress();

    return AddItem(ItemAddress, ItemSize, p_Type, p_Index);
}

void QuantumBlock::SetItemCount(ArrayIndex p_NewItemCount)
{
    m_Block->m_QuantumBlockData.m_QuantumBlockHeader.m_ItemCount = p_NewItemCount;
    SetModified();
}

void QuantumBlock::SetQuantumType(ObjectType p_QuantumType)
{
    m_Block->m_QuantumBlockData.m_QuantumBlockHeader.m_QuantumType = p_QuantumType;
    SetModified();
}

ObjectType QuantumBlock::GetQuantumType()
{
    return m_Block->m_QuantumBlockData.m_QuantumBlockHeader.m_QuantumType;
}


void QuantumBlock::SetMainObjectNumber(ObjectNumber p_MainObjectNumber)
{
    m_Block->m_QuantumBlockData.m_QuantumBlockHeader.m_MainObjectNumber = p_MainObjectNumber;
    SetModified();
}

void QuantumBlock::UpdateFreeSpace()
{
    QFreeSpaceEntry SpaceAvailable;

    SpaceAvailable.m_ObjectNumber = GetMainObjectNumber();
    SpaceAvailable.m_FreeSpaceCode =
        FreeSpaceCode(CalculateFreeSpace()/FreeSpaceConversion);
    m_BlockManager->SetFreeSpace(
        m_BlockNumber-m_BlockManager->GetQuantumNumberAdjustment(),
        SpaceAvailable);
    SetModified();
}

FreeSpaceCode QuantumBlock::GetMainObjectNumber()
{
    return m_Block->m_QuantumBlockData.m_QuantumBlockHeader.m_MainObjectNumber;
}


void QuantumBlock::Clear()
{
    memset(m_Block,0,BlockSize);
    SetModified();
}

ItemReference LittlePointerBlock::GetLittleArrayElement(ArrayIndex p_Index)
{
    return m_LittlePointerArray[p_Index];
}

void LittlePointerBlock::SetLittleArrayElement(ArrayIndex p_Index, 
    ItemReference p_ItemReference)
{
    m_BlockManager->SetModified(m_BlockNumber);
    m_LittlePointerArray[p_Index] = p_ItemReference;
}

ArrayIndex LittlePointerBlock::GetLittleArraySize()
{
    return m_LittlePointerArray.GetSize();
}

ArrayIndex QuantumBlock::GetItemCount()
{
    ArrayIndex ItemCount = m_Block->m_QuantumBlockData.m_QuantumBlockHeader.m_ItemCount;

    qfassert (ItemCount < NoItem && ItemCount != BadBlockNumber);
    
    return ItemCount;
}

FreeSpaceBlockPtr::FreeSpaceBlock *FreeSpaceBlockPtr::operator->()
{
    m_FreeSpaceBlock.m_Block = 
    m_FreeSpaceBlock.m_BlockManager->MakeBlockResident(
    m_FreeSpaceBlock.m_BlockNumber);

    return &m_FreeSpaceBlock;
}

BigPointerBlock *BigPointerBlockPtr::operator->()
{
    m_BigPointerBlock.m_Block =
    m_BigPointerBlock.m_BlockManager->MakeBlockResident(
    m_BigPointerBlock.m_BlockNumber);

    m_BigPointerBlock.m_ItemIndex = 
    &m_BigPointerBlock.m_Block->m_QuantumBlockData.m_ItemIndex[0];

    m_BigPointerBlock.SetBigArrayHeader();
    m_BigPointerBlock.SetBigPointerArray();

    return &m_BigPointerBlock;
}


BaseBlockPtr::BaseBlockPtr()
{
    m_BlockNumber = 0;
    m_BlockManager = 0;
}





