/* 
 *
 * GLX Hardware Device Driver for Sun Creator/Creator3D
 * Copyright (C) 2000, 2001 David S. Miller
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * DAVID MILLER, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, 
 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 
 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE 
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 *
 *    David S. Miller <davem@redhat.com>
 */

#include "ffb_xmesa.h"
#include "ffb_context.h"
#include "ffb_vb.h"
#include "main/imports.h"
#include "tnl/t_context.h"
#include "swrast_setup/swrast_setup.h"
#include "math/m_translate.h"

#undef VB_DEBUG

static void ffb_copy_pv_oneside(GLcontext *ctx, GLuint edst, GLuint esrc)
{
	ffbContextPtr fmesa = FFB_CONTEXT(ctx);
	ffb_vertex *dst = &fmesa->verts[edst];
	ffb_vertex *src = &fmesa->verts[esrc];

#ifdef VB_DEBUG
	fprintf(stderr, "ffb_copy_pv_oneside: edst(%d) esrc(%d)\n", edst, esrc);
#endif
	dst->color[0].alpha = src->color[0].alpha;
	dst->color[0].red   = src->color[0].red;
	dst->color[0].green = src->color[0].green;
	dst->color[0].blue  = src->color[0].blue;
}

static void ffb_copy_pv_twoside(GLcontext *ctx, GLuint edst, GLuint esrc)
{
	ffbContextPtr fmesa = FFB_CONTEXT(ctx);
	ffb_vertex *dst = &fmesa->verts[edst];
	ffb_vertex *src = &fmesa->verts[esrc];

#ifdef VB_DEBUG
	fprintf(stderr, "ffb_copy_pv_twoside: edst(%d) esrc(%d)\n", edst, esrc);
#endif
	dst->color[0].alpha = src->color[0].alpha;
	dst->color[0].red   = src->color[0].red;
	dst->color[0].green = src->color[0].green;
	dst->color[0].blue  = src->color[0].blue;
	dst->color[1].alpha = src->color[1].alpha;
	dst->color[1].red   = src->color[1].red;
	dst->color[1].green = src->color[1].green;
	dst->color[1].blue  = src->color[1].blue;
}

#define FFB_VB_RGBA_BIT		0x01
#define FFB_VB_XYZ_BIT		0x02
#define FFB_VB_TWOSIDE_BIT	0x04
#define FFB_VB_MAX		0x08

typedef void (*ffb_emit_func)(GLcontext *, GLuint, GLuint);

static struct {
 	ffb_emit_func	emit;
	tnl_interp_func	interp;
} setup_tab[FFB_VB_MAX];


#define IND	(FFB_VB_XYZ_BIT)
#define TAG(x)	x##_w
#include "ffb_vbtmp.h"

#define IND	(FFB_VB_RGBA_BIT)
#define TAG(x)	x##_g
#include "ffb_vbtmp.h"

#define IND	(FFB_VB_XYZ_BIT | FFB_VB_RGBA_BIT)
#define TAG(x)	x##_wg
#include "ffb_vbtmp.h"

#define IND	(FFB_VB_TWOSIDE_BIT)
#define TAG(x)	x##_t
#include "ffb_vbtmp.h"

#define IND	(FFB_VB_XYZ_BIT | FFB_VB_TWOSIDE_BIT)
#define TAG(x)	x##_wt
#include "ffb_vbtmp.h"

#define IND	(FFB_VB_RGBA_BIT | FFB_VB_TWOSIDE_BIT)
#define TAG(x)	x##_gt
#include "ffb_vbtmp.h"

#define IND	(FFB_VB_XYZ_BIT | FFB_VB_RGBA_BIT | FFB_VB_TWOSIDE_BIT)
#define TAG(x)	x##_wgt
#include "ffb_vbtmp.h"

static void init_setup_tab( void )
{
	init_w();
	init_g();
	init_wg();
	init_t();
	init_wt();
	init_gt();
	init_wgt();
}

#ifdef VB_DEBUG
static void ffbPrintSetupFlags(char *msg, GLuint flags)
{
   fprintf(stderr, "%s(%x): %s%s%s\n",
	   msg,
	   (int)flags,
	   (flags & FFB_VB_XYZ_BIT)     ? " xyz," : "", 
	   (flags & FFB_VB_RGBA_BIT)    ? " rgba," : "",
	   (flags & FFB_VB_TWOSIDE_BIT) ? " twoside," : "");
}
#endif

static void ffbDDBuildVertices(GLcontext *ctx, GLuint start, GLuint count, 
			       GLuint newinputs)
{
	ffbContextPtr fmesa = FFB_CONTEXT(ctx);

	newinputs |= fmesa->setupnewinputs;
	fmesa->setupnewinputs = 0;

	if (!newinputs)
		return;

	if (newinputs & VERT_BIT_POS) {
		setup_tab[fmesa->setupindex].emit(ctx, start, count);
	} else {
		GLuint ind = 0;

		if (newinputs & VERT_BIT_COLOR0)
			ind |= (FFB_VB_RGBA_BIT | FFB_VB_TWOSIDE_BIT);

		ind &= fmesa->setupindex;

		if (ind)
			setup_tab[ind].emit(ctx, start, count);
	}
}

void ffbChooseVertexState( GLcontext *ctx )
{
 	TNLcontext *tnl = TNL_CONTEXT(ctx);
	ffbContextPtr fmesa = FFB_CONTEXT(ctx);
	int ind = FFB_VB_XYZ_BIT | FFB_VB_RGBA_BIT;

	if (ctx->_TriangleCaps & DD_TRI_LIGHT_TWOSIDE)
		ind |= FFB_VB_TWOSIDE_BIT;

#ifdef VB_DEBUG
	ffbPrintSetupFlags("ffb: full setup function", ind);
#endif

	fmesa->setupindex = ind;

	tnl->Driver.Render.BuildVertices = ffbDDBuildVertices;
	tnl->Driver.Render.Interp = setup_tab[ind].interp;
	if (ind & FFB_VB_TWOSIDE_BIT)
		tnl->Driver.Render.CopyPV = ffb_copy_pv_twoside;
	else
		tnl->Driver.Render.CopyPV = ffb_copy_pv_oneside;
}

void ffbInitVB( GLcontext *ctx )
{
	ffbContextPtr fmesa = FFB_CONTEXT(ctx);
	GLuint size = TNL_CONTEXT(ctx)->vb.Size;

	fmesa->verts = (ffb_vertex *)ALIGN_MALLOC(size * sizeof(ffb_vertex), 32);

	{
		static int firsttime = 1;
		if (firsttime) {
			init_setup_tab();
			firsttime = 0;
		}
	}
}


void ffbFreeVB( GLcontext *ctx )
{
	ffbContextPtr fmesa = FFB_CONTEXT(ctx);
	if (fmesa->verts) {
		ALIGN_FREE(fmesa->verts);
		fmesa->verts = 0;
	}
}
