/*-
 * Copyright (c) 1999,2000 Qube Software, Ltd.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 * 
 * 1. Redistributions of the source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 2. Any redistribution solely in binary form must conspicuously
 *    reproduce the following disclaimer in documentation provided with the
 *    binary redistribution.
 * 
 * THIS SOFTWARE IS PROVIDED ``AS IS'', WITHOUT ANY WARRANTIES, EXPRESS
 * OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  LICENSOR SHALL
 * NOT BE LIABLE FOR ANY LOSS OR DAMAGES RESULTING FROM THE USE OF THIS
 * SOFTWARE, EITHER ALONE OR IN COMBINATION WITH ANY OTHER SOFTWARE.
 * 
 * 	$Id: draw.h,v 1.73 2000/06/15 10:43:21 peterj Exp $
 */

#ifndef __draw_h__
#define __draw_h__

#ifndef DRAW_BEGIN
#define DRAW_BEGIN	namespace Draw {
#define DRAW_END	}
#endif

#include "linkage.h"

#include "utils.h"

#ifdef _MSC_VER
#pragma warning(disable : 4786) // truncated debug info
#endif

// class ostream;

DRAW_BEGIN


struct Event;
struct Factory;
struct Renderer;
struct WindowInfo;

/**
 * Geometric data is presented to the api in a flexible format.  Each
 * field is either present or not present but if present must be in
 * the order declared below.  The fields have fixed sizes and are
 * packed together with a natural alignment depending on the
 * datatypes. 
 *
 * The different possible fields are:
 *
 *	Coord		Model space coordinate (Vec3f)
 *	RHW		Reciprocal w coordinate (internal use only)
 *	Normal		Model space normal direction (Vec3f)
 *	MaterialColor	Input color to lighting (modulates material)
 *	PrimaryColor	Output color from lighting (typically diffuse)
 *	SecondaryColor	Output color from lighting (typically specular)
 *	TexCoord1	Texture coordinates for first texture (Vec2f)
 *	TexCoord2	Texture coordinates for second texture (Vec2f)
 *	TexCoord3	Texture coordinates for third texture (Vec2f)
 *	TexCoord4	Texture coordinates for fourth texture (Vec2f)
 *
 * Colors are specified in a packed integer representation with 8 bits 
 * per channel.  The red component is in bits 0-7, green in bits 8-15, 
 * blue in bits 16-23 and alpha in bits 24-31.
 *
 * The intention is that a simple structure can be defined by the
 * application to describe its chosen vertex format.  Such structures
 * are not declared here since the number of combinations is
 * prohibitively large.
 */
struct FVF
{
    enum {
				// Alignment		Size
	Coord		= 0x001,// sizeof(float)	3 * sizeof(float)
	RHW		= 0x002, // sizeof(float)	sizeof(float)
	BlendMask	= 0x01c, // sizeof(float)	n * sizeof(float)
	BlendCount	= 8,
	BlendShift	= 2,
	Normal		= 0x020, // sizeof(float)	3 * sizeof(float)
	MaterialColor	= 0x040, // sizeof(int)		sizeof(int)
	PrimaryColor	= 0x080, // sizeof(int)		sizeof(int)
	SecondaryColor	= 0x100, // sizeof(int)		sizeof(int)
	TexCoordMask	= 0xe00, // sizeof(float)	2 * n * sizeof(float)
	TexCoordCount	= 8,
	TexCoordShift	= 9
    };

    static int TexCoord(unsigned int n) {
	return n << TexCoordShift;
    }
    static int Blend(unsigned int n) {
	return n << BlendShift;
    }
};

/**
 * Vertices are managed by VertexArray objects. The VertexArray
 * controlls access to the memory used for the vertices using a
 * locking system. This allows an implementation which used
 * asynchronous rendering (either using threads or in hardware) to
 * safely use the contents of the array.
 *
 * A VertexArray can either manage its own memory if it is created
 * using Factory::createVertexArray(unsigned int, unsigned int) or it 
 * can use externally managed memory.
 *
 * The format of vertices in the array is fixed when it is created but 
 * the size can be changed at any time by calling resize(unsigned int) 
 * or resize(unsigned int, void*) for a VertexArray with extenally
 * managed memory.
 */
struct VertexArray : public Utils::Object
{
    enum Access {
	ReadOnly,		// Read only access
	ReadWrite,		// Read/write access (reading is really bad
				// for arrays allocated in video memory)

	WriteOnly,		// Generic write only access
	Truncate,		// Truncate write access (hardware can do
				// buffer renaming, to avoid being stalled on
				// a lock.
	Append			// Append mode writing, means no locks are
				// needed for writing
    };

    /**
     * Destroy the vertex array, reclaiming memory.
     */
    virtual	       ~VertexArray() {}

    /**
     * Return a count of how many vertices are in the array.
     */
    virtual unsigned int size() const = 0;
    
    /**
     * Return the format of the vertices.
     */
    virtual unsigned int format() const = 0;

    /**
     * Return the size of an individual vertex.
     */
    virtual unsigned int vertexSize() const = 0;

    /**
     * Resize the array. If the array is increasing in size, new
     * vertices will contain default values.
     */
    virtual void	resize(unsigned int newsize) = 0;

    /**
     * Notify a VertexArray with externally managed memory that the
     * external vertex array has changed size and/or moved.
     */
    virtual void	resize(unsigned int newsize, void* contents) = 0;

