/* 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/>.
 *
 */

namespace Agi {

/**
 * WagProperty represents a single property from WinAGI's *.wag file.
 * A property consists of a header and of data.
 * The header consists of the following:
 * - Property code (Integer/Enumeration, 1 byte)
 * - Property type (Integer/Enumeration, 1 byte)
 * - Property number (Integer, 1 byte)
 * - Property size (Little endian integer, 2 bytes)
 * And then there's the data with as many bytes as defined in the header's property size variable.
 */
class WagProperty {
// Constants, enumerations etc
public:
	/**
	 * Property codes taken from WinAGI 1.1.21's source code file WinAGI/AGIObjects.bas.
	 */
	enum WagPropertyCode {
		PC_GAMEDESC = 129, ///< Game description (WinAGI 1.1.21 limits these to 4096 bytes)
		PC_GAMEAUTHOR,     ///< Game author (WinAGI 1.1.21 limits these to 256 bytes)
		PC_GAMEID,         ///< Game ID
		PC_INTVERSION,     ///< Interpreter version (WinAGI 1.1.21 defaults to version 2.917)
		PC_GAMELAST,       ///< Last edit date
		PC_GAMEVERSION,    ///< Game version (WinAGI 1.1.21 limits these to 256 bytes)
		PC_GAMEABOUT,      ///< About game (WinAGI 1.1.21 limits these to 4096 bytes)
		PC_GAMEEXEC,       ///< Game executable
		PC_RESDIR,         ///< Resource directory name
		PC_DEFSYNTAX,      ///< Default syntax
		PC_INVOBJDESC = 144,
		PC_VOCABWORDDESC = 160,
		PC_PALETTE = 172,
		PC_USERESNAMES = 180,
		PC_LOGIC = 192,
		PC_PICTURE = 208,
		PC_SOUND = 224,
		PC_VIEW = 240,
		PC_UNDEFINED = 0x100 ///< An undefined property code (Added for ScummVM).
	};

	/**
	 * Property types taken from WinAGI 1.1.21's source code file WinAGI/AGIObjects.bas.
	 * At the moment these aren't really at all needed by ScummVM. Just here if anyone decides to use them.
	 */
	enum WagPropertyType {
		PT_ID,
		PT_DESC,
		PT_SYNTAX,
		PT_CRC32,
		PT_KEY,
		PT_INST0,
		PT_INST1,
		PT_INST2,
		PT_MUTE0,
		PT_MUTE1,
		PT_MUTE2,
		PT_MUTE3,
		PT_TPQN,
		PT_ROOM,
		PT_VIS0,
		PT_VIS1,
		PT_VIS2,
		PT_VIS3,
		PT_ALL = 0xff,
		PT_UNDEFINED = 0x100 ///< An undefined property type (Added for ScummVM).
	};

// Constructors, destructors, operators etc
public:
	/**
	 * Creates an empty WagProperty object.
	 * No property header or property data in it.
	 */
	WagProperty();

	/**
	 * Destructor. Releases allocated memory if any etc. The usual.
	 */
	~WagProperty();

	/**
	 * Copy constructor. Deep copies the variables.
	 */
	WagProperty(const WagProperty &other);

	/**
	 * Assignment operator. Deep copies the variables.
	 */
	WagProperty &operator=(const WagProperty &other);

// Non-public helper methods
protected:
	/**
	 * Sets the default values for member variables.
	 */
	void setDefaults();

	/**
	 * Delete's the property's data from memory if we have it, otherwise does nothing.
	 */
	void deleteData();

	/**
	 * Deep copies the parameter object to this object.
	 * @param other The object to be deep copied to this object.
	 */
	void deepCopy(const WagProperty &other);

// Public methods that have side-effects
public:
	/**
	 * Read in a property (Header and data).
	 * @return True if reading was a success, false otherwise.
	 */
	bool read(Common::SeekableReadStream &stream);

	/**
	 * Clears the property.
	 * After this the property is empty. No header or data.
	 */
	void clear();

// Public access functions
public:
	/**
	 * Was the property read ok from the source stream?
	 */
	bool readOk() const { return _readOk; }

	/**
	 * Return the property's code.
	 * @return The property's code if readOk(), PC_UNDEFINED otherwise.
	 */
	enum WagPropertyCode getCode() const { return _propCode; }

	/**
	 * Return the property's type.
	 * @return The property's type if readOk(), PT_UNDEFINED otherwise.
	 */
	enum WagPropertyType getType() const { return _propType; }

	/**
	 * Return the property's number.
	 * @return The property's number if readOk(), 0 otherwise.
	 */
	byte getNumber() const { return _propNum; }

	/**
	 * Return the property's data's length.
	 * @return The property's data's length if readOk(), 0 otherwise.
	 */
	uint16 getSize() const { return _propSize; }

