/* 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 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 */

// Based on eos' BitStream implementation

#ifndef COMMON_BITSTREAM_H
#define COMMON_BITSTREAM_H

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

namespace Common {

/** A bit stream. */
class BitStream {
public:
	virtual ~BitStream() {
	}

	/** Return the stream position in bits. */
	virtual uint32 pos() const = 0;

	/** Return the stream size in bits. */
	virtual uint32 size() const = 0;

	/** Has the end of the stream been reached? */
	virtual bool eos() const = 0;

	/** Rewind the bit stream back to the start. */
	virtual void rewind() = 0;

	/** Skip the specified amount of bits. */
	virtual void skip(uint32 n) = 0;

	/** Skip the bits to closest data value border. */
	virtual void align() = 0;

	/** Read a bit from the bit stream. */
	virtual uint32 getBit() = 0;

	/** Read a multi-bit value from the bit stream. */
	virtual uint32 getBits(uint8 n) = 0;

	/** Read a bit from the bit stream, without changing the stream's position. */
	virtual uint32 peekBit() = 0;

	/** Read a multi-bit value from the bit stream, without changing the stream's position. */
	virtual uint32 peekBits(uint8 n) = 0;

	/** Add a bit to the value x, making it an n+1-bit value. */
	virtual void addBit(uint32 &x, uint32 n) = 0;

protected:
	BitStream() {
	}
};

/**
 * A template implementing a bit stream for different data memory layouts.
 *
 * Such a bit stream reads valueBits-wide values from the data stream and
 * gives access to their bits, one at a time.
 *
 * For example, a bit stream with the layout parameters 32, true, false
 * for valueBits, isLE and isMSB2LSB, reads 32bit little-endian values
 * from the data stream and hands out the bits in the order of LSB to MSB.
 */
template<int valueBits, bool isLE, bool isMSB2LSB>
class BitStreamImpl : public BitStream {
private:
	SeekableReadStream *_stream; ///< The input stream.
	bool _disposeAfterUse;       ///< Should we delete the stream on destruction?

	uint32 _value;   ///< Current value.
	uint8  _inValue; ///< Position within the current value.

	/** Read a data value. */
	inline uint32 readData() {
		if (isLE) {
			if (valueBits ==  8)
				return _stream->readByte();
			if (valueBits == 16)
				return _stream->readUint16LE();
			if (valueBits == 32)
				return _stream->readUint32LE();
		} else {
			if (valueBits ==  8)
				return _stream->readByte();
			if (valueBits == 16)
				return _stream->readUint16BE();
			if (valueBits == 32)
				return _stream->readUint32BE();
		}

		assert(false);
		return 0;
	}

	/** Read the next data value. */
	inline void readValue() {
		if ((size() - pos()) < valueBits)
			error("BitStreamImpl::readValue(): End of bit stream reached");

		_value = readData();
		if (_stream->err() || _stream->eos())
			error("BitStreamImpl::readValue(): Read error");

		// If we're reading the bits MSB first, we need to shift the value to that position
		if (isMSB2LSB)
			_value <<= 32 - valueBits;
		}

public:
	/** Create a bit stream using this input data stream and optionally delete it on destruction. */
	BitStreamImpl(SeekableReadStream *stream, bool disposeAfterUse = false) :
		_stream(stream), _disposeAfterUse(disposeAfterUse), _value(0), _inValue(0) {

		if ((valueBits != 8) && (valueBits != 16) && (valueBits != 32))
			error("BitStreamImpl: Invalid memory layout %d, %d, %d", valueBits, isLE, isMSB2LSB);
	}

	/** Create a bit stream using this input data stream. */
	BitStreamImpl(SeekableReadStream &stream) :
		_stream(&stream), _disposeAfterUse(false), _value(0), _inValue(0) {

		if ((valueBits != 8) && (valueBits != 16) && (valueBits != 32))
			error("BitStreamImpl: Invalid memory layout %d, %d, %d", valueBits, isLE, isMSB2LSB);
	}

	~BitStreamImpl() {
		if (_disposeAfterUse)
			delete _stream;
	}

	/** Read a bit from the bit stream. */
	uint32 getBit() {
		// Check if we need the next value
		if (_inValue == 0)
			readValue();

		// Get the current bit
		int b = 0;
		if (isMSB2LSB)
			b = ((_value & 0x80000000) == 0) ? 0 : 1;
		else
			b = ((_value & 1) == 0) ? 0 : 1;

		// Shift to the next bit
		if (isMSB2LSB)
			_value <<= 1;
		else
			_value >>= 1;

		// Increase the position within the current value
		_inValue = (_inValue + 1) % valueBits;

		return b;
	}

