//
// This file is part of the aMule Project.
//
// Copyright (c) 2003-2011 aMule Team ( admin@amule.org / http://www.amule.org )
// Copyright (c) 2002-2011 Merkur ( devs@emule-project.net / http://www.emule-project.net )
//
// Any parts of this program derived from the xMule, lMule or eMule project,
// or contributed by third-party developers are copyrighted by their
// respective authors.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301, USA
//

#ifndef SAFEFILE_H
#define SAFEFILE_H


#include <wx/filename.h>			// Needed for wxFileName
#include <common/MuleDebug.h>			// Needef for CMuleException
#include "Tag.h"

namespace Kademlia {
	class CUInt128;
}
using Kademlia::CUInt128;
class CMD4Hash;


/**
 * This class provides a interface for safe file IO.
 *
 * Basic IO operations will either succeed or throw an exception,
 * so that failure cannot be ignored. There are currently 3 types
 * of failures: Read past EOF, errors while reading, and errors
 * while writing.
 *
 * Beyond basic IO, the interface provides functions for reading
 * and writing a number of simple data-types. These are all written
 * and read as little-endian in order to allow for communication
 * across platforms.
 *
 * Note that when empty areas are created, for instance by seeking
 * past the end, then writing, the value of bytes where no data was
 * explicitly written is not specified.
 */
class CFileDataIO
{
public:
	/**
	 * The Destructor does nothing, but is needed to allow
	 * for safe deletion objects via CFileDataIO pointers.
	 */
	virtual ~CFileDataIO();


	/**
	 * Must return the current position in the file.
	 */
	virtual uint64 GetPosition() const = 0;

	/**
	 * Must return the length of the file-object in bytes.
	 */
	virtual uint64 GetLength() const = 0;

	/**
	 * Returns true when the file-position is past or at the end of the file.
	 */
	virtual bool Eof() const;


	/**
	 * Changes the file position.
	 *
	 * Note that seeking to an negative position is an illegal operation.
	 *
	 * @see wxFile::Seek
	 */
	virtual uint64 Seek(sint64 offset, wxSeekMode from = wxFromStart) const;


	/**
	 * Reads 'count' bytes into 'buffer'.
	 *
	 * @param buffer The target buffer.
	 * @param count The number of bytes to read.
	 *
	 * Note that Read will read the specified number of
	 * bytes unless this would read past the end of the
	 * file. In that case, a CEOFException is thrown and
	 * the position and target buffer is left unchanged.
	 *
	 * However, it is also possible that the read will
	 * fail due to IO errors (bad hardware, ect), in which
	 * case an CIOFailureException will be thrown.
	 */
	virtual void Read(void* buffer, size_t count) const;

	/**
	 * Write 'count' bytes from 'buffer' into the file.
	 *
	 * @param buffer The source-data buffer.
	 * @param count The number of bytes to write.
	 *
	 * Note that Write will throw a CIOFailureException
	 * if it fails to write the specified number of bytes,
	 * which can be caused by hardware failures, lack of
	 * free space, etc.
	 */
	virtual void Write(const void* buffer, size_t count);


	/**
	 * Reads the given type from the file, stored as little-endian.
	 *
	 * @see CSafeFileIO::Read
	 */
	//@{
	virtual uint8		ReadUInt8() const;
	virtual uint16		ReadUInt16() const;
	virtual uint32		ReadUInt32() const;
	virtual uint64		ReadUInt64() const;
	virtual CUInt128	ReadUInt128() const;
	virtual CMD4Hash	ReadHash() const;
	virtual float		ReadFloat() const;
	virtual unsigned char*	ReadBsob(uint8* size) const;
	//@}

	/**
	 * Reads a string from the file.
	 *
	 * @param bOptUTF8 Specifies if the string is UTF8 encoded.
	 * @param lenBytes The number of bytes used to store the string length.
	 * @param SafeRead Avoids throwing CEOFException, see below.
	 * @return The resulting text-string.
	 *
	 * Note that when SafeRead is set to true, CSafeFileIO will crop the length
	 * read from the lenght-field (see lenBytes), so that at most GetLength() -
	 * GetPosition() bytes are read.
	 *
	 * @see CSafeFileIO::Read
	 */
	virtual wxString	ReadString(bool bOptUTF8, uint8 lenBytes = 2, bool SafeRead = false) const;

