#include "stdafx.h"
//#include <stdio.h>

#define StreamName           L"ResetTable"


ULONG CXResetData::BinarySearch(
                        LPRESETREC	   pTbl,
                        ULONG		   ulStart,
                        ULONG		   ulEnd,
                        ULARGE_INTEGER uliKey
                        )
{
#ifdef _DEBUG
    static int cLoop;  // BugBug! This will fail in multithreaded situations!
    cLoop++;
    RonM_ASSERT(cLoop < 20);
#endif

	if (ulStart == ulEnd)
	{
		DEBUGCODE(cLoop = 0;)
		return ulStart;
	}
	else
    if (ulStart == (ulEnd -1))
	{
		#ifdef _DEBUG
		cLoop = 0;
		#endif	
		
		if (CULINT(uliKey) >= CULINT(ulEnd * m_ulBlockSize))
			return ulEnd;
		else
			return ulStart;
	}
	
    ULONG ulMid = (ulEnd + ulStart) / 2;

    if (CULINT(uliKey) <= CULINT(ulMid * m_ulBlockSize))
    {
        return BinarySearch(pTbl, ulStart, ulMid, uliKey);
    }
    else
    {
        return BinarySearch(pTbl, ulMid, ulEnd, uliKey);
    }
}

ULONG CXResetData::FindRecord(
                              ULARGE_INTEGER uliOffset, 
                              ULARGE_INTEGER *puliXOffset, 
                              BOOL  *pfLastRecord
)
{
	ULONG ulRecNum;
	
	if (m_cFillRecs)
	{
		ulRecNum = BinarySearch(m_pSyncTbl, 0, m_cFillRecs - 1, uliOffset);
		*puliXOffset = (m_pSyncTbl + ulRecNum)->uliXOffset;
		*pfLastRecord = (ulRecNum == (m_cFillRecs - 1));
	}
    return ulRecNum;
}

BOOL CXResetData::FGetRecord(
                       ULONG			ulRecNum, 
                       ULARGE_INTEGER   *puliOffset, 
                       ULARGE_INTEGER   *puliXOffset, 
                       BOOL				*pfLastRecord 
)
{
    if (ulRecNum >= m_cFillRecs)
        return FALSE;

    *puliOffset =  CULINT(ulRecNum * m_ulBlockSize).Uli();
    *puliXOffset = (m_pSyncTbl + ulRecNum)->uliXOffset;
    *pfLastRecord = (ulRecNum == (m_cFillRecs - 1));
    return TRUE;
}

HRESULT CXResetData::DeleteRecord(ULONG ulRecNum)
{
	HRESULT hr = NO_ERROR;

	//Verify we are deleting valid record
    if (ulRecNum < m_cFillRecs)
    {
		m_cFillRecs--;   
		m_cEmptyRecs++;
	}
	else
	{
		hr = E_FAIL;
	}

	return hr;
}

HRESULT CXResetData::AddRecord(ULARGE_INTEGER  uliOffset, ULARGE_INTEGER  uliXOffset)
{
    if (m_cEmptyRecs <= 0)
    {
        //double the size of reset table for future use
        LPRESETREC prec		  = m_pSyncTbl;

        ULONG	   nTotalRecs = (m_cFillRecs ? m_cFillRecs : 64) * 2;
        
		if (m_pSyncTbl = New sResetRec[nTotalRecs])
        {
			//copy old table to newly created table with bigger size
			if (prec)
			{
				memCpy((LPBYTE)m_pSyncTbl, (LPBYTE)prec, m_cFillRecs * sizeof(sResetRec));
				delete prec;
			}
			m_cEmptyRecs = nTotalRecs - m_cFillRecs;
        }
        else
        {
			m_pSyncTbl = prec;
            return STG_E_INSUFFICIENTMEMORY;
        }
    }
    
    LPRESETREC prec  = m_pSyncTbl + m_cFillRecs;
    prec->uliXOffset = uliXOffset;

    m_cFillRecs++;
    m_cEmptyRecs--;

    m_fDirty = TRUE;

    return NO_ERROR;
}

CXResetData::CXResetData(
                         /*IStorage *pstg*/
                         )
{
    m_fDirty		= FALSE;
    m_pStm			= NULL;
	m_pStg			= NULL;
    m_cFillRecs		= 0;
    m_cEmptyRecs	=0;
    m_pSyncTbl		= NULL;

}

CXResetData::~CXResetData()
{
    if (m_pSyncTbl)
    {
        delete m_pSyncTbl;
        m_pSyncTbl = NULL;
    }
    
    if (m_pStm)
        m_pStm->Release();
	
	if (m_pStg)
		m_pStg->Release();
}