    /**
     * Reserve space for allocating vertices. This is a hint that the
     * size of the array may be increased to this size as a result of
     * calling add.
     */
    virtual void	reserve(unsigned int newsize) = 0;

    /**
     * Return a pointer to the vertex at the given index.  If the
     * array is not locked, an exception will be generated.
     */
    virtual void*	at(unsigned int index) = 0;

    /**
     * Set one vertex in the array to the given value (which should
     * point at memory formatted according to the array's vertex
     * format).
     */
    virtual void	set(unsigned int index, const void* value) = 0;

    /**
     * Add a vertex to the array. The array is first searched to find
     * if a vertex with the same contents exists and if so, that index 
     * is returned. The array keeps a reference count for each vertex
     * added in this way.
     */
    virtual unsigned int add(const void* value) = 0;

    /**
     * Increment the reference count of a vertex in the array.
     */
    virtual void	addRef(unsigned int index) = 0;

    /**
     * Decrement the reference count of a vertex in the array and if
     * the count reaches zero, add the vertex to a list of free
     * vertices. When a vertex is allocated using add(void*), vertices 
     * are taken from the free list before extending the array.
     */
    virtual void	decRef(unsigned int index) = 0;

    /**
     * Return the reference count of a vertex.
     */
    virtual unsigned int refCount(unsigned int index) = 0;

    /**
     * Lock the array to obtain a pointer to its contents. If the
     * array is already locked (either by another thread in the
     * application or internally for asynchronous rendering) then the
     * calling thread blocks until the array is unlocked.
     */
    virtual void*	lock(Access a = ReadWrite) = 0;

    /**
     * Unlock the array.
     */
    virtual void	unlock() = 0;

    /**
     * Optimize the vertex array. Once optimized, the vertex array can
     * nolonger be locked.
     */
    virtual void	optimize() = 0;

    /**
     * Return the maximum size of the vertex array, or 0 for unlimited.
     */
    virtual unsigned int maxSize() const = 0;
};

/**
 * A PrimitiveArray contains a number of primitives (e.g. lines,
 * triangles etc.). Each primitive is represented by some number of
 * Elements (which depends on the primitive). For 'strip' type
 * primitives, a number of Elements is added before the first
 * primitive to 'prime' things (for instance a TriStrip array contains
 * two Elements followed by one element per triangle in the strip.
 */
struct PrimitiveArray : public Utils::Object
{
    enum Type {
	Points,
	Lines,
	Triangles,
	Quads,
	LineStrip,
	TriStrip,
	TriFan,
	QuadStrip
    };
    typedef unsigned short Element;

    /**
     * Destroy the primitive array, reclaiming memory.
     */
    virtual	       ~PrimitiveArray() {}

    /**
     * Return the factory which owns this primitive array.
     */
    virtual Factory*	factory() const = 0;

    /**
     * Return a count of how many primitives are in the array.
     */
    virtual unsigned int size() const = 0;
    
    /**
     * Return the type of the primitives.
     */
    virtual Type	type() const = 0;

    /**
     * Resize the array. If the array is increasing in size, new
     * vertices will contain default values.
     */
    virtual void	resize(unsigned int newsize) = 0;

    /**
     * Notify a PrimitiveArray with externally managed memory that the
     * external primitive array has changed size and/or moved.
     */
    virtual void	resize(unsigned int newsize, Element* contents) = 0;

    /**
     * Return a pointer to the primitive at the given index.  If the
     * array is not locked, an exception will be generated.
     *
     * Note that for 'strip' type primitives (TriStrip, TriFan and
     * QuadStrip), the pointer returned for index zero does not
     * include the startup overhead (i.e. the first two vertices for
     * TriStrip). The first two vertices can be accessed either by
     * using negative indices or by obtaining a pointer to the start
     * of the array using lock().
     */
    virtual Element*	at(unsigned int index) = 0;

    /**
     * Set one primitive in the array to the given value (which should
     * point at memory formatted according to the array's primitive
     * format).
     *
     * For 'strip' type primitives, the pointer should point at the
     * unique part of the primitive (i.e. not including the first two
     * vertices for TriStrip).
     */
    virtual void	set(unsigned int index, const Element* value) = 0;

    /**
     * Append one primitive to the end of the array. The appropriate
     * variant of add() should be used depending on the primitive type 
     * (e.g. add(Element, Element, Element) for Triangles).
     */
    virtual void	add(Element e1) = 0;
    virtual void	add(Element e1, Element e2) = 0;
    virtual void	add(Element e1, Element e2, Element e3) = 0;
    virtual void	add(Element e1, Element e2,
			    Element e3, Element e4) = 0;

    /**
     * Lock the array to obtain a pointer to its contents. If the
     * array is already locked (either by another thread in the
     * application or internally for asynchronous rendering) then the
     * calling thread blocks until the array is unlocked.
     */
    virtual Element*	lock() = 0;

    /**
     * Unlock the array.
     */
    virtual void	unlock() = 0;

    /**
     * Optimize (reorder) the array
     */
    virtual void	optimize() = 0;
    
    /**
     * Test intersection of a ray with 'count' primitives from the array
     * starting at 'start'.
     * Note Lines are not tested for intersection.
     */
    virtual bool	rayIntersect(VertexArray* vertices,
                                     const Maths::Rayf& ray,
                                     unsigned int start,
                                     unsigned int count,
                                     unsigned int& hitIndex,
                                     float& hitDistance,
                                     unsigned int hitVertexFormat,
                                     void *hitVertex) = 0;
};

