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

#ifndef COMMON_IFF_CONTAINER_H
#define COMMON_IFF_CONTAINER_H

#include "common/scummsys.h"
#include "common/endian.h"
#include "common/func.h"
#include "common/stream.h"
#include "common/textconsole.h"

namespace Common {

/**
 * @defgroup common_iff Interchange File Format (IFF)
 * @ingroup common
 *
 * @brief API for operations on IFF container files.
 *
 *
 * @{
 */

typedef uint32 IFF_ID;

#define ID_FORM     MKTAG('F','O','R','M')
/* EA IFF 85 group identifier */
#define ID_CAT      MKTAG('C','A','T',' ')
/* EA IFF 85 group identifier */
#define ID_LIST     MKTAG('L','I','S','T')
/* EA IFF 85 group identifier */
#define ID_PROP     MKTAG('P','R','O','P')
/* EA IFF 85 group identifier */
#define ID_END      MKTAG('E','N','D',' ')
/* unofficial END-of-FORM identifier (see Amiga RKM Devices Ed.3
page 376) */
#define ID_ILBM     MKTAG('I','L','B','M')
/* EA IFF 85 raster bitmap form */
#define ID_DEEP     MKTAG('D','E','E','P')
/* Chunky pixel image files (Used in TV Paint) */
#define ID_RGB8     MKTAG('R','G','B','8')
/* RGB image forms, Turbo Silver (Impulse) */
#define ID_RGBN     MKTAG('R','G','B','N')
/* RGB image forms, Turbo Silver (Impulse) */
#define ID_PBM      MKTAG('P','B','M',' ')
/* 256-color chunky format (DPaint 2 ?) */
#define ID_ACBM     MKTAG('A','C','B','M')
/* Amiga Contiguous Bitmap (AmigaBasic) */
#define ID_8SVX     MKTAG('8','S','V','X')
/* Amiga 8 bits voice */

/* generic */

#define ID_FVER     MKTAG('F','V','E','R')
/* AmigaOS version string */
#define ID_JUNK     MKTAG('J','U','N','K')
/* always ignore this chunk */
#define ID_ANNO     MKTAG('A','N','N','O')
/* EA IFF 85 Generic Annotation chunk */
#define ID_AUTH     MKTAG('A','U','T','H')
/* EA IFF 85 Generic Author chunk */
#define ID_CHRS     MKTAG('C','H','R','S')
/* EA IFF 85 Generic character string chunk */
#define ID_NAME     MKTAG('N','A','M','E')
/* EA IFF 85 Generic Name of art, music, etc. chunk */
#define ID_TEXT     MKTAG('T','E','X','T')
/* EA IFF 85 Generic unformatted ASCII text chunk */
#define ID_copy     MKTAG('(','c',')',' ')
/* EA IFF 85 Generic Copyright text chunk */

/* IFF chunks */

#define ID_BMHD     MKTAG('B','M','H','D')
/* IFF BitmapHeader */
#define ID_CMAP     MKTAG('C','M','A','P')
/* IFF 8bit RGB colormap */
#define ID_GRAB     MKTAG('G','R','A','B')
/* IFF "hotspot" coordiantes */
#define ID_DEST     MKTAG('D','E','S','T')
/* IFF destination image info */
#define ID_SPRT     MKTAG('S','P','R','T')
/* IFF sprite identifier */
#define ID_CAMG     MKTAG('C','A','M','G')
/* Amiga viewportmodes */
#define ID_BODY     MKTAG('B','O','D','Y')
/* IFF image data */
#define ID_CRNG     MKTAG('C','R','N','G')
/* color cycling */
#define ID_CCRT     MKTAG('C','C','R','T')
/* color cycling */
#define ID_CLUT     MKTAG('C','L','U','T')
/* Color Lookup Table chunk */
#define ID_DPI      MKTAG('D','P','I',' ')
/* Dots per inch chunk */
#define ID_DPPV     MKTAG('D','P','P','V')
/* DPaint perspective chunk (EA) */
#define ID_DRNG     MKTAG('D','R','N','G')
/* DPaint IV enhanced color cycle chunk (EA) */
#define ID_EPSF     MKTAG('E','P','S','F')
/* Encapsulated Postscript chunk */
#define ID_CMYK     MKTAG('C','M','Y','K')
/* Cyan, Magenta, Yellow, & Black color map (Soft-Logik) */
#define ID_CNAM     MKTAG('C','N','A','M')
/* Color naming chunk (Soft-Logik) */
#define ID_PCHG     MKTAG('P','C','H','G')
/* Line by line palette control information (Sebastiano Vigna) */
#define ID_PRVW     MKTAG('P','R','V','W')
/* A mini duplicate IFF used for preview (Gary Bonham) */
#define ID_XBMI     MKTAG('X','B','M','I')
/* eXtended BitMap Information (Soft-Logik) */
#define ID_CTBL     MKTAG('C','T','B','L')
/* Newtek Dynamic Ham color chunk */
#define ID_DYCP     MKTAG('D','Y','C','P')
/* Newtek Dynamic Ham chunk */
#define ID_SHAM     MKTAG('S','H','A','M')
/* Sliced HAM color chunk */
#define ID_ABIT     MKTAG('A','B','I','T')
/* ACBM body chunk */
#define ID_DCOL     MKTAG('D','C','O','L')
/* unofficial direct color */
#define ID_DPPS     MKTAG('D','P','P','S')
/* ? */
#define ID_TINY     MKTAG('T','I','N','Y')
/* ? */
#define ID_DPPV     MKTAG('D','P','P','V')
/* ? */

/* 8SVX chunks */

#define ID_VHDR     MKTAG('V','H','D','R')
/* 8SVX Voice8Header */


/**
 *  Represents a IFF chunk available to client code.
 *
 *  Client code must *not* deallocate _stream when done.
 */
struct IFFChunk {
	IFF_ID      _type;
	uint32      _size;
	ReadStream *_stream;