HRESULT CXResetData::InitResetTable(IStorage		*pStg,
								    ULARGE_INTEGER *puliVSpaceSize,
								    ULARGE_INTEGER *puliTxSpaceSize,
									ULONG			ulBlockSize)
{
	m_pStg = pStg;
	m_pStg->AddRef();
	
	HRESULT hr = NO_ERROR;

	*puliVSpaceSize  = CULINT(0).Uli();
	*puliTxSpaceSize = CULINT(0).Uli();
	m_ulBlockSize = ulBlockSize;

    if (SUCCEEDED(hr = GetResetTblStream(pStg, &m_pStm)))
    {
        sHeader header;
		ULONG	cbBytesRead = 0;

		int hr = m_pStm->Read((LPVOID)&header, sizeof(sHeader), &cbBytesRead);
       
		if (SUCCEEDED(hr) && (cbBytesRead == sizeof(sHeader)))
        {
			*puliVSpaceSize = header.uliVSpaceSize;
			*puliTxSpaceSize = header.uliTxSpaceSize;
			
		    m_cFillRecs = header.cRecs;
            if (m_cFillRecs > 0)
            {
                //allocate reset table with record 
                //count which is double of m_cFillRecs.
                ULONG nTotalRecs = m_cFillRecs * 2;
                if (m_pSyncTbl = New sResetRec[nTotalRecs])
                {
                    m_cEmptyRecs = nTotalRecs - m_cFillRecs;
    
					LARGE_INTEGER dliB;
					ULARGE_INTEGER dliBFinal;

					dliB.HighPart = 0;
					dliB. LowPart = header.cbSize;

					dliBFinal.HighPart = 0;
					dliBFinal. LowPart = 0;
  			
					if (SUCCEEDED(hr = m_pStm->Seek(dliB, STREAM_SEEK_SET, &dliBFinal)))
					{
						if (header.dwVerInfo == 1) 
						{
							sResetRecV1 sRecV1;
							ULONG cb;
							for (int iRec = 0; SUCCEEDED(hr) && (iRec < m_cFillRecs); iRec++)
							{
								hr = m_pStm->Read((LPVOID)&sRecV1, 
												sizeof(sResetRecV1), 
												&cb);
								cbBytesRead += cb;
								(m_pSyncTbl + iRec)->uliXOffset = sRecV1.uliXOffset;
							}
						}
						else if (header.dwVerInfo == LZX_Current_Version) 
						{
							hr = m_pStm->Read((LPVOID)(m_pSyncTbl), 
											m_cFillRecs * sizeof(sResetRec), 
											&cbBytesRead);
						}//read
                    }//seek
                }// new
				else
				{
					hr = STG_E_INSUFFICIENTMEMORY;
				}
            }//no. of entries in table > 0
        }//read header
    }//open reset table stream
	
	return hr;
}

HRESULT CXResetData::DumpStream(IStream *pTempStrm, LPSTR pFileName)
{
	HRESULT hr = NO_ERROR;
#if 0 //test code
	FILE *file = fopen(pFileName, "w" );

	if (file != NULL)
	{
		RonM_ASSERT(pTempStrm != NULL);
		HRESULT	hr = NO_ERROR;
		BYTE	lpBuf[2048];

		if (SUCCEEDED(hr = pTempStrm->Seek(CLINT(0).Li(), STREAM_SEEK_SET, 0)))
		{
			ULONG		   cbToRead = sizeof(lpBuf);
			ULONG		   cbWritten;
			ULONG		   cbRead = cbToRead;
				
			while (SUCCEEDED(hr) && (cbRead == cbToRead))
			{
				if (SUCCEEDED(hr = pTempStrm->Read(lpBuf, cbToRead, &cbRead)))
				{
					cbWritten = fwrite(lpBuf, sizeof(BYTE), cbRead, file);
					RonM_ASSERT(cbRead == cbWritten);
					if (cbRead != cbWritten)
						hr = E_FAIL;
				}//ReadAt
			}//while

		}//seek
 
		fclose(file);
	}
	else
		hr = E_FAIL;

#endif //end test code
return hr;
}

HRESULT CXResetData::CommitResetTable(ULARGE_INTEGER uliVSpaceSize,
								   ULARGE_INTEGER  uliTxSpaceSize)
{
    RonM_ASSERT(m_pStm);
	HRESULT hr = NO_ERROR;

	if (m_fDirty)
    {
		sHeader header;
        
        //latest version
        header.dwVerInfo			= LZX_Current_Version;
        header.cRecs				= m_cFillRecs;
        header.cbSize				= sizeof(sHeader);
		header.cbRecSize			= sizeof(sResetRec);
		header.uliVSpaceSize		= uliVSpaceSize;
		header.uliTxSpaceSize		= uliTxSpaceSize;
		header.ulBlockSize			= m_ulBlockSize;
		header.unused				= 0;
        
		ULONG cbWritten;
		
		LARGE_INTEGER dliB;
		ULARGE_INTEGER dliBFinal;
        
		dliB.HighPart = 0;
		dliB. LowPart = 0;

		dliBFinal.HighPart = 0;
		dliBFinal. LowPart = 0;

		if (SUCCEEDED(hr = m_pStm->Seek(dliB, STREAM_SEEK_SET, &dliBFinal)))
		{
			hr = m_pStm->Write((LPVOID)&header, sizeof(header), &cbWritten);
			if (SUCCEEDED(hr) && (cbWritten == sizeof(header)))
			{
				if (SUCCEEDED(hr = m_pStm->Write((LPVOID)m_pSyncTbl, 
									sizeof(sResetRec) * m_cFillRecs, 
									&cbWritten)))
				{
					if (cbWritten != sizeof(sResetRec) * m_cFillRecs)
						hr = E_FAIL;
					else
						m_fDirty = FALSE;

				}//write table
			}//write header
		}//seek
    }//if table dirty
    
	//test code
#ifdef _DEBUG
	DumpStream(m_pStm, "c:\\dbg.tbl");
#else
	DumpStream(m_pStm, "c:\\rel.tbl");
#endif
	return hr;
}

HRESULT CXResetData::GetResetTblStream(IStorage *pStg, IStream **ppStm)
{
	HRESULT hr;
	if (STG_E_FILENOTFOUND == (hr = pStg->OpenStream(StreamName, 
										0, 
										STGM_READWRITE | STGM_SHARE_EXCLUSIVE| STGM_DIRECT, 
										0, 
										ppStm)))
	{
		hr = pStg->CreateStream(StreamName, 
								STGM_READWRITE | STGM_SHARE_EXCLUSIVE 
                                                    | STGM_DIRECT
                                                    | STGM_CREATE, 
								0, 
								0, 
								ppStm);
	}//open stream

	return hr;
}