/* 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.
 *
 * $URL$
 * $Id$
 *
 */

/*
 * This code is based on Broken Sword 2.5 engine
 *
 * Copyright (c) Malte Thiesen, Daniel Queteschiner and Michael Elsdoerfer
 *
 * Licensed under GNU GPL v2
 *
 */

/*
 * BS_GraphicEngine
 * ----------------
 * This the graphics engine interface.
 *
 * Autor: Malte Thiesen
 */

#ifndef SWORD25_GRAPHICENGINE_H
#define SWORD25_GRAPHICENGINE_H

// Includes
#include "common/array.h"
#include "common/str.h"
#include "graphics/surface.h"
#include "sword25/kernel/common.h"
#include "sword25/kernel/bs_stdint.h"
#include "sword25/kernel/resservice.h"
#include "sword25/kernel/persistable.h"
#include "sword25/math/rect.h"
#include "sword25/gfx/framecounter.h"
#include "sword25/gfx/renderobjectptr.h"

namespace Sword25 {

class BS_Kernel;
class BS_Image;
class BS_Panel;
class BS_Screenshot;

// Typen
typedef unsigned int BS_COLOR;

// Makros
#define BS_RGB(R,G,B)       (0xFF000000 | ((R) << 16) | ((G) << 8) | (B))
#define BS_ARGB(A,R,G,B)    (((A) << 24) | ((R) << 16) | ((G) << 8) | (B))

/**
    @brief Dies ist das Graphik-Engine Interface, dass alle Methoden und Klassen enth�lt, die eine Graphik-Engine implementieren muss.

    Hier sind nur wenige Rumpffunktionen realisiert, wie z.B. das Abfragen der Parameter des Ausgabepuffers.
    Die Hauptfunktionen muss eine Implementation dieses Inferfaces stellen.<br>
    Die bisher einzige Implementation ist BS_DDrawGfx.
*/

class BS_GraphicEngine : public BS_ResourceService, public BS_Persistable {
public:
	// Enums
	// -----

	// Colour formats
	//
	/**
	 * The colour format used by the engine
	 */
	enum COLOR_FORMATS {
		/// Undefined/unknown colour format
		CF_UNKNOWN = 0,
		/// 16-bit colour format (R5G5B5)
		CF_RGB15,
		/// 16-bit colour format (R5G6R5)
		CF_RGB16,
		/**
		 * Special alpha colour format of the engine, which supports very quick display using MMX instructions.
		 * The pixels are 16-bits wide and have the same format as #CF_RGB15. In addition, each pixel has an 8-bit
		 * alpha value.
		 * It summarises groupings of pixels pixels and four alpha values in a 12-byte data block.
		 * The data is stored in the following order:
		 * Alpha0 Alpha1 Alpha2 Alpha3 Pixel0 Pixel1 Pixel2 Pixel3
		 * If the number of pixels in a line is not divisible by 4, then unused pixels and alpha values can have
		 * arbitrary values.
		 */
		CF_RGB15_INTERLEAVED,
		/**
		 * Special alpha colour format of the engine, which supports very quick display using MMX instructions.
		 * The pixels are 16-bits wide and have the same format as #CF_RGB16. In addition, each pixel has an 8-bit
		 * alpha value.
		 * It summarises groupings of pixels pixels and four alpha values in a 12-byte data block.
		 * The data is stored in the following order:
		 * Alpha0 Alpha1 Alpha2 Alpha3 Pixel0 Pixel1 Pixel2 Pixel3
		 * If the number of pixels in a line is not divisible by 4, then unused pixels and alpha values can have
		 * arbitrary values.
		 */
		CF_RGB16_INTERLEAVED,
		/**
		 * 24-bit colour format (R8G8B8)
		 */
		CF_RGB24,
		/**
		 * 32-bit colour format (A8R8G8B8) (little endian)
		*/
		CF_ARGB32,
		/**
		    32-bit colour format (A8B8G8R8) (little endian)
		*/
		CF_ABGR32
	};

	// Interface
	// ---------

	/**
	 * Initialises the graphics engine and sets the screen mode. Returns true if initialisation failed.
	 * Notes: This method should be called immediately after the initialisation of all services.
	 *
	 * @param Height            The height of the output buffer in pixels. The default value is 600
	 * @param BitDepth          The bit depth of the desired output buffer in bits. The default value is 16
	 * @param BackbufferCount   The number of back buffers to be created. The default value is 2
	 * @param Windowed          Indicates whether the engine is to run in windowed mode.
	 */
	virtual bool        Init(int Width = 800, int Height = 600, int BitDepth = 16, int BackbufferCount = 2, bool Windowed = false) = 0;

