/* ScummVM - Graphic Adventure Engine
 *
 * ScummVM is the legal property of its developers, whose names
 * are too numerous to list here. Please refer to the COPYRIGHT
 * file distributed with this source distribution.
 *
 * 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 *
 * This file is dual-licensed.
 * In addition to the GPLv3 license mentioned above, this code is also
 * licensed under LGPL 2.1. See LICENSES/COPYING.LGPL file for the
 * full text of the license.
 *
 */

#ifndef GOB_SAVE_SAVEFILE_H
#define GOB_SAVE_SAVEFILE_H

#include "common/endian.h"
#include "common/array.h"
#include "common/savefile.h"

namespace Gob {

class GobEngine;
class Surface;

/**
 * A class wrapping a save part header.
 *
 * A save part header consists of 4 fields:
 * ID      : The 8 character ID \0SCVMGOB
 * Type    : The 4 character ID for this part's type
 * Version : This part's version. Each type has its own version counter
 * Size    : The size of the contents, i.e. excluding this header
*/
class SaveHeader {
public:
	/** The size of the header. */
	static const int kSize = 20;
	static const uint32 kID1 = MKTAG(0,'S','C','V');
	static const uint32 kID2 = MKTAG('M','G','O','B');

	SaveHeader(uint32 type = 0, uint32 version = 0, uint32 size = 0);

	bool operator==(const SaveHeader &header) const;
	bool operator!=(const SaveHeader &header) const;

	/** Read the header out of a stream into this class. */
	bool read(Common::ReadStream &stream);
	/** Read the header out of a stream and checks it against this class's contents. */
	bool verify(Common::ReadStream &stream) const;
	/** Read the header out of a stream and checks it against this class's contents,
	 *  but read the size field instead.
	 */
	bool verifyReadSize(Common::ReadStream &stream);
	/** Write this class's contents into a stream. */
	bool write(Common::WriteStream &stream) const;

	uint32 getType() const;
	uint32 getVersion() const;
	uint32 getSize() const;

	void setType(uint32 type);
	void setVersion(uint32 version);
	void setSize(uint32 size);

private:
	/** An ID specifying the part's type. */
	uint32 _type;
	/** The part's version. */
	uint32 _version;
	/** The size of the contents. */
	uint32 _size;
};

/** An abstract class for a part in a save file. */
class SavePart {
public:
	SavePart();
	virtual ~SavePart();

	/** Return the total size of the part. */
	virtual uint32 getSize() const;

	/** Read the part (with header) out of the stream. */
	virtual bool read(Common::ReadStream &stream) = 0;
	/** Write the part (with header) into the stream. */
	virtual bool write(Common::WriteStream &stream) const = 0;

protected:
	SaveHeader _header;
};

/** A save part consisting of plain memory. */
class SavePartMem : public SavePart {
public:
	static const uint32 kVersion = 1;
	static const uint32 kID = MKTAG('P','M','E','M');

	SavePartMem(uint32 size);
	~SavePartMem() override;

	bool read(Common::ReadStream &stream) override;
	bool write(Common::WriteStream &stream) const override;

	/** Read size bytes of data into the part at the specified offset. */
	bool readFrom(const byte *data, uint32 offset, uint32 size);
	/** Write size bytes of the part at the specified offset int data. */
	bool writeInto(byte *data, uint32 offset, uint32 size) const;

private:
	uint32 _size;
	byte *_data;
};

/** A save part holding script variables. */
class SavePartVars : public SavePart {
public:
	static const uint32 kVersion = 1;
	static const uint32 kID = MKTAG('V','A','R','S');

	SavePartVars(GobEngine *vm, uint32 size);
	~SavePartVars() override;

	bool read(Common::ReadStream &stream) override;
	bool write(Common::WriteStream &stream) const override;

	/** Read size bytes of variables starting at var into the part at the specified offset. */
	bool readFrom(uint32 var, uint32 offset, uint32 size);
	/** Write size bytes of the part at the specified offset into the variable starting at var. */
	bool writeInto(uint32 var, uint32 offset, uint32 size) const;

	/** Read size bytes of raw data into the part. */
	bool readFromRaw(const byte *data, uint32 offset, uint32 size);

	/** Write size bytes of the part at the specified offset into a raw buffer. */
	bool writeIntoRaw(byte *data, uint32 offset, uint32 size) const;

	const byte* data() const { return _data; }

private:
	GobEngine *_vm;

	uint32 _size;
	byte *_data;
};

/** A save part holding a sprite. */
class SavePartSprite : public SavePart {
public:
	static const uint32 kVersion = 2;
	static const uint32 kID = MKTAG('S','P','R','T');

	SavePartSprite(uint32 width, uint32 height, bool trueColor = false);
	~SavePartSprite() override;

	bool read(Common::ReadStream &stream) override;
	bool write(Common::WriteStream &stream) const override;

	/** Read a palette into the part. */
	bool readPalette(const byte *palette);
	/** Read a sprite into the part. */
	bool readSprite(const Surface &sprite);

	/** Read size bytes of raw data into the sprite. */
	bool readSpriteRaw(const byte *data, uint32 size);

