//
// flatfile.h -- This file contains the class definations for:
//  CFlatFile
//
// Created:
//   Sep 3, 1996 -- Alex Wetmore (awetmore)
// Changes:
//   May 7, 1998 -- Alex Wetmore (awetmore)
//      -- Modify for use in NNTP.  Remove sorting features, file handle
//         cache, etc.
//   Oct 23,1998 -- Kangrong Yan ( kangyan )
//      -- Added integrity flag
//

#ifndef __FLATFILE_H__
#define __FLATFILE_H__

#include <windows.h>
#include <stdio.h>
#include <writebuf.h>

//
// size of the read buffer used for GetFirstRecord/GetNextRecord()
//
#define FF_BUFFER_SIZE 8192

//
// maximum record size
//
#define MAX_RECORD_SIZE 4096

// do a automatic compaction if there are more then 10 deleted records and
// the ratio of records to deleted records is less then 10
#define FF_COMPACTION_MIN_DELETED_RECORDS 10
#define FF_COMPACTION_MIN_TOTAL_RECORDS 100
#define FF_COMPACTION_RATIO 10

//
// Integrity flag values
//
#define FF_FILE_GOOD    0x0000FFFF
#define FF_FILE_BAD     0x00010000

//
// file extensions for flat files
//
#define NEW_FF_EXT ".tmp"       // extension for new flatfile as its built
#define BAK_FF_EXT ".bak"       // extension for an old backup flatfile
#define FF_IDX_EXT ".idx"       // extension for an index file

#pragma pack(push, flatfile)
#pragma pack(1)

//
// the structure for the header of the file
//
typedef struct {
    DWORD   dwSignature;                    // file signature
	DWORD	dwFlags;						// file flags
} FLATFILE_HEADER;

#define FF_FLAG_COMPACT		0x01

#define FLATFILE_SIGNATURE (DWORD)'__fF'

//
// the record structure for the data file
//
// the size of it is RECORD_HEADER_SIZE + cData;
//
typedef struct {
    BOOL    fDeleted;                       // is this deleted?
    DWORD   cData;                          // length of the data
    BYTE    pData[MAX_RECORD_SIZE];         // data
} RECORD;

typedef struct {
    BOOL    fDeleted;                       // is this deleted?
    DWORD   cData;                          // length of the data
} RECORDHDR;

#define RECORD_HEADER_SIZE sizeof(RECORDHDR)

#pragma pack(pop, flatfile)

//
// A function of this type will be called whenever a record's offset 
// changes in the flatfile.  It is used to keep the owner up to date 
// on record offsets, so that the owner can make quick Delete's
//
typedef void (*PFN_OFFSET_UPDATE)(void *pContext, BYTE *pData, DWORD cData, DWORD iNewOffset);

class CFlatFile {
    public:

        friend class CFlatFileWriteBuf;
        
        CFlatFile(LPSTR szFilename, 
				  LPSTR szExtension, 
				  void *pContext,
				  PFN_OFFSET_UPDATE pfnOffsetUpdate,
				  DWORD dwSignature = FLATFILE_SIGNATURE,
				  BOOL fClear = FALSE, 
				  DWORD dwFileFlags = 0);
        ~CFlatFile();

		// insert a new record into the file
        HRESULT InsertRecord(LPBYTE pData, DWORD cData, DWORD *piOffset = NULL, DWORD dwVer = 0);

		// delete a record from the file
        HRESULT DeleteRecord(DWORD iOffset);

		// compact out any deleted records in the file
        HRESULT Compact();

		// get the first record in the file
        HRESULT GetFirstRecord(LPBYTE pData, 
							   DWORD *cData,
            				   DWORD *piByteOffset = NULL,
            				   DWORD *pdwVer = NULL );

		// get the next record in the file
        HRESULT GetNextRecord(LPBYTE pData, 
							  DWORD *cData,
            				  DWORD *piByteOffset = NULL,
            				  DWORD *pdwVer = NULL);

        // delete everything in the file
        void DeleteAll();

        // Dirty the integrity flag
        HRESULT DirtyIntegrityFlag();

        // Set the integrity flag
        HRESULT SetIntegrityFlag();

        // Is the file in good integrity ?
        BOOL  FileInGoodShape();

        // Enable the write buffer
        VOID EnableWriteBuffer( DWORD cbBuffer );

        // Check to see if the file has been opened
        BOOL IsFileOpened();

    private:
        //
        // open/close a file.  
        //
        // because this uses cached file handles, the position of the file
        // should not be assumed
        //
        HRESULT OpenFile(LPSTR szFilename = NULL,
                         DWORD dwOpenMode = OPEN_ALWAYS, 
						 DWORD dwFlags = 0);

		//
		// close the file handle
		//
		void CloseFile();

        //
        // set and get the file header
        //
        HRESULT SetFileHeader(FLATFILE_HEADER *pHeader);
        HRESULT GetFileHeader(FLATFILE_HEADER *pHeader);

        //
        // read the next chunk of the file into the temporary buffer
        // used by GetFirstRecord/GetNextRecord()
        //
        HRESULT ReadNextNBytes(LPBYTE pData, DWORD cData);
        HRESULT ReadNBytesFrom(LPBYTE pData, DWORD cData, DWORD iOffset, DWORD *pcDidRead = NULL);
        // iOffset can be set to infinite to append
        HRESULT WriteNBytesTo(LPBYTE pData, 
							  DWORD cData,
            				  DWORD *piOffset = NULL,
            				  DWORD iOffset = INFINITE,
            				  DWORD *pcDidWrite = NULL);
            				  
        HRESULT CFlatFile::WriteNBytesToInternal(
                                 LPBYTE pData,
								 DWORD cData,
								 DWORD *piOffset,
                              	 DWORD iOffset,
								 DWORD *pcDidWrite);

        HRESULT ReloadReadBuffer();

        //
		// the file handle for this file
        //
        HANDLE  m_hFile;

        //
        // flags for creating the file with
        //
        DWORD   m_dwFileFlags;

        //
        // filename for the flat file
        //
        char    m_szFilename[FILENAME_MAX];
        char    m_szBaseFilename[FILENAME_MAX];

        //
        // current read buffer
        //
        BYTE    m_pBuffer[FF_BUFFER_SIZE];

        //
        // current offset inside the buffer
        //
        DWORD   m_iBuffer;

        //
        // offset of the read buffer in the file
        //
        DWORD   m_iFile;

        //
        // size of the read buffer (zero means its not valid)
        //
        DWORD   m_cBuffer;

        //
        // clear the file on the next open
        //
        BOOL    m_fClearOnOpen;

        //
        // the number of deleted records in the file.  this is first 
		// computed by FindFirst/FindNext and then kept updated by
		// DeleteRecord and DeleteRecordAtOffset
        //
        DWORD   m_cDeletedRecords;

        //
        // the number of records in the file.  this is first 
		// computed by FindFirst/FindNext and then kept updated by
		// InsertRecord
        //
        DWORD   m_cRecords;

		//
		// context passed into callback functions
		//
		void *m_pContext;

		//
		// Whether the file is open
		//
		BOOL m_fOpen;

		//
		// The write buffer
		//
		CFlatFileWriteBuf m_wbBuffer;

		//
		// function to call when an items offset changes in the flatfile
		//
		PFN_OFFSET_UPDATE m_pfnOffsetUpdate;

		// 
		// signature for the file
		//
		DWORD m_dwSignature;
};

#define ret(__rc__) { /* TraceFunctLeave(); */ return(__rc__); }
#define retEC(__ec__, __rc__) { SetLastError(__ec__); /* TraceFunctLeave(); */ return(__rc__); }

#endif