	/**
	 * Begins rendering a new frame.
	 * Notes: This method must be called at the beginning of the main loop, before any rendering methods are used.
	 * Notes: Implementations of this method must call _UpdateLastFrameDuration()
	 * @param UpdateAll         Specifies whether the renderer should redraw everything on the next frame.
	 * This feature can be useful if the renderer with Dirty Rectangles works, but sometimes the client may
	*/
	virtual bool        StartFrame(bool UpdateAll = false) = 0;

	/**
	 * Ends the rendering of a frame and draws it on the screen.
	 *
	 * This method must be at the end of the main loop. After this call, no further Render method may be called.
	 * This should only be called once for a given previous call to #StartFrame.
	*/
	virtual bool        EndFrame() = 0;

	// Debug methods

	/**
	 * Draws a line in the frame buffer
	 *
	 * This method must be called between calls to StartFrame() and EndFrame(), and is intended only for debugging
	 * purposes. The line will only appear for a single frame. If the line is to be shown permanently, it must be
	 * called for every frame.
	* @param Start      The starting point of the line
	* @param End        The ending point of the line
	* @param Color      The colour of the line. The default is BS_RGB (255,255,255) (White)
	*/
	virtual void        DrawDebugLine(const BS_Vertex &Start, const BS_Vertex &End, unsigned int Color = BS_RGB(255, 255, 255)) = 0;

	/**
	 * Creates a screenshot of the current frame buffer and writes it to a graphic file in PNG format.
	 * Returns true if the screenshot was saved successfully.
	 * Notes: This method should only be called after a call to EndFrame(), and before the next call to StartFrame().
	 * @param Filename  The filename for the screenshot
	 */
	bool SaveScreenshot(const Common::String &Filename);

	/**
	 * Creates a thumbnail with the dimensions of 200x125. This will not include the top and bottom of the screen..
	 * the interface boards the the image as a 16th of it's original size.
	 * Notes: This method should only be called after a call to EndFrame(), and before the next call to StartFrame().
	 * The frame buffer must have a resolution of 800x600.
	 * @param Filename  The filename for the screenshot
	 */
	bool SaveThumbnailScreenshot(const Common::String &Filename);

	/**
	 * Reads the current contents of the frame buffer
	 * Notes: This method is for creating screenshots. It is not very optimised. It should only be called
	 * after a call to EndFrame(), and before the next call to StartFrame().
	 * @param Width     Returns the width of the frame buffer
	 * @param Height    Returns the height of the frame buffer
	 * @param Data      Returns the raw data of the frame buffer as an array of 32-bit colour values.
	*/
	virtual bool GetScreenshot(unsigned int &Width, unsigned int &Height, byte **Data) = 0;


	virtual BS_RenderObjectPtr<BS_Panel> GetMainPanel() = 0;

	/**
	 * Specifies the time (in microseconds) since the last frame has passed
	 */
	int GetLastFrameDurationMicro() {
		if (m_TimerActive) return m_LastFrameDuration;
		else return 0;
	}

	/**
	 * Specifies the time (in microseconds) the previous frame took
	*/
	float GetLastFrameDuration() {
		if (m_TimerActive) return static_cast<float>(m_LastFrameDuration) / 1000000.0f;
		else return 0;
	}

	void StopMainTimer() {
		m_TimerActive = false;
	}
	void ResumeMainTimer() {
		m_TimerActive = true;
	}
	float GetSecondaryFrameDuration() {
		return static_cast<float>(m_LastFrameDuration) / 1000000.0f;
	}

	// Accessor methods

	/**
	 * Returns the width of the output buffer in pixels
	 */
	int         GetDisplayWidth() {
		return m_Width;
	}

	/**
	 * Returns the height of the output buffer in pixels
	 */
	int         GetDisplayHeight() {
		return m_Height;
	}

	/**
	 * Returns the bounding box of the output buffer: (0, 0, Width, Height)
	 */
	BS_Rect    &GetDisplayRect() {
		return m_ScreenRect;
	}

	/**
	 * Returns the bit depth of the output buffer
	 */
	int         GetBitDepth() {
		return m_BitDepth;
	}

	/**
	 * Determines whether the frame buffer change is to be synchronised with Vsync. This is turned on by default.
	 * Notes: In windowed mode, this setting has no effect.
	 * @param Vsync     Indicates whether the frame buffer changes are to be synchronised with Vsync.
	 */
	virtual void    SetVsync(bool Vsync) = 0;