	IFFChunk(IFF_ID type, uint32 size, ReadStream *stream) : _type(type), _size(size), _stream(stream) {
		assert(_stream);
	}
};

/**
 *  Parser for IFF containers.
 */
class IFFParser {

	/**
	 *  This private class implements IFF chunk navigation.
	 */
	class IFFChunkNav : public ReadStream {
	protected:
		ReadStream *_input;
		uint32 _bytesRead;
	public:
		IFF_ID id;
		uint32 size;

		IFFChunkNav() : _input(nullptr) {
		}
		void setInputStream(ReadStream *input) {
			_input = input;
			size = _bytesRead = 0;
		}
		void incBytesRead(uint32 inc) {
			_bytesRead += inc;
			if (_bytesRead > size) {
				error("Chunk overread");
			}
		}
		void readHeader() {
			id = _input->readUint32BE();
			size = _input->readUint32BE();
			_bytesRead = 0;
		}
		bool hasReadAll() const {
			return (size - _bytesRead) == 0;
		}
		void feed() {
			if (size % 2) {
				size++;
			}
			while (!hasReadAll()) {
				readByte();
			}
		}
		// ReadStream implementation
		bool eos() const { return _input->eos(); }
		bool err() const { return _input->err(); }
		void clearErr() { _input->clearErr(); }

		uint32 read(void *dataPtr, uint32 dataSize) {
			incBytesRead(dataSize);
			return _input->read(dataPtr, dataSize);
		}
	};

protected:
	IFFChunkNav _formChunk;	///< The root chunk of the file.
	IFFChunkNav _chunk; 	///< The current chunk.

	uint32 _formSize;
	IFF_ID _formType;

	ReadStream *_stream;
	bool _disposeStream;

	void setInputStream(ReadStream *stream);

public:
	IFFParser(ReadStream *stream, bool disposeStream = false);
	~IFFParser();

	/**
	 * Callback type for the parser.
	 */
	typedef Functor1< IFFChunk&, bool > IFFCallback;

	/**
	 * Parse the IFF container, invoking the callback on each chunk encountered.
	 * The callback can interrupt the parsing by returning 'true'.
	 */
	void parse(IFFCallback &callback);
};


/**
 * Decode a given PackBits encoded stream.
 *
 * PackBits is an RLE compression algorithm introduced by Apple. It is also
 * used to encode ILBM and PBM subtypes of IFF files, and some flavors of
 * TIFF.
 *
 * As there is no compression across row boundaries in the above formats,
 * read() will extract a *new* line on each call, discarding any alignment
 * or padding.
 */
class PackBitsReadStream : public Common::ReadStream {

protected:
	Common::ReadStream *_input;

public:
	PackBitsReadStream(Common::ReadStream &input);
	~PackBitsReadStream();

	virtual bool eos() const;

	uint32 read(void *dataPtr, uint32 dataSize);
};

/** @} */

} // namespace Common

#endif