	/**
	 * Return property's data. Constant access version.
	 * Can be used as a C-style string (i.e. this is guaranteed to have a trailing zero).
	 * @return The property's data if readOk(), NULL otherwise.
	 */
	const char *getData() const { return _propData; }

	/**
	 * Set property's code
	 * @param propCode the code value to set
	*/
	void setPropCode(WagPropertyCode propCode);

	/**
	 * Set property's data and property's size
	 * @param str the string that according to it these are set
	*/
	void setPropDataSize(Common::String str);

// Member variables
protected:
	bool _readOk;                   ///< Was the property read ok from the source stream?
	enum WagPropertyCode _propCode; ///< Property code (Part of the property's header)
	enum WagPropertyType _propType; ///< Property type (Part of the property's header)
	byte _propNum;                  ///< Property number (Part of the property's header)
	uint16 _propSize;               ///< Property's size (Part of the property's header)
	char *_propData;                ///< The property's data (Plus a trailing zero for C-style string access)
};


/**
 * Class for parsing *.wag files created by WinAGI.
 * Using this class you can get information about fanmade AGI games if they have provided a *.wag file with them.
 */
class WagFileParser {
// Constants, type definitions, enumerations etc.
public:
	enum {
		WINAGI_VERSION_LENGTH = 16 ///< WinAGI's version string's length (Always 16)
	};
	typedef Common::Array<WagProperty> PropertyList; ///< A type definition for an array of *.wag file properties

public:
	/**
	 * Constructor. Creates a WagFileParser object in a default state.
	 */
	WagFileParser();

	/**
	 * Destructor.
	 */
	~WagFileParser();

	/**
	 * Add property to _propList from INI file, if key exist
	 * @param iniWagFile ini loaded from WAG file stream
	 * @param section ini section
	 * @param key ini key
	 * @param code corresponding WAG code
	*/
	void addPropFromIni(Common::INIFile *iniWagFile, Common::String section, Common::String key, Agi::WagProperty::WagPropertyCode code);

	/**
	 * Loads a *.wag file and parses it.
	 * @note After this you can access the loaded properties using getProperty() and getProperties() etc.
	 * @param filename Name of the file to be parsed.
	 * @return True if parsed successfully, false otherwise.
	 */
	bool parse(const Common::FSNode &node);

	/**
	 * Get list of the loaded properties.
	 * @note Use only after a call to parse() first.
	 * @return The list of loaded properties.
	 */
	const PropertyList &getProperties() const { return _propList; }

	/**
	 * Get property with the given property code.
	 * @note Use only after a call to parse() first.
	 * @return Pointer to the property if its found in memory, NULL otherwise.
	 *
	 * TODO/FIXME: Handle cases where several properties with the given property code are found.
	 *             At the moment we don't need this functionality because the properties we use
	 *             for fallback detection probably don't have multiples in the WAG-file.
	 * TODO: Make this faster than linear time if desired/needed.
	 */
	const WagProperty *getProperty(const WagProperty::WagPropertyCode code) const;

	/**
	 * Tests if the given property contains a valid AGI interpreter version string.
	 * A valid AGI interpreter version string is of the form "X.Y" or "X,Y" where
	 * X is a single decimal digit and Y is a string of decimal digits (At least one digit).
	 * @param version The property to be tested.
	 * @return True if the given property contains a valid AGI interpreter version string, false otherwise.
	 */
	bool checkAgiVersionProperty(const WagProperty &version) const;

	/**
	 * Convert property's data to an AGI interpreter version number.
	 * @param version The property to be converted (Property code should be PC_INTVERSION).
	 * @return AGI interpreter version number if successful, 0 otherwise.
	 */
	uint16 convertToAgiVersionNumber(const WagProperty &version);

	/**
	 * Was the file parsed successfully?
	 * @return True if file was parsed successfully, false otherwise.
	 */
	bool parsedOk() const { return _parsedOk; }

protected:
	/**
	 * Checks if stream has a valid WinAGI version string in its end.
	 * @param stream The stream to be checked.
	 * @return True if reading was successful and stream contains a valid WinAGI version string, false otherwise.
	 */
	bool checkWagVersion(Common::SeekableReadStream &stream);

	/**
	 * Checks if we're at or past the end of the properties stored in the stream.
	 * @param stream The stream whose seeking position is to be checked.
	 * @return True if stream's seeking position is at or past the end of the properties, false otherwise.
	 */
	bool endOfProperties(const Common::SeekableReadStream &stream) const;

// Member variables
protected:
	PropertyList _propList; ///< List of loaded properties from the file.
	bool         _parsedOk; ///< Did the parsing of the file go ok?
};

} // End of namespace Agi