/**
 * Lights are defined by a position in world coordinates. The
 * coordinate for the position is specified as a homogeneous
 * coordinate (i.e. to get the regular 3-element vector, divide by the
 * w component). This allows directional lights to be defined as
 * lights with a position having a w component of zero; in this case,
 * the x, y and z components specify a vector pointing to the light.
 * For point and spotlights, the w component should be non-zero and the
 * position of the light is given by (x/w, y/w, z/w).
 *
 * For spotlights (which must have a non-zero w component in their
 * position), the angle of influence of the is given by two
 * angles. For simple positional light, both these angles should be
 * set to zero. Within the inner angle, the light the same as a
 * non-spotlight at the same position. Between the inner and outer
 * angle, the intensity of the light falls off smoothly so that no
 * light at all is generated outside the outer angle.
 *
 * Non-directional lights have a range parameter which specifies
 * a cut-off distance for the light. In addition, a falloff equation
 * is given as the reciprocal of a quadratic equation (falloff0 +
 * distance * falloff1 + distance * distance * falloff2).
 */
struct Light
{
    enum Type {
	Directional,
	Point,
	Spot
    };

    /**
     * Initialise a directional light with the vector pointing to the
     * light set to v.
     */
    void EXPORT_QDRAW	directional(const Maths::Vec3f& v);

    /**
     * Initialise a simple point light at position p with the given
     * range and no falloff.
     */
    void EXPORT_QDRAW	point(const Maths::Vec3f& p, float r = 1e10);

    /**
     * Initialise a spotlight with position p and angles inner and 
     * outer (in radians) with the given range and no falloff.
     */
    void EXPORT_QDRAW	spot(const Maths::Vec3f& p, const Maths::Vec3f& d,
			     float inner, float outer,
			     float r = 1e10);

    bool		disabled;	// true if this light should be ignored
    Type		type;		// light type
    Maths::Vec4f	position;	// position or direction of the light
    Maths::Vec3f	color;		// light color
    float		startRange;	// start of range of influence
    float		endRange;	// end of range of influence
    Maths::Vec3f	direction;	// direction of spot
    float		innerAngle;	// inside angle of spotlight
    float		outerAngle;	// outer angle of spotlight
    float		falloff0;	// constant falloff value
    float		falloff1;	// linear falloff value
    float		falloff2;	// quadratic falloff value
};

struct Material
{
    Maths::Vec3f	emissive;	// emissive color
    Maths::Vec3f	ambient;	// ambient color
    Maths::Vec3f	diffuse;	// diffuse color
    Maths::Vec3f	specular;	// specular color
    float		shininess;      // specular exponent    
    float		brilliance;     // diffuse exponent
};

struct LightManager : public Utils::Object
{
    /**
     * Various option flags for controlling the lighting process.
     */
    enum {
	/**
	 * These options allow the MaterialColor field of a vertex to
	 * override various fields of the Material structure used for
	 * lighting.
	 */
	ModulateEmissive = 1,	// replace material emissive with vertex color
	ModulateAmbient = 2,	// replace material ambient with vertex color
	ModulateDiffuse = 4,	// replace material diffuse with vertex color
	ModulateSpecular = 8,	// replace material specular with vertex color

	/**
	 * If the LocalViewer option is used, the maths used for
	 * specular reflection takes into account the position of the
	 * viewer, otherwise it assumes that the viewer is located an
	 * infinite distance away to simplify calculations.
	 */
	LocalViewer = 16,

	/**
	 * If the Renormalize option is used, normals are
	 * re-normalised after transform which allows non-normalised
	 * normalized normals to be used in the model and also allows
	 * the use of scaling and shearing transforms.
	 */
	Renormalize = 32,

	/**
	 * If the CopySharedFields options is used, then fields which
	 * are common to both the source and destination vertex format 
	 * are copied during the lighting process. Without this
	 * option, only the PrimaryColor and SecondaryColor fields of
	 * the destination vertex are modified.
	 */
	CopySharedFields = 64,

	/**
	 * Is fogging enabled. Fog is always vertex, range based fog.
	 */
	Fogging = 128
    };

    enum FogType {
	LinearFog,
	ExponentialFog,
	Exponential2Fog
    };
    
    /**
     * Destroy the light manager, reclaiming memory.
     */
    virtual	       ~LightManager() {}

    /**
     * Return the renderer which owns this light manager.
     */
    virtual Renderer*	renderer() const = 0;

    /**
     * Set the list of lights for the manager. Any existing lights are
     * discarded and replaced by the new list.
     */
    virtual void	set(Light* lights, unsigned int lightCount) = 0;

    /**
     * Set an individual light. If the index passed is greater than or 
     * equal to the light count, an exception is generated.
     */
    virtual void	set(unsigned int index, const Light& light) = 0;

    /**
     * Disable a light (prevent it from taking part in lighting
     * calculations.
     */
    virtual void	disable(unsigned int index) = 0;

    /**
     * Enable a light.
     */
    virtual void	enable(unsigned int index) = 0;

    /**
     * Get the description of an individual light.
     */
    virtual void	get(unsigned int index, Light& light) const = 0; 

    /**
     * Reserve space for at least this many lights. If the number of
     * lights is being increased, new lights are added as disabled
     * directional lights with a position of (0, 0, 1, 0).
     */
    virtual void	reserve(unsigned int count) = 0;

    /**
     * Return a count of the number of lights in the manager.
     */
    virtual unsigned int size() const = 0;

    /**
     * Set the ambient light level.
     */
    virtual void	setAmbient(const Maths::Vec3f& ambient) = 0;