	/**
	 * Read a multi-bit value from the bit stream.
	 *
	 * The value is read as if just taken as a whole from the bitstream.
	 *
	 * For example:
	 * Reading a 4-bit value from an 8-bit bitstream with the contents 01010011:
	 * If the bitstream is MSB2LSB, the 4-bit value would be 0101.
	 * If the bitstream is LSB2MSB, the 4-bit value would be 0011.
	 */
	uint32 getBits(uint8 n) {
		if (n == 0)
			return 0;

		if (n > 32)
			error("BitStreamImpl::getBits(): Too many bits requested to be read");

		// Read the number of bits
		uint32 v = 0;

		if (isMSB2LSB) {
			while (n-- > 0)
				v = (v << 1) | getBit();
		} else {
			for (uint32 i = 0; i < n; i++)
				v = (v >> 1) | (((uint32) getBit()) << 31);

			v >>= (32 - n);
		}

		return v;
	}

	/** Read a bit from the bit stream, without changing the stream's position. */
	uint32 peekBit() {
		uint32 value   = _value;
		uint8  inValue = _inValue;
		uint32 curPos  = _stream->pos();

		uint32 v = getBit();

		_stream->seek(curPos);
		_inValue = inValue;
		_value   = value;

		return v;
	}

	/**
	 * Read a multi-bit value from the bit stream, without changing the stream's position.
	 *
	 * The bit order is the same as in getBits().
	 */
	uint32 peekBits(uint8 n) {
		uint32 value   = _value;
		uint8  inValue = _inValue;
		uint32 curPos  = _stream->pos();

		uint32 v = getBits(n);

		_stream->seek(curPos);
		_inValue = inValue;
		_value   = value;

		return v;
	}

	/**
	 * Add a bit to the value x, making it an n+1-bit value.
	 *
	 * The current value is shifted and the bit is added to the
	 * appropriate place, dependant on the stream's bitorder.
	 *
	 * For example:
	 * A bit y is added to the value 00001100 with size 4.
	 * If the stream's bitorder is MSB2LSB, the resulting value is 0001100y.
	 * If the stream's bitorder is LSB2MSB, the resulting value is 000y1100.
	 */
	void addBit(uint32 &x, uint32 n) {
		if (n >= 32)
			error("BitStreamImpl::addBit(): Too many bits requested to be read");

		if (isMSB2LSB)
			x = (x << 1) | getBit();
		else
			x = (x & ~(1 << n)) | (getBit() << n);
	}

	/** Rewind the bit stream back to the start. */
	void rewind() {
		_stream->seek(0);

		_value   = 0;
		_inValue = 0;
	}

	/** Skip the specified amount of bits. */
	void skip(uint32 n) {
		while (n-- > 0)
			getBit();
	}

	/** Skip the bits to closest data value border. */
	void align() {
		while (_inValue)
			getBit();
	}

	/** Return the stream position in bits. */
	uint32 pos() const {
		if (_stream->pos() == 0)
			return 0;

		uint32 p = (_inValue == 0) ? _stream->pos() : ((_stream->pos() - 1) & ~((uint32) ((valueBits >> 3) - 1)));
		return p * 8 + _inValue;
	}

	/** Return the stream size in bits. */
	uint32 size() const {
		return (_stream->size() & ~((uint32) ((valueBits >> 3) - 1))) * 8;
	}

	bool eos() const {
		return _stream->eos() || (pos() >= size());
	}
};

// typedefs for various memory layouts.

/** 8-bit data, MSB to LSB. */
typedef BitStreamImpl<8, false, true > BitStream8MSB;
/** 8-bit data, LSB to MSB. */
typedef BitStreamImpl<8, false, false> BitStream8LSB;

/** 16-bit little-endian data, MSB to LSB. */
typedef BitStreamImpl<16, true , true > BitStream16LEMSB;
/** 16-bit little-endian data, LSB to MSB. */
typedef BitStreamImpl<16, true , false> BitStream16LELSB;
/** 16-bit big-endian data, MSB to LSB. */
typedef BitStreamImpl<16, false, true > BitStream16BEMSB;
/** 16-bit big-endian data, LSB to MSB. */
typedef BitStreamImpl<16, false, false> BitStream16BELSB;

/** 32-bit little-endian data, MSB to LSB. */
typedef BitStreamImpl<32, true , true > BitStream32LEMSB;
/** 32-bit little-endian data, LSB to MSB. */
typedef BitStreamImpl<32, true , false> BitStream32LELSB;
/** 32-bit big-endian data, MSB to LSB. */
typedef BitStreamImpl<32, false, true > BitStream32BEMSB;
/** 32-bit big-endian data, LSB to MSB. */
typedef BitStreamImpl<32, false, false> BitStream32BELSB;

} // End of namespace Common

#endif // COMMON_BITSTREAM_H
