////////////////////////////////////////////////////////////////////////////////
// Win32/MappedFile.h -- this file is part of the Emulator Developers Kit
// available at http://ourworld.compuserve.com/homepages/pc64/develop.htm

class MappedFile : public File {

  byte* pbInitialStart;
  int iInitialSize;
  int iExpandGranularity;
  HANDLE hMapping;
  byte* pbStart;
  byte* pbPosition;
  const byte* pbEnd;
  const byte* pbReserved;
  void (*pfniOnMove)(int);

  global void ReadAfterEndOfFile();

public:

  // constructor
  global MappedFile() {
    dwFlagsAndAttributes = FILE_FLAG_RANDOM_ACCESS;
    pbInitialStart = NULL;
    iInitialSize = 0;
    iExpandGranularity = 1;
    hMapping = NULL;
    pbStart = NULL;
    pbPosition = NULL;
    pbEnd = NULL;
    pbReserved = NULL;
    pfniOnMove = NULL;
  }

  // destructor
  global void Close();
  virtual ~MappedFile() {
    Close();
  }

  // properties
  inline void SetInitialStart(byte* pbNewInitialStart) {
    pbInitialStart = pbNewInitialStart;
  }
  inline void SetInitialSize(int iNewInitialSize) {
    iInitialSize = iNewInitialSize;
  }
  inline void SetExpandGranularity(int iNewExpandGranularity) {
    iExpandGranularity = iNewExpandGranularity;
  }
  inline void SetOnMove(void (*pfniNewOnMove)(int)) {
    pfniOnMove = pfniNewOnMove;
  }
  global void SetName(const char* pcNewName);
  inline void SetSize(int iNewSize) {
    SetEnd(pbStart + iNewSize);
  }
  inline int GetSize() {
    if (pbPosition > pbEnd) {
      pbEnd = pbPosition;
    }
    return pbEnd - pbStart;
  }
  inline void SetPosition(int iNewPosition) {
    if (pbPosition > pbEnd) {
      pbEnd = pbPosition;
    }
    byte* pbNewPosition = pbStart + iNewPosition;
    if (pbNewPosition > pbReserved) {
      SetReserved(pbNewPosition);
    }
    pbPosition = pbNewPosition;
  }
  inline int GetPosition() {
    return pbPosition - pbStart;
  }

  // access functions
  inline void Read(void* pBuffer, int iCount) {
    byte* pbNewPosition = pbPosition + iCount;
    if (pbNewPosition > pbEnd) {
      ReadAfterEndOfFile();
    }
    memcpy(pBuffer, pbPosition, iCount);
    pbPosition = pbNewPosition;
  }
  inline byte Read1() {
    byte* pbNewPosition = pbPosition + 1;
    if (pbNewPosition > pbEnd) {
      ReadAfterEndOfFile();
    }
    byte b = *(byte*)pbPosition;
    pbPosition = pbNewPosition;
    return b;
  }
  inline word Read2() {
    byte* pbNewPosition = pbPosition + 2;
    if (pbNewPosition > pbEnd) {
      ReadAfterEndOfFile();
    }
    word w = *(word*)pbPosition;
    pbPosition = pbNewPosition;
    return w;
  }
  inline dword Read4() {
    byte* pbNewPosition = pbPosition + 4;
    if (pbNewPosition > pbEnd) {
      ReadAfterEndOfFile();
    }
    dword dw = *(dword*)pbPosition;
    pbPosition = pbNewPosition;
    return dw;
  }
  inline void Write(const void* pBuffer, int iCount) {
    byte* pbNewPosition = pbPosition + iCount;
    if (pbNewPosition > pbReserved) {
      SetReserved(pbNewPosition);
    }
    memcpy(pbPosition, pBuffer, iCount);
    pbPosition = pbNewPosition;
  }
  inline void Write1(byte b) {
    byte* pbNewPosition = pbPosition + 1;
    if (pbNewPosition > pbReserved) {
      SetReserved(pbNewPosition);
    }
    *(byte*)pbPosition = b;
    pbPosition = pbNewPosition;
  }
  inline void Write2(word w) {
    byte* pbNewPosition = pbPosition + 2;
    if (pbNewPosition > pbReserved) {
      SetReserved(pbNewPosition);
    }
    *(word*)pbPosition = w;
    pbPosition = pbNewPosition;
  }
  inline void Write4(dword dw) {
    byte* pbNewPosition = pbPosition + 4;
    if (pbNewPosition > pbReserved) {
      SetReserved(pbNewPosition);
    }
    *(dword*)pbPosition = dw;
    pbPosition = pbNewPosition;
  }

  // direct access
  inline byte* GetStart() {
    return pbStart;
  }
  inline void SetEnd(const byte* pbNewEnd) {
    if (pbNewEnd > pbReserved) {
      SetReserved(pbNewEnd);
    }
    pbEnd = pbNewEnd;
    if (pbPosition > pbEnd) {
      pbPosition = (byte*)pbEnd;
    }
  }
  inline const byte* GetEnd() {
    if (pbPosition > pbEnd) {
      pbEnd = pbPosition;
    }
    return pbEnd;
  }
  global void SetReserved(const byte* pbNewReserved);
  inline const byte* GetReserved() {
    return pbReserved;
  }
  global int Replace(byte* pbOld, int iOldSize, const byte* pbNew, int iNewSize);
};