    /**
     * Get the ambient light level.
     */
    virtual Maths::Vec3f ambient() const = 0;

    /**
     * Set the lighting options (material options, specular options).
     */
    virtual void	setOptions(unsigned int options) = 0;

    /**
     * Get the options.
     */
    virtual unsigned int options() const = 0;

    virtual void	setFogType(FogType) = 0;
    virtual void	setFogColor(const Maths::Color4b&) = 0;
    virtual void	setFogStart(float) = 0;
    virtual void	setFogEnd(float) = 0;
    virtual void	setFogDensity(float) = 0;

    virtual FogType	fogType() const = 0;
    virtual Maths::Color4b fogColor() const = 0;
    virtual float	fogStart() const = 0;
    virtual float	fogEnd() const = 0;
    virtual float	fogDensity() const = 0;
    
    /**
     * If this method is called, the manager may optimise the internal 
     * representation of the lighting state to allow more efficient
     * lighting. This optimisation process may be moderately
     * expensive, so there is no point in optimising when the state is 
     * changing often.
     */
    virtual void	optimize() = 0;

    /**
     * Light vertices, copying from one array to another, converting
     * the format based on the format of the input and output
     * vertices.
     *
     * The input vertices are transformed by the given matrix before
     * being lit. Typically the matrix which transforms model
     * coordinates to world coordinates is used. 
     */
    virtual void	lightVertices(VertexArray* inVertices,
				      VertexArray* outVertices,
				      const Material& material,
				      const Maths::Matrix4f& matrix,
				      const Maths::Vec3f& eye) = 0;

    /**
     * Light a subset of the vertices in a vertex array, placing the
     * results at a specified place in the output array.
     */
    virtual void	lightVertices(VertexArray* inVertices,
				      unsigned int inStart,
				      unsigned int inCount,
				      VertexArray* outVertices,
				      unsigned int outStart,
				      const Material& material,
				      const Maths::Matrix4f& matrix,
				      const Maths::Vec3f& eye) = 0;
};

/**
 * A texture is a stack of 2d pixel arrays (mip maps).
 */
struct Texture : public Utils::Object
{
    /**
     * Destroy the texture, reclaiming memory.
     */
    virtual	       ~Texture() {}

    /**
     * Return the renderer which owns this texture.
     */
    virtual Renderer*	renderer() const = 0;

    /**
     * Return the number of mip levels in the texture.
     */
    virtual unsigned int levels() const = 0;

    /**
     * Return the number of mip levels currently loaded.
     */
    virtual unsigned int loadedLevels() const = 0;
    
    /**
     * Load texels into a mip level. The level must be less than or equal to
     * the number of valid levels.
     *
     * If format is 0 then the data is raw image data and the first byte of
     * the data should represent the bottom left of the image. Otherwise
     * format should be the mime type of the image data loaded. 
     */
    virtual void	loadLevel(unsigned int, const unsigned char[],
				  const char* format = 0) = 0;

    /**
     * Increase the number of valid levels in the texture by providing the
     * data for the new top level. Returns wether the operation actually took
     * place or not. If texture memory is low then adding more data would just
     * degrade performance.
     */
    virtual bool	addLevel(const unsigned char[],
				 const char* format = 0) = 0;
};

/**
 * Blend functions for shader stages. In the following, the source
 * color is the color of the pixel fragment being rendered and the
 * destination color is the color of the framebuffer. The shader also
 * maintains a seperate blend color which is used for the constant
 * color cases.
 */
struct Blend
{
    enum {
	Zero,				// 0
	One,				// 1
	SrcColor,			// src
	OneMinusSrcColor,		// 1 - src
	DstColor,			// dst
	OneMinusDstColor,		// 1 - dst
	SrcAlpha,			// (srcA, srcA, srcA, srcA)
	OneMinusSrcAlpha,		// 1 - (srcA, srcA, srcA, srcA)
	DstAlpha,			// (dstA, dstA, dstA, dstA)
	OneMinusDstAlpha,		// 1 - (dstA, dstA, dstA, dstA)
    };
};

/**
 * A description of one stage of the shader pipeline.
 */
struct EXPORT_QDRAW ShaderStage
{
    enum State {
	PrimaryColor,		// Enable primary * tex
	SecondaryColor,		// Enable secondary * tex or
				// primary * tex + secondary if PrimaryColor
				// defined also
	TextureTransform	// Enable texture transforms
    };

    /**
     * Default constructor - set disabled to true.
     */
    ShaderStage();

    /**
     * Construct a shader stage from a simple texture. The input color
     * is BothColors, the source blend is One and the destination
     * blend is Zero.
     */
    ShaderStage(Texture*);

    /**
     * Construct a shader given an input color, texture and blend
     * factors.
     */
    ShaderStage(State, Texture*, unsigned int, unsigned int);

    bool		disabled;	// true if stage is disabled
    unsigned int	stateFlags;
    Texture*		texture;	// which texture to read from
    unsigned int	srcBlend;	// source blend function
    unsigned int	dstBlend;	// destination blend function
};

/**
 * A Shader is a texture and an associated set of material
 * properties.  To render a triangle with a particular texture, first
 * create a shader, then set the state of a Buffer to use that
 * shader before rendering the triangle.
 * 
 * @see Buffer
 * @see Renderer
 * @see Material
 */
struct Shader : public Utils::Object
{
    enum State {
	DepthTest,
	DepthWrite,
    };   
 
    /**
     * Destroy the shader, reclaiming memory.
     */
    virtual		~Shader() {}

    /**
     * Return the renderer which owns this shader.
     */
    virtual Renderer*	renderer() const = 0;

    /**
     * Set the parameters for one stage of the shader pipeline
     */
    virtual void	setStage(unsigned int index,
				 const ShaderStage& stage) = 0;

    /**
     * Get the parameters for one stage of the shader pipeline
     */
    virtual void	getStage(unsigned int index,
				 ShaderStage& stage) const = 0;

    /**
     * Return the number of stages in the shader
     */
    virtual unsigned int stageCount() const = 0;

    /**
     * Set rendering states on the shader.
     */
    virtual void	enable(State) = 0;
    virtual void	disable(State) = 0;

    /**
     * Set rendering states on a shader stage.
     */
    virtual void	stageEnable(unsigned int stage,
				    ShaderStage::State) = 0;
    virtual void	stageDisable(unsigned int stage,
				     ShaderStage::State) = 0;
};

/**
 * Clip flags for transformed vertices.
 */
struct Clip {
    enum {
        Left	= 1,
        Right	= 2,
	Top	= 4,
        Bottom	= 8,
        Front	= 16,
        Back	= 32,
        All	= Left|Right|Top|Bottom|Front|Back
    };
};

/**
 * A buffer is a rectangular pixel array which can be rendered to.
 *
 * To render, first set the state of the Buffer, then render triangles or
 * lines.
 *
 * @see DirectDrawSurface
 */
struct Buffer : public Utils::Object
{
    enum CoordinateSystem {
	LeftHanded,
	RightHanded
    };
    enum MatrixType {
	ModelMatrix	= 0,
	Model0Matrix	= 0,
	Model1Matrix	= 1,
	Model2Matrix	= 2,
	Model3Matrix	= 3,
	Model4Matrix	= 4,
	Model5Matrix	= 5,
	Model6Matrix	= 6,
	Model7Matrix	= 7,
	Texture0Matrix	= 8,
	CameraMatrix	= 16,
	ProjectionMatrix = 24
    };

    /**
     * Destroy the buffer, reclaiming memory.
     */
    virtual		~Buffer() {}

    /**
     * Return the renderer which owns this buffer.
     */
    virtual Renderer*	renderer() const = 0;

    /**
     * Return the width and height of the renderer.
     */
    virtual Maths::Vec2f extent() const = 0;

    /**
     * Set the current Shader to use for rendering triangles.
     */
    virtual void	setShader(Shader* shader) = 0;

    /**
     * Return the current Shader.
     */
    virtual Shader*	shader() const = 0;

    /**
     * Set the vertex array which triangle indices reference.  The
     * vertices are in a flexible format (described above).  For
     * vertices which use indexed coordinates, a supplementary array
     * of coordinates is also passed.
     *
     * Implementation note: an implementation which cached information
     * about the vertex arrays during rendering must flush that cache
     * when a new set of vertices is presented by the user.
     */
    virtual void	setVertices(VertexArray* vertices) = 0;

    /**
     * Return the current vertex array.
     */
    virtual VertexArray* vertices() const = 0;

    /**
     * Set the lights which are used to light vertices from the
     * current vertex array. To disable lighting, call
     * setLights(NULL).
     */
    virtual void	setLights(LightManager* lights) = 0;

    /**
     * Return the current light manager.
     */
    virtual LightManager* lights() const = 0;

    /**
     * Set the material to be used for lighting.
     */
    virtual void	setMaterial(const Material& material) = 0;

    /**
     * Get the current material.
     */
    virtual void	getMaterial(Material& material) const = 0;

    /**
     * Set the coordinate system used for rendering. In a left-handed
     * coordinate system, the positive Z axis extends into the window
     * and in a right-handed system, the negative Z axis extends into
     * the window. The default coordinate system is left-handed.
     */
    virtual void	setCoordinateSystem(CoordinateSystem coord) = 0;

    /**
     * Return the current coordinate system.
     */
    virtual CoordinateSystem coordinateSystem() const = 0;

    /**
     * Duplicate the current matrix and push it onto the matrix stack.
     */
    virtual void	pushMatrix(MatrixType type) = 0;

    /**
     * Push the given matrix onto the model matrix stack.
     */
    virtual void	pushMatrix(MatrixType type,
    				   const Maths::Matrix4f& m) = 0;
    virtual void	pushMatrix(MatrixType type,
    				   const Maths::Affine4f& m) = 0;
    /**
     * Set the matrix on the top of the model matrix stack.
     */
    virtual void	setMatrix(MatrixType type,
    				  const Maths::Matrix4f& m) = 0;
    virtual void	setMatrix(MatrixType type,
    				  const Maths::Affine4f& m) = 0;

    /**
     * Multiply a matrix with the matrix on the top of the model
     * matrix * stack and replace the top of the stack with the
     * result.
     */
    virtual void	multiplyMatrix(MatrixType type,
    				       const Maths::Matrix4f& m) = 0;
    virtual void	multiplyMatrix(MatrixType type,
    				       const Maths::Affine4f& m) = 0;

    /**
     * Get the current value of the matrix at the top of one of the
     * matrix stacks.
     */
    virtual void	getMatrix(MatrixType type,
    				  Maths::Matrix4f& m) const = 0;
    virtual void	getMatrix(MatrixType type,
    				  Maths::Affine4f& m) const = 0;

    /**
     * Return the current value of the matrix at the top of one of the 
     * matrix stacks.
     */
    Maths::Matrix4f	matrix(MatrixType type) const {
	Maths::Matrix4f m;
	getMatrix(type, m);
	return m;
    }

    /**
     * Drop the top element of the model matrix stack.
     */
    virtual void	popMatrix(MatrixType type) = 0;

    /**
     * Return the position of the eye point in world coordinates (i.e.
     * the position of the camera).
     */
    virtual Maths::Vec3f worldEye() const = 0;

    /**
     * Set the top of the projection matrix stack to a perspective
     * projection which render's the scene projected onto the given
     * rectangle in the projection plane.  The front clipping plane
     * specifies the position of the projection plane along the z
     * axis.
     */
    virtual void	setFrustum(const Maths::Box2f& plane,
				   float front, float back) = 0;

    /**
     * Set the top of the projection matrix stack to an orthographic
     * projection which render's the given rectangle in the camera's
     * coordinate system.
     */
    virtual void	setOrtho(const Maths::Box2f& rect,
				 float front, float back) = 0;

    /**
     * Duplicate the top of the window matrix and clip rectangle
     * stack.
     */
    virtual void	pushWindow() = 0;

    /**
     * Push a new window transform onto the window matrix stack.  The window
     * transform scales and translates coordinates after the projection
     * so that they fit into a given window on the buffer.
     *
     * The uniformScaling parameter specifies whether the aspect
     * ratio of the virtual projection rectangle should be
     * preserved. If true, the projection rectangle is scaled such
     * that the major axis of the window displays the full size of the 
     * projection rectangle with the other axis being clipped. If
     * uniformScaling is false, then the projection rectangle is
     * scaled to fit exactly within the window.
     *
     * The window stack also includes a clipping rectangle which is
     * set to the window rectangle when a new window is pushed. It is
     * possible to push a new clipping rectangle while preserving the
     * existing window transformation by calling pushClip(const
     * Box2f&).
     */
    virtual void	pushWindow(const Maths::Box2f& rect,
    				   bool uniformScaling) = 0;

    /**
     * Push a new clipping rectangle onto the window stack,
     * maintaining the existing window transform. This can be used for 
     * example to efficiently render through a portal.
     */
    virtual void	pushClip(const Maths::Box2f& clip) = 0;

    /**
     * Set the current window transform, replacing the existing
     * transform and clipping rectangle.
     */
    virtual void	setWindow(const Maths::Box2f& rect,
    				  bool uniformScaling) = 0;

    /**
     * Set the current clipping rectangle, maintaining the existing
     * window transform.
     */
    virtual void	setClip(const Maths::Box2f& clip) = 0;

    /**
     * Pop the current window transform and clipping rectangle from
     * the window stack.
     */
    virtual void	popWindow() = 0;

    /**
     * Get the current clipping rectangle.
     */
    virtual Maths::Box2f clip() const = 0;

    /**
     * Return the current matrix used to transform from world
     * coordinates to eye coordinates (this is just the inverse of the 
     * current camera matrix).
     */
    virtual void	getWorldToEyeMatrix(Maths::Matrix4f& m) const = 0;
    Maths::Matrix4f	worldToEyeMatrix() const {
	Maths::Matrix4f m;
	getWorldToEyeMatrix(m);
	return m;
    }

    /**
     * Return the current matrix used to transform from projection
     * coordinates to window coordinates (based on the viewport
     * dimensions).
     */
    virtual void	getProjectionToWindowMatrix(Maths::Matrix4f& m) const = 0;
    Maths::Matrix4f	projectionToWindowMatrix() const {
	Maths::Matrix4f m;
	getProjectionToWindowMatrix(m);
	return m;
    }

    /**
     * Return the current matrix used to transform from model
     * coordinates to window coordinate for a specific model
     * matrix. This is a concatenation of the model, inverse camera,
     * projection and window matrices.
     */
    virtual void	getModelToWindowMatrix(MatrixType which,
					       Maths::Matrix4f& m) const = 0;
    virtual Maths::Matrix4f modelToWindowMatrix(MatrixType which) const {
	Maths::Matrix4f m;
	getModelToWindowMatrix(which, m);
	return m;
    }

    /**
     * Transform a vector from model coordinates to window coordinates.
     * The vector returned is in homogeneous coordinates.  To convert
     * to 2D coordinates, first test the clip flags to make sure the
     * transformed vector is inside the viewing frustum and then
     * divide the x and y components by the w component.
     */
    virtual void	transform(const Maths::Vec3f& in,
				  Maths::Vec4f& out, int& clip) = 0;

    /**
     * Transform a vector from window coordinates to model
     * coordinates.  This is the opposite of transform(const Vec3f&,
     * Vec4f&, int&).
     */
    virtual void	inverseTransform(const Maths::Vec4f& in,
					 Maths::Vec3f& out) = 0;

    /**
     * Transform a vector from world coordinates to window coordinates.
     * The vector returned is in homogeneous coordinates.  To convert
     * to 2D coordinates, first test the clip flags to make sure the
     * transformed vector is inside the viewing frustum and then
     * divide the x and y components by the w component.
     */
    virtual void	worldTransform(const Maths::Vec3f& in,
				       Maths::Vec4f& out, int& clip) = 0;

    /**
     * Transform a vector from window coordinates to world
     * coordinates.  This is the opposite of transform(const Vec3f&,
     * Vec4f&, int&).
     */
    virtual void	inverseWorldTransform(const Maths::Vec4f& in,
					      Maths::Vec3f& out) = 0;

    /**
     * Transform and clip a polygon and return true if any part of the polygon
     * is visible. The transformed vertices are returned in clippedPolygon in
     * window coordinates and this array should be freed by the caller using
     * delete.
     */
    virtual bool	transformPolygon(unsigned int count,
					 const unsigned short indices[],
					 VertexArray*& clippedPolygon) = 0;

    /**
     * Render a single point.
     */
    virtual void	point(unsigned short i0) = 0;

    /**
     * Render a line with the indexed vertices as endpoints.
     */
    virtual void	line(unsigned short i0,
			     unsigned short i1) = 0;

    /**
     * Render a triangle with three indices into the vertex list.
     */
    virtual void	triangle(unsigned short i0,
				 unsigned short i1,
				 unsigned short i2) = 0;


    /**
     * Render a polygon with a specified number of indexed vertices.
     */
    virtual void	polygon(unsigned int count,
				const unsigned short indices[]) = 0;

    /**
     * Render 'count' primitives from the array starting at 'start'.
     */
    virtual void	render(PrimitiveArray* array,
			       unsigned int start,
			       unsigned int count) = 0;

    /**
     * Render 'count' primitives from the array starting at 'start',
     * specifying the range of vertices used.
     */
    virtual void	render(PrimitiveArray* array,
			       unsigned int start,
			       unsigned int count,
			       unsigned int firstVertex,
			       unsigned int lastVertex) = 0;

    /**
     * Complete any buffered rendering commands.
     */
    virtual void	flush() = 0;

    /**
     * Return the rendered triangle count to date.
     */
    virtual unsigned int triangleCount() = 0;

    /**
     * Reset the triangle count.
     */
    virtual void	resetTriangleCount() = 0;
};

struct InputDevice
{
    enum DeviceType {
	IDTypeUnknown = 0,
	IDTypeKeyboard,
	IDTypeMouse,
	IDTypeJoystick,
	IDTypeGamepad,
    };

    enum AxisMode {
	IDAxisModeUnknown,
	IDAxisModePosition,
	IDAxisModeMove,
	IDAxisModeAcceleration
    };

    // XXX Make this some system value (BIGINT?)
#   define IDAxisNegInfinite	-1000000000
#   define IDAxisPosInfinite	 1000000000

    virtual		~InputDevice() {}

    /**
     * Check the device has started succesfully
     */
    virtual bool	ok() const = 0;

    /**
     * Human readable description of the device (less than 40 characters)
     */
    virtual const char*	deviceDescription() const = 0;

    /**
     * Type of the device. See the types above.
     */
    virtual DeviceType	type() const = 0;

    /**
     * The minimum number of hands required to use the device. This is
     * 1 for most devices. For gamepads and handheld joysticks this method
     * should return 2.
     * Effectively this means that the user cannot do anything with the
     * device when he is using any other device.
     */
    virtual int		minHands() const = 0;

    /**
     * The maximum number of hands one can use with the device. This
     * is 1 for mice, 2 for joysticks with buttons not directly accessible
     * from the hand that holds the stick, etc.
     */
    virtual int		maxHands() const = 0;

    /**
     * Number of buttons on the device. Buttons are push buttons that are
     * released when the user removes pressure.
     * Buttons are numbered from 0 to (buttons()-1).
     */
    virtual int		buttons() const = 0;

    /**
     * A human readable description of the button.
     */
    virtual const char*	buttonDescription(int buttonno) const = 0;

    /**
     * Number of levers on the device. A lever is a device that stays in a
     * certain position when used. A switch is a lever with a maximum of 1.
     * Levers are numbered from 0 to (levers()-1).
     */
    virtual int		sliders() const = 0;

    /**
     * Maximum value returned by this lever. The minimum value is always 0.
     */
    virtual int		sliderMax(int leverno) const = 0;

    /**
     * A human readable description of the slider
     */
    virtual const char*	sliderDescription(int leverno) const = 0;

    /**
     * Number of axes available on this device. Axis are interfaces that
     * have a left-right, up-down, back-forward type of functionality.
     * It can be assumed that the first 3 axes are X,Y,Z.
     */
    virtual int		axes() const = 0;

    /**
     * Minimum value for the axis
     */
    virtual int		axisMin(int axisno) const = 0;

    /**
     * Maximum value for the axis
     */
    virtual int		axisMax(int axisno) const = 0;

    /**
     * Stepsize on the axis (normally 1)
     */
    virtual int		axisStep(int axisno) const = 0;

    /**
     * Human readable description of the device
     */
    virtual const char*	axisDescription(int axisno) const  = 0;

    /**
     * Set/get the current axis mode. If set fails, false is returned.
     */
    virtual bool	setAxisMode(int axisno, AxisMode mode) = 0;
    virtual AxisMode	getAxisMode(int axisno) const = 0;

    /**
     * Point-Of-View controllers
     */
    virtual int		povs() const = 0;

    /**
     * Number of directions on the controller, normally 4 or 8
     */
    virtual int		povDirections(int povno) const = 0;

    /**
     * Human readable description of the device
     */
    virtual const char*	povDescription(int povno) const = 0;
};


struct Renderer : public Utils::Object
{
    /**
     * Destroy the renderer and all renderer-specific objects created
     * from it.
     */
    virtual		~Renderer() {}

    /**
     * Return the factory which owns this renderer.
     */
    virtual Factory*	factory() const = 0;

    /**
     * Return the width and height of the renderer.
     */
    virtual Maths::Vec2f extent() const = 0;