	/** Write a palette out of the part. */
	bool writePalette(byte *palette) const;
	/** Write a sprite out of the part. */
	bool writeSprite(Surface &sprite) const;

private:
	uint32 _width;
	uint32 _height;

	uint32 _spriteSize;

	bool _oldFormat;
	bool _trueColor;

	byte *_dataSprite;
	byte *_dataPalette;
};

/** A save part containing informations about the save's game. */
class SavePartInfo : public SavePart {
public:
	static const uint32 kVersion = 1;
	static const uint32 kID = MKTAG('I','N','F','O');

	/**
	 * The constructor.
	 * @param descMaxLength The maximal number of bytes that fit into the description.
	 * @param gameID An ID for the game (Gob1, Gob2, Gob3, ...).
	 * @param gameVersion An ID for game specific versioning
	 * @param endian Endianness of the platform the game originally ran on.
	 * @param varCount The number of script variables.
	 */
	SavePartInfo(uint32 descMaxLength, uint32 gameID,
			uint32 gameVersion, byte endian, uint32 varCount);
	~SavePartInfo() override;

	/** Return the save's description. */
	const char *getDesc() const;
	/** Return the description's maximal length. */
	uint32 getDescMaxLength() const;

	/** Set the variable count. */
	void setVarCount(uint32 varCount);
	/** Set the save's description. */
	void setDesc(const char *desc = 0);
	/** Set the save's description. */
	void setDesc(const byte *desc, uint32 size);

	bool read(Common::ReadStream &stream) override;
	bool write(Common::WriteStream &stream) const override;

private:
	char *_desc;
	uint32 _descMaxLength;
	uint32 _gameID;
	uint32 _gameVersion;
	uint32 _varCount;
	byte _endian;
};

/** A container for several save parts. */
class SaveContainer {
public:
	static const uint32 kVersion = 1;
	static const uint32 kID = MKTAG('C','O','N','T');

	/**
	 * The constructor.
	 * @param partCount The number parts this container shall hold.
	 * @param slot The save slot this save's for.
	 */
	SaveContainer(uint32 partCount, uint32 slot);
	~SaveContainer();

	uint32 getSlot() const;
	uint32 getSize() const;

	/** All parts filled? */
	bool hasAllParts() const;

	/** Empty all parts. */
	void clear();

	/** Write a SavePart into the container's part. */
	bool writePart(uint32 partN, const SavePart *part);
	/** Read the container's part's content into a SavePart. */
	bool readPart(uint32 partN, SavePart *part) const;
	/** Read only the container's part's header. */
	bool readPartHeader(uint32 partN, SaveHeader *header) const;

	/** Checks if the stream is a valid save container. */
	static bool isSave(Common::SeekableReadStream &stream);

protected:
	/** A part. */
	struct Part {
		uint32 size;
		byte *data;

		Part(uint32 s);
		~Part();

		Common::WriteStream *createWriteStream();
		Common::ReadStream *createReadStream() const;
	};

	/** Basic information about a part. */
	struct PartInfo {
		uint32 id;
		uint32 offset;
		uint32 size;
	};

	typedef Common::Array<Part *>::iterator PartIterator;
	typedef Common::Array<Part *>::const_iterator PartConstIterator;

	uint32 _partCount;
	uint32 _slot;

	SaveHeader _header;
	Common::Array<Part *> _parts;

	uint32 calcSize() const;

	bool read(Common::ReadStream &stream);
	bool write(Common::WriteStream &stream) const;

	/** Get an array containing basic information about all parts in the container in the stream. */
	static Common::Array<PartInfo> *getPartsInfo(Common::SeekableReadStream &stream);
};

/** Reads a save. */
class SaveReader : public SaveContainer {
public:
	SaveReader(uint32 partCount, uint32 slot, const Common::String &fileName);
	SaveReader(uint32 partCount, uint32 slot, Common::SeekableReadStream &stream);
	~SaveReader();

	bool load();

	bool readPart(uint32 partN, SavePart *part) const;
	bool readPartHeader(uint32 partN, SaveHeader *header) const;

	/** Find and read the save's info part. */
	static bool getInfo(Common::SeekableReadStream &stream, SavePartInfo &info);
	/** Find and read the save's info part. */
	static bool getInfo(const Common::String &fileName, SavePartInfo &info);

protected:
	Common::String _fileName;
	Common::SeekableReadStream *_stream;

	bool _loaded;

	static Common::InSaveFile *openSave(const Common::String &fileName);
	Common::InSaveFile *openSave();
};

/** Writes a save. */
class SaveWriter: public SaveContainer {
public:
	SaveWriter(uint32 partCount, uint32 slot);
	SaveWriter(uint32 partCount, uint32 slot, const Common::String &fileName);
	~SaveWriter();

	bool writePart(uint32 partN, const SavePart *part);

	bool save(Common::WriteStream &stream);
	bool deleteFile();

protected:
	bool save();

	Common::String _fileName;

	/** Is everything ready for saving? */
	bool canSave() const;

	static Common::OutSaveFile *openSave(const Common::String &fileName);
	Common::OutSaveFile *openSave();
};

} // End of namespace Gob

#endif // GOB_SAVE_SAVEFILE_H