	/**
	 * Returns true if V-Sync is on.
	 * Notes: In windowed mode, this setting has no effect.
	 */
	virtual bool    GetVsync() const = 0;

	/**
	 * Returns true if the engine is running in Windowed mode.
	 */
	bool    IsWindowed() {
		return m_Windowed;
	}

	/**
	 * Fills a rectangular area of the frame buffer with a colour.
	 * Notes: It is possible to create transparent rectangles by passing a colour with an Alpha value of 255.
	 * @param FillRectPtr   Pointer to a BS_Rect, which specifies the section of the frame buffer to be filled.
	 * If the rectangle falls partly off-screen, then it is automatically trimmed.
	 * If a NULL value is passed, then the entire image is to be filled.
	 * @param Color         The 32-bit colour with which the area is to be filled. The default is BS_RGB(0, 0, 0) (black)
	    @remark Falls das Rechteck nicht v�llig innerhalb des Bildschirms ist, wird es automatisch zurechtgestutzt.
	 */
	virtual bool Fill(const BS_Rect *FillRectPtr = 0, unsigned int Color = BS_RGB(0, 0, 0)) = 0;

	// Debugging Methods

	int GetFPSCount() const {
		return m_FPSCounter.GetFPS();
	}
	int GetRepaintedPixels() const {
		return m_RepaintedPixels;
	}

	Graphics::Surface _backSurface;
	Graphics::Surface *getSurface() { return &_backSurface; }

	// Access methods

	/**
	 * Returns the size of a pixel entry in bytes for a particular colour format
	 * @param ColorFormat   The desired colour format. The parameter must be of type COLOR_FORMATS
	 * @return              Returns the size of a pixel in bytes. If the colour format is unknown, -1 is returned.
	 */
	static int GetPixelSize(BS_GraphicEngine::COLOR_FORMATS ColorFormat) {
		switch (ColorFormat) {
		case BS_GraphicEngine::CF_RGB16:
		case BS_GraphicEngine::CF_RGB15:
			return 2;

		case BS_GraphicEngine::CF_RGB16_INTERLEAVED:
		case BS_GraphicEngine::CF_RGB15_INTERLEAVED:
			return 3;

		case BS_GraphicEngine::CF_ARGB32:
			return 4;
		}

		return -1;
	}

	/**
	 * Calculates the length of an image line in bytes, depending on a given colour format.
	 * @param ColorFormat   The colour format
	 * @param Width         The width of the line in pixels
	 * @return              Reflects the length of the line in bytes. If the colour format is
	 * unknown, -1 is returned
	 */
	static int CalcPitch(BS_GraphicEngine::COLOR_FORMATS ColorFormat, int Width) {
		switch (ColorFormat) {
		case BS_GraphicEngine::CF_RGB16:
		case BS_GraphicEngine::CF_RGB15:
			return Width * 2;

		case BS_GraphicEngine::CF_RGB16_INTERLEAVED:
		case BS_GraphicEngine::CF_RGB15_INTERLEAVED:
			return (Width + 3) / 4 * 12;

		case BS_GraphicEngine::CF_ARGB32:
		case BS_GraphicEngine::CF_ABGR32:
			return Width * 4;

		default:
			BS_ASSERT(false);
		}

		return -1;
	}

	// Persistence Methods
	// -------------------
	virtual bool Persist(BS_OutputPersistenceBlock &Writer);
	virtual bool Unpersist(BS_InputPersistenceBlock &Reader);

	static void ARGBColorToLuaColor(lua_State *L, unsigned int Color);
	static unsigned int LuaColorToARGBColor(lua_State *L, int StackIndex);

protected:
	// Constructor
	// -----------
	BS_GraphicEngine(BS_Kernel *pKernel);

	// Display Variables
	// -----------------
	int     m_Width;
	int     m_Height;
	BS_Rect m_ScreenRect;
	int     m_BitDepth;
	bool    m_Windowed;

	// Debugging Variables
	// -------------------
	BS_Framecounter m_FPSCounter;

	unsigned int    m_RepaintedPixels;

	/**
	 * Calculates the time since the last frame beginning has passed.
	 */
	void UpdateLastFrameDuration();

private:
	bool RegisterScriptBindings();

	// LastFrameDuration Variables
	// ---------------------------
	uint64                      m_LastTimeStamp;
	unsigned int                m_LastFrameDuration;
	bool                        m_TimerActive;
	Common::Array<unsigned int> m_FrameTimeSamples;
	unsigned int                m_FrameTimeSampleSlot;
};

} // End of namespace Sword25

#endif