////////////////////////////////////////////////////////////////////////////////
// MemoryBlock.h
// Represent inhomogeneous data in memory, e.g. a file
// Copyright Bjoern Ganster
////////////////////////////////////////////////////////////////////////////////

#ifndef MemoryBlock__H
#define MemoryBlock__H

#include "BGBase.h"

#define FILE_NUMBER FILE_NO_MEMORYBLOCK_H

////////////////////////////////////////////////////////////////////////////////

class MemoryBlock: public VirtualClass {
public:
	// Default constructor
	MemoryBlock();

	// Construct and reserve memory
	MemoryBlock(size_t newSize);

	// Clone constructor
	MemoryBlock(void* newBuf, size_t newSize, bool freeLater = true);

	// Destructor
	~MemoryBlock() OVERRIDE;

	// Clear contents
	void clear();

	// Resize MemoryBlock
	bool resize(size_t newSize);

	// Get pointer into the buffer
	// Buffer is resized if necessary, the function returns nullptr if resizing fails
	template <typename T>
	T* getPtr (size_t offset = 0, bool mayEnlarge = true)
	{
		SET_SCOPE;
		if (mBuf == nullptr) {
			RETURN nullptr;
		}
		if (offset + sizeof(T) > mSize) {
			if (mayEnlarge) {
				if (!resize(offset+sizeof(T))) {
					RETURN nullptr;
				}
			} else {
				RETURN nullptr;
			}
		}
		RETURN (T*)(mBuf+offset);
	}
	template <typename T>
	const T* getPtr (size_t offset = 0) const
	{
		SET_SCOPE;
		if (offset+sizeof(T) <= mSize && mBuf != nullptr) {
			RETURN (T*)(mBuf+offset);
		} else {
			RETURN nullptr;
		}
	}

	// Read access
	template <typename T>
	T get(size_t offset) const
	{
		SET_SCOPE;
		if (offset + sizeof(T) <= mSize && mBuf != nullptr) {
			RETURN *(T*)(mBuf+offset);
		} else {
			RETURN 0;
		}
	}

	// Set value in the buffer, enlarging it when necessary
    template <typename T> 
    bool set(size_t offset, const T& value)
    {
		SET_SCOPE;
        T* p = getPtr<T>(offset);
        if (p != nullptr) {
            *p = value;
            RETURN true;
        } else {
            RETURN false;
        }
    }

    // Get size
    size_t getSize () const;

    // Load from file
    bool loadFromFile (const PathChar* FN);

    // Save to file
    bool saveToFile (const char* FN) const
    {
    	return ::saveToFile((const char*) mBuf, mSize, FN);
    }
    #ifdef _WIN32
    bool saveToFile (const wchar_t* FN) const
    {
    	return ::saveToFile((const char*) mBuf, mSize, FN);
    }
    #endif

    // Copy data into the buffer, enlarging it when necessary
    // todo: add flag that controls whether the buffer may be enlarged ...
    bool write(size_t offset, const void* _buf, size_t count);

    // Append
    void append(const void* buf, size_t bufSize)
    {
        size_t offset = mSize;
        resize (mSize+bufSize);

        if (buf != nullptr) {
            write(offset, buf, bufSize);
        } else {
            // Attempt to copy from null - write zero bytes instead ...
            // For example, this can happen when the empty string is passed as a null pointer!
            fill(offset, bufSize, 0);
        }
    }

    // Replace all occurences of old with new string
    size_t replaceAll (const char* oldStr, const char* newStr);

    // Fill block with byte
    bool fill(size_t offset, size_t count, char byte);

    // Read data
    size_t read(size_t offset, size_t byteCount, void* data) const;

	void dump(size_t offset, size_t count);

	// Scan file for end of this line and the start of the next line
	template <typename C>
	bool findEOL(size_t offset, size_t& EOL, size_t& nextLineOffset)
	{
		bool result = false;
		const C* chars = (C*)(mBuf);
		size_t cc = mSize / sizeof(C);
		while(offset < cc && chars[offset] != 0x0d && chars[offset] != 0x0a) {
			++offset;
			result = true;
		}

		EOL = offset;

		while(offset < cc && (chars[offset] == 0x0d || chars[offset] == 0x0a)) {
			++offset;
			result = true;
		}

		nextLineOffset = offset;

		return result;
	}

	// xor this and other bytewise
	bool xorData(const MemoryBlock& other);

	// Copy/assignment operator
	void operator=(const MemoryBlock& other);

protected:
    unsigned char* mBuf;
    size_t mSize;
    bool mNeedToFreeMem;
};

// Encrypt the file given by fn1 using the file given by fn2
bool xorEncrypt(const PathChar* fn1, const PathChar* fn2);

#undef FILE_NUMBER

#endif