	/**
	 * Reads a string from the file, where the length is specified directly.
	 *
	 * @param bOptUTF8 Specifies if the string is UTF8 encoded.
	 * @param length The length of the string.
	 * @return The resulting text-string.
	 *
	 * This function is typically used when the text-fields length is not stored
	 * as an integer-field in front of the text-field.
	 */
	virtual wxString	ReadOnlyString(bool bOptUTF8, uint16 length) const;


	/**
	 * Writes a value of the given type to the file, storing it as little-endian.
	 *
	 * @see CSafeFileIO::Write
	 */
	//@{
	virtual void WriteUInt8(uint8 value);
	virtual void WriteUInt16(uint16 value);
	virtual void WriteUInt32(uint32 value);
	virtual void WriteUInt64(uint64 value);
	virtual void WriteUInt128(const CUInt128& value);
	virtual void WriteHash(const CMD4Hash& value);
	virtual void WriteFloat(float value);
	virtual void WriteBsob( const unsigned char* val, uint8 size);
	//@}

	/**
	 * Writes a text-string to the file.
	 *
	 * @param str The string to be written.
	 * @param encoding The text-ecoding, see EUtf8Str.
	 * @param lenBytes The number of bytes used to store the string length.
	 *
	 * Valid values for the 'lenBytes' parameters is 0 bytes (no length field),
	 * 2 bytes and 4 bytes.
	 *
	 * @see CSafeFileIO::Write
	 */
	virtual void WriteString(const wxString& str, EUtf8Str encoding = utf8strNone, uint8 lenBytes = 2);

/* Warning: Special Kad functions, needs documentation */

	CTag*		ReadTag(bool bOptACP = false) const;
	void		ReadTagPtrList(TagPtrList* taglist, bool bOptACP = false) const;

	void		WriteTag(const CTag& tag);
	void		WriteTagPtrList(const TagPtrList& tagList);

/* Special ED2Kv2 function */
	uint64		GetIntTagValue() const;

/* Some functions I added for simplicity */
	// Very obvious
	bool IsEmpty() { return (GetLength() == 0); }

	// Appends to the end
	void Append(const uint8* buffer, int n) { Seek(0, wxFromEnd); Write(buffer, n); }

protected:
	/**
	 * The actual read / write function, as implemented by subclasses.
	 *
	 * @param buffer The buffer to read data into / write data from.
	 * @param count The number of bytes to read / written.
	 * @return The number of bytes read / written or -1 in case of errors.
	 *
	 * Note that the return value must be the actual number of bytes
	 * read or written, with the exception that in case of errors, -1
	 * may be returned. This is because the return value is used to
	 * detect if the operation succeded.
	 *
	 * This function should not throw Either of the CSafeIOExceptions,
	 * this is done by the CSafeFileIO::Read and the CSafeFileIO::Write
	 * functions.
	 */
	//@{
	virtual sint64 doRead(void* buffer, size_t count) const = 0;
	virtual sint64 doWrite(const void* buffer, size_t count) = 0;
	//@}

	/**
	 * The actual seek function, as implemented by subclasses.
	 *
	 * @param offset The absolute offset to seek to.
	 * @return The resulting offset.
	 *
	 * This function should not throw of the CSafeIOExceptions,
	 * this is handled by the CSafeFileIO::Seek. At the moment,
	 * seeks that fail are considered a fatal error.
	 */
	virtual sint64 doSeek(sint64 offset) const = 0;

private:
	/**
	 * Helper-function that does the actual writing of the string.
	 *
	 * @param str The string to be written.
	 * @param encoding The encoding of the string.
	 * @param lenBytes The number of bytes used to store the string length.
	 *
	 */
	void WriteStringCore(const char* str, EUtf8Str encoding, uint8 lenBytes);
};


/**
 * The base class of IO exceptions used by
 * the CSafeFileIO interface and implementations
 * of the interface.
 */
struct CSafeIOException : public CMuleException
{
	CSafeIOException(const wxString& type, const wxString& desc);
};


/**
 * This exception is thrown when attempts are
 * made at reading past the end of the file.
 *
 * This typically happens when a invalid packet
 * is received that is shorter than expected and
 * is not fatal.
 */
struct CEOFException : public CSafeIOException {
	CEOFException(const wxString& desc);
};


/**
 * This exception reflects a failure in performing
 * basic IO operations read and write. It will be
 * thrown in case a read or a write fails to read
 * or write the specified number of bytes.
 */
struct CIOFailureException : public CSafeIOException {
	CIOFailureException(const wxString& type, const wxString& desc);
	CIOFailureException(const wxString& desc);
};


#endif // SAFEFILE_H
// File_checked_for_headers