    /**
     * Get the buffer for normal rendering (the 'back buffer').
     */
    virtual Buffer*	backBuffer() = 0;

    /**
     * Create a buffer for offscreen rendering.
     */
    virtual Buffer*	createBuffer(int, int) = 0;

    /**
     * Create a buffer for rendering into a texture.
     */
    virtual Buffer*	createBuffer(Texture*) = 0;

    /**
     * Finish rendering a frame and make the back buffer visible.
     * This implicitly calls flush() on the back buffer.
     */
    virtual void	update() = 0;

    /**
     * Create a shader with no texture.  This needs to be extended for
     * multipass texturing.
     */
    virtual Shader*	createShader() = 0;

    /**
     * Create a shader with the given texture.  This needs to be
     * extended for multipass texturing.
     */
    virtual Shader*	createShader(Texture*) = 0;

    /**
     * Create a texture with a number of mip levels.
     * And a given bit depth (24,32)
     */
    virtual Texture*	createTexture(unsigned int levels,
				      unsigned int bitDepth) = 0;

    /**
     * Create an input device of the specified type with its focus bound
     * to a specific Renderer, spooling its events into events.
     */
    virtual InputDevice* createInputDevice(InputDevice::DeviceType type) = 0;

    virtual InputDevice* createInputDevice(const char* input,
    					   InputDevice::DeviceType type) = 0;

    /**
     * Create a light manager (to control lighting state for a set of
     * lights. Note that you can use more than one light manager to
     * allow different sets of vertices or different models to be lit
     * differently.
     */
    virtual LightManager* createLightManager() = 0;

    /**
     * Create a vertex array with the given format and size.
     *
     * Creating the vertex array here, creates (if possible) a vertex array in
     * the driver only. As a result of this, externally managed memory for the
     * vertex array is not available, neither is vertex allocation, through
     * the use of the add method, incRef, decRef and refCount. This is to
     * eliminate unnecessary memory usage once the array is created.
     * 
     * To use vertex allocation first create and fill a factory vertex array
     * and then create a renderer vertex array and copy the data across and
     * then delete the factory array.
     *
     * NOTE: renederer vertex arrays will have a maximum size, depending on
     * the driver and so multiple renderer arrays might be needed to take a
     * single factory array.
     */
    virtual VertexArray* createVertexArray(unsigned int format,
					   unsigned int size) = 0;

    /**
     * Set the background color used for clearing the buffer.
     */
    virtual void	setBackgroundColor(Maths::Color4b color) = 0;

    /**
     * Get the current background color.
     */
    virtual Maths::Color4b getBackgroundColor() = 0;

    /**
     * Wait for the OS to return the next input event.
     * Return true for a valid event or false if the renderer has shut down.
     * If block is true, then nextEvent will not return until an event is
     * ready, otherwise if no input is ready, it will return immediately
     * with the event.code set to Event::None.
     */
    virtual bool	nextEvent(Event& event, bool block = true) = 0;

    /**
     * Resize the renderer
     */
    virtual void	resize(unsigned int width, unsigned int height) = 0;
    
    /**
     * Return true if the renderer is in fullscreen mode.
     */
    virtual bool	fullscreen() const = 0;

    /**
     * If the argument is true, put the renderer in fullscreen mode
     * (if possible), otherwise put the renderer in windowed mode.
     */
    virtual void	fullscreen(bool mode) = 0;

    /**
     * Shut down the renderer, closing all windows and threads. After calling
     * shutdown(), the application must continue calling nextEvent() until
     * it return false, indicating that it is safe to delete the renderer.
     */
    virtual void	shutdown() = 0;
};

struct Factory : public Utils::Object
{
    /**
     * Create a new factory.
     */
    static EXPORT_QDRAW	Factory* create();

    /**
     * Destroy the factory and all objects created by it.
     */
    virtual		~Factory() = 0;

    /**
     * Create a new renderer using the specified display driver and input
     * drivers.  The name is used as a window title if the display driver is
     * running in a GUI.
     */
    virtual Renderer*	createRenderer(WindowInfo* windowInfo,
    				       const char* name,
				       const char* input,
				       const char* display,
				       unsigned int width, unsigned int height,
				       bool fullscreen = false) = 0;

   /**
    * Create a vertex array with the given format and size.
    *
    * Setting dynamic = true will improve performance on vertex arrays that
    * get locked & unlocked regularly.
    */
    virtual VertexArray* createVertexArray(unsigned int format,
					   unsigned int size,
					   bool dynamic = false) = 0;

    /**
     * Create a vertex array with the given format and size using the
     * memory pointed to by contents to store the vertices.
     *
     * Setting dynamic = true will improve performance on vertex arrays that
     * get locked & unlocked regularly.
     */
    virtual VertexArray* createVertexArray(unsigned int format,
					   unsigned int size,
					   void* contents,
					   bool dynamic = false) = 0;

    /**
     * Create a primitive array with the given format and size.
     */
    virtual PrimitiveArray* createPrimitiveArray(PrimitiveArray::Type type,
						 unsigned int size) = 0;

    /**
     * Create a primitive array with the given format and size using the
     * memory pointed to by contents to store the vertices.
     */
    virtual PrimitiveArray* createPrimitiveArray(PrimitiveArray::Type type,
						 unsigned int size,
						 PrimitiveArray::Element* contents) = 0;
};

DRAW_END

#endif // __draw_h__

// Local Variables:
// mode: c++
// End:
