/* ResidualVM - A 3D game interpreter
 *
 * ResidualVM 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.
 *
 */

#ifndef GRAPHICS_PIXELBUFFER_H
#define GRAPHICS_PIXELBUFFER_H

#include "common/types.h"
#include "common/endian.h"
#include "common/textconsole.h"

#include "graphics/colormasks.h"
#include "graphics/pixelformat.h"

namespace Graphics {

class PixelBuffer {
public:
	/**
	 * Create a PixelBuffer.
	 * Convenience syntax for PixelBuffer(createPixelFormat<format>(), buffersize, dispose).
	 */
	template<int format>
	inline static PixelBuffer createBuffer(int buffersize, DisposeAfterUse::Flag dispose) {
		return PixelBuffer(createPixelFormat<format>(), buffersize, dispose);
	}

	/**
	 * Create a PixelBuffer.
	 * Convenience syntax for PixelBuffer(createPixelFormat<format>(), buffer).
	 */
	template<int format>
	inline static PixelBuffer createBuffer(byte *buffer) {
		return PixelBuffer(createPixelFormat<format>(), buffer);
	}

	/**
	 * Construct an empty PixelBuffer.
	 */
	PixelBuffer();
	/**
	 * Construct a PixelBuffer, allocating the internal buffer.
	 *
	 * @param format The format of the pixels in this buffer.
	 * @param buffersize The number of pixels the buffer will store.
	 * @param dispose If YES the internal buffer will be deleted when this object is destroyed,
	 */
	PixelBuffer(const Graphics::PixelFormat &format, int buffersize, DisposeAfterUse::Flag dispose);
	/**
	 * Construct a PixelBuffer, using an already allocated buffer.
	 *
	 * @param format The format of the pixels in this buffer.
	 * @param buffer The raw buffer containing the pixels.
	 */
	PixelBuffer(const Graphics::PixelFormat &format, byte *buffer);
	/**
	 * Copy constructor.
	 * The internal buffer will NOT be duplicated, it will be shared between the instances.
	 */
	PixelBuffer(const PixelBuffer &buf);
	/**
	 * Destroy the object.
	 */
	~PixelBuffer();

	/**
	 * Initialize the buffer.
	 *
	 * @param format The format of the pixels.
	 * @param buffersize The number of pixels the buffer will store.
	 * @param dispose If YES the internal buffer will be deleted when this object is destroyed,
	 */
	void create(const Graphics::PixelFormat &format, int buffersize, DisposeAfterUse::Flag dispose);
	/**
	 * Initialize the buffer, using the already set pixel format.
	 * @note If the pixel format was not set before the results are undefined.
	 *
	 * @param buffersize The number of pixels the buffer will store.
	 * @param dispose If YES the internal buffer will be deleted when this object is destroyed,
	 */
	void create(int buffersize, DisposeAfterUse::Flag dispose);

	/**
	 * Initialize the buffer.
	 *
	 * @param format The format of the pixels in this buffer.
	 * @param buffer The raw buffer containing the pixels.
	 */
	void set(const Graphics::PixelFormat &format, byte *buffer);

	/**
	 * Delete the internal pixel buffer.
	 */
	void free();

	/**
	 * Reset the value of the pixels.
	 *
	 * @param length The length of the buffer, in pixels.
	 */
	void clear(int length);

	/**
	 * Set the value of the pixel at index 'pixel' to 'value',
	 */
	inline void setPixelAt(int pixel, uint32 value) {
		switch (_format.bytesPerPixel) {
		case 2:
			((uint16 *) _buffer)[pixel] = TO_LE_16((uint16) value);
			return;
		case 3:
			pixel *= 3;
			value = TO_LE_32(value);
			_buffer[pixel + 0] = value & 0xFF;
			_buffer[pixel + 1] = (value >> 8) & 0xFF;
			_buffer[pixel + 2] = (value >> 16) & 0xFF;
			return;
		case 4:
			((uint32 *) _buffer)[pixel] = TO_LE_32(value);
			return;
		}
		error("setPixelAt: Unhandled bytesPerPixel %i", _format.bytesPerPixel);
	}
	/**
	 * Set the value of a pixel. The pixel will be converted from a pixel in another PixelBuffer,
	 * at the same index.
	 *
	 * @param pixel The index of the pixel to set.
	 * @param buf The buffer storing the source pixel.
	 */
	inline void setPixelAt(int pixel, const PixelBuffer &buf) { setPixelAt(pixel, buf, pixel); }
	/**
	 * Set the value of a pixel. The pixel will be converted from a pixel in another PixelBuffer.
	 *
	 * @param thisPix The index of the pixel to set.
	 * @param buf The buffer storing the source pixel.
	 * @param otherPix The index of the source pixel in 'buf'.
	 */
	inline void setPixelAt(int thisPix, const PixelBuffer &buf, int otherPix) {
		if (_format == buf._format) {
			memcpy(getRawBuffer(thisPix), buf.getRawBuffer(otherPix), _format.bytesPerPixel);
			return;
		}
		uint8 a, r, g, b;
		buf.getARGBAt(otherPix, a, r, g, b);
		setPixelAt(thisPix, a, r, g, b);
	}
	/**
	 * Set a pixel, from RGB values.
	 */
	inline void setPixelAt(int pixel, uint8 r, uint8 g, uint8 b) { setPixelAt(pixel, _format.RGBToColor(r, g, b)); }
	/**
	 * Set a pixel, from ARGB values.
	 */
	inline void setPixelAt(int pixel, uint8 a, uint8 r, uint8 g, uint8 b) { setPixelAt(pixel, _format.ARGBToColor(a, r, g, b)); }

	/**
	 * Copy some pixels from a buffer. The pixels will be converted, storing the same ARGB value.
	 *
	 * @param from The starting index.
	 * @param length The number of pixels to copy.
	 * @param buf The source buffer.
	 */
	inline void copyBuffer(int from, int length, const PixelBuffer &buf) { copyBuffer(from, from, length, buf); }
	/**
	 * Copy some pixels from a buffer. The pixels will be converted, storing the same ARGB value.
	 *
	 * @param thisFrom The starting index.
	 * @param otherFrom The starting index in the source buffer.
	 * @param length The number of pixels to copy.
	 * @param buf The source buffer.
	 */
	void copyBuffer(int thisFrom, int otherFrom, int length, const PixelBuffer &buf);

	/**
	 * Shift the internal buffer of some pixels, losing some pixels at the start of the buffer.
	 * The pixels lost will NOT be deleted.
	 */
	inline void shiftBy(int amount) { _buffer += amount * _format.bytesPerPixel; }

	/**
	 * Return the encoded value of the pixel at the given index.
	 */
	inline uint32 getValueAt(int i) const {
		switch (_format.bytesPerPixel) {
		case 2:
			return FROM_LE_16(((uint16 *) _buffer)[i]);
		case 3:
			i *= 3;
#if defined(SCUMM_BIG_ENDIAN)
			return (_buffer[i + 0] << 16) | (_buffer[i + 1] << 8) | _buffer[i + 2];
#elif defined(SCUMM_LITTLE_ENDIAN)
			return _buffer[i + 0] | (_buffer[i + 1] << 8) | (_buffer[i + 2] << 16);
#endif
		case 4:
			return FROM_LE_32(((uint32 *) _buffer)[i]);
		}
		error("getValueAt: Unhandled bytesPerPixel %i", _format.bytesPerPixel);
	}
	/**
	 * Return the RGB value of the pixel at the given index.
	 */
	inline void getRGBAt(int i, uint8 &r, uint8 &g, uint8 &b) const { _format.colorToRGB(getValueAt(i), r, g, b); }
	/**
	 * Return the ARGB value of the pixel at the given index.
	 */
	inline void getARGBAt(int i, uint8 &a, uint8 &r, uint8 &g, uint8 &b) const { _format.colorToARGB(getValueAt(i), a, r, g, b); }

	/**
	 * Return the internal buffer.
	 */
	inline byte *getRawBuffer() const { return _buffer; }
	/**
	 * Return the internal buffer, pointing at the wanted pixel.
	 */
	inline byte *getRawBuffer(int pixel) const { return _buffer + _format.bytesPerPixel * pixel; }

	/**
	 * Return the pixel format used.
	 */
	inline const PixelFormat &getFormat() const { return _format; }

	/**
	 * Copy a PixelBuffer object.
	 * The internal buffer will NOT be duplicated, it will be shared between the instances.
	 */
	PixelBuffer &operator=(const PixelBuffer &buf);
	/**
	 * Set the internal buffer to an already allocated array.
	 *
	 * @param buffer The pointer to the array.
	 */
	PixelBuffer &operator=(byte *buffer);

	/**
	 * Check if the interal buffer is allocated.
	 *
	 * @returns true if allocated.
	 */
	inline operator bool() const { return (_buffer); }

private:
	byte *_buffer;
	Graphics::PixelFormat _format;
	DisposeAfterUse::Flag _dispose;
};

}

#endif
