/* GGI-Driver for MESA
 * 
 * Copyright (C) 1997  Uwe Maurer
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 * ---------------------------------------------------------------------
 * This code was derived from the following source of information:
 *
 * svgamesa.c and ddsample.c by Brian Paul
 * 
 */

#include <stdio.h>

#include <ggi/internal/ggi-dl.h>
#include <ggi/mesa/ggimesa_int.h>
#include <ggi/mesa/debug.h>

#include "swrast/swrast.h"
//#include "swrast_setup/swrast_setup.h"
//#include "swrast/s_context.h"
//#include "swrast/s_depth.h"
//#include "swrast/s_triangle.h"

#define FLIP(coord) (LIBGGI_MODE(ggi_ctx->ggi_visual)->visible.y-(coord)-1)

/**********************************************************************/
/*****            Write spans of pixels                           *****/
/**********************************************************************/

void GGIwrite_ci32_span(const GLcontext *ctx,
                         GLuint n, GLint x, GLint y,
                         const GLuint ci[],
                         const GLubyte mask[] )
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	y = FLIP(y);
	if (mask)
	{
		while (n--) {
			if (*mask++)
				ggiPutPixel(ggi_ctx->ggi_visual, x, y, *ci);
			x++;
			ci++;
		}
	}
	else
	{
		while (n--)
			ggiPutPixel(ggi_ctx->ggi_visual, x++, y, *ci++);
	}
}

void GGIwrite_ci8_span(const GLcontext *ctx,
                         GLuint n, GLint x, GLint y,
                         const GLubyte ci[],
                         const GLubyte mask[] )
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	y = FLIP(y);
	if (mask)
	{
		while (n--) {
			if (*mask++)
				ggiPutPixel(ggi_ctx->ggi_visual, x, y, *ci);
			x++;
			ci++;
		}
	}
	else
	{
		while (n--)
			ggiPutPixel(ggi_ctx->ggi_visual, x++, y, *ci++);
	}
}

void GGIwrite_mono_ci_span(const GLcontext *ctx, GLuint n, GLint x, GLint y,
			   const GLuint ci, const GLubyte mask[])
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	y = FLIP(y);
	if (mask)
	{
		while (n--) {
			if (*mask++)
				ggiPutPixel(ggi_ctx->ggi_visual, x, y, ci);
			x++;
		}
	}
	else
	{
		while (n--)
			ggiPutPixel(ggi_ctx->ggi_visual, x++, y, ci);
	}
}

void GGIwrite_mono_rgba_span(const GLcontext *ctx, GLuint n, GLint x, GLint y,
			     const GLchan rgba[4], const GLubyte mask[])
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	ggi_color rgb;
	ggi_pixel col;	

	y = FLIP(y);

	rgb.r = (uint16)(rgba[RCOMP]) << SHIFT;
	rgb.g = (uint16)(rgba[GCOMP]) << SHIFT;
	rgb.b = (uint16)(rgba[BCOMP]) << SHIFT;
	col = ggiMapColor(ggi_ctx->ggi_visual, &rgb);

	if (mask)
	{
		while (n--) {
			if (*mask++)
				ggiPutPixel(ggi_ctx->ggi_visual, x, y, col);
			x++;
		}
	}
	else
	{
		ggiDrawHLine(ggi_ctx->ggi_visual, x, y, n);
	}
}

void GGIwrite_rgba_span( const GLcontext *ctx,
                          GLuint n, GLint x, GLint y,
                          const GLubyte rgba[][4],
                          const GLubyte mask[])
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	ggi_color rgb;
	ggi_pixel col;	
	y = FLIP(y);

	if (mask)
	{
		while (n--) {
			if (*mask++) 
			{
				rgb.r = (uint16)(rgba[0][RCOMP]) << SHIFT;
				rgb.g = (uint16)(rgba[0][GCOMP]) << SHIFT;
				rgb.b = (uint16)(rgba[0][BCOMP]) << SHIFT;
				col = ggiMapColor(ggi_ctx->ggi_visual, &rgb);
				ggiPutPixel(ggi_ctx->ggi_visual, x, y, col);
			}
			x++;
			rgba++;
		}
	}
	else
	{
		while (n--)
		{
			rgb.r = (uint16)(rgba[0][RCOMP]) << SHIFT;
			rgb.g = (uint16)(rgba[0][GCOMP]) << SHIFT;
			rgb.b = (uint16)(rgba[0][BCOMP]) << SHIFT;
			col = ggiMapColor(ggi_ctx->ggi_visual, &rgb);
			ggiPutPixel(ggi_ctx->ggi_visual, x++, y, col);
			rgba++;
		}
	}
}

void GGIwrite_rgb_span( const GLcontext *ctx,
                          GLuint n, GLint x, GLint y,
                          const GLubyte rgba[][3],
                          const GLubyte mask[] )
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	ggi_color rgb;
	ggi_pixel col;	
	y = FLIP(y);

	if (mask)
	{
		while (n--) {
			if (*mask++) 
			{
				rgb.r = (uint16)(rgba[0][RCOMP]) << SHIFT;
				rgb.g = (uint16)(rgba[0][GCOMP]) << SHIFT;
				rgb.b = (uint16)(rgba[0][BCOMP]) << SHIFT;
				col = ggiMapColor(ggi_ctx->ggi_visual, &rgb);
				ggiPutPixel(ggi_ctx->ggi_visual, x, y, col);
			}
			x++;
			rgba++;
		}
	}
	else
	{
		while (n--)
		{
			rgb.r = (uint16)(rgba[0][RCOMP]) << SHIFT;
			rgb.g = (uint16)(rgba[0][GCOMP]) << SHIFT;
			rgb.b = (uint16)(rgba[0][BCOMP]) << SHIFT;
			col = ggiMapColor(ggi_ctx->ggi_visual, &rgb);
			ggiPutPixel(ggi_ctx->ggi_visual, x++, y, col);
			rgba++;
		}
	}
}



/**********************************************************************/
/*****                 Read spans of pixels                       *****/
/**********************************************************************/


void GGIread_ci32_span( const GLcontext *ctx,
                         GLuint n, GLint x, GLint y, GLuint ci[])
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	y = FLIP(y);
	while (n--)
		ggiGetPixel(ggi_ctx->ggi_visual, x++, y, ci++);
}

void GGIread_rgba_span( const GLcontext *ctx,
                         GLuint n, GLint x, GLint y,
                         GLubyte rgba[][4])
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	ggi_color rgb;
	ggi_pixel col;

	y = FLIP(y);

	while (n--)
	{
		ggiGetPixel(ggi_ctx->ggi_visual, x++, y, &col);
		ggiUnmapPixel(ggi_ctx->ggi_visual, col, &rgb);
		rgba[0][RCOMP] = (GLubyte) (rgb.r >> SHIFT);
		rgba[0][GCOMP] = (GLubyte) (rgb.g >> SHIFT);
		rgba[0][BCOMP] = (GLubyte) (rgb.b >> SHIFT);
		rgba[0][ACOMP] = 0;
		rgba++;
	}
}

/**********************************************************************/
/*****                  Write arrays of pixels                    *****/
/**********************************************************************/

void GGIwrite_ci32_pixels( const GLcontext *ctx,
                            GLuint n, const GLint x[], const GLint y[],
                            const GLuint ci[], const GLubyte mask[] )
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	while (n--) {
		if (*mask++)
			ggiPutPixel(ggi_ctx->ggi_visual, *x, FLIP(*y), *ci);
		ci++;
		x++;
		y++;
	}
}

void GGIwrite_mono_ci_pixels(const GLcontext *ctx,
			     GLuint n, const GLint x[], const GLint y[],
			     GLuint ci, const GLubyte mask[])
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	while (n--) {
		if (*mask++)
			ggiPutPixel(ggi_ctx->ggi_visual, *x, FLIP(*y), ci);
		x++;
		y++;
	}
}

void GGIwrite_rgba_pixels( const GLcontext *ctx,
                            GLuint n, const GLint x[], const GLint y[],
                            const GLubyte rgba[][4],
                            const GLubyte mask[] )
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	ggi_pixel col;
	ggi_color rgb;
	while (n--) {
		if (*mask++) {
			rgb.r = (uint16)(rgba[0][RCOMP]) << SHIFT;
			rgb.g = (uint16)(rgba[0][GCOMP]) << SHIFT;
			rgb.b = (uint16)(rgba[0][BCOMP]) << SHIFT;
			col = ggiMapColor(ggi_ctx->ggi_visual, &rgb);
			ggiPutPixel(ggi_ctx->ggi_visual, *x, FLIP(*y), col);
		}
		x++;
		y++;
		rgba++;
	}
}

void GGIwrite_mono_rgba_pixels(const GLcontext *ctx,
			       GLuint n, const GLint x[], const GLint y[],
			       const GLchan rgba[4], const GLubyte mask[])
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	ggi_color rgb;
	ggi_pixel col;	

	rgb.r = (uint16)(rgba[RCOMP]) << SHIFT;
	rgb.g = (uint16)(rgba[GCOMP]) << SHIFT;
	rgb.b = (uint16)(rgba[BCOMP]) << SHIFT;
	col = ggiMapColor(ggi_ctx->ggi_visual, &rgb);
	
	while (n--) {
		if (*mask++)
			ggiPutPixel(ggi_ctx->ggi_visual, *x, FLIP(*y), col);
		x++;
		y++;
	}
}

/**********************************************************************/
/*****                   Read arrays of pixels                    *****/
/**********************************************************************/

void GGIread_ci32_pixels( const GLcontext *ctx,
                           GLuint n, const GLint x[], const GLint y[],
                           GLuint ci[], const GLubyte mask[])
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	while (n--) {
		if (*mask++) 
			ggiGetPixel(ggi_ctx->ggi_visual, *x, FLIP(*y), ci);
		ci++;
		x++;
		y++;
	}
}

void GGIread_rgba_pixels( const GLcontext *ctx,
                           GLuint n, const GLint x[], const GLint y[],
                           GLubyte rgba[][4],
                           const GLubyte mask[] )
{
	ggi_mesa_context_t ggi_ctx = (ggi_mesa_context_t)ctx->DriverCtx;
	ggi_color rgb;
	ggi_pixel col;

	while (n--)
	{
		if (*mask++)
		{
			ggiGetPixel(ggi_ctx->ggi_visual, *x, FLIP(*y), &col);
			ggiUnmapPixel(ggi_ctx->ggi_visual, col, &rgb);
			rgba[0][RCOMP] = rgb.r >> SHIFT; 
			rgba[0][GCOMP] = rgb.g >> SHIFT;
			rgba[0][BCOMP] = rgb.b >> SHIFT;
			rgba[0][ACOMP] = 0;
		}	
		x++;
		y++;
		rgba++;
	}
}

int GGIextend_visual(ggi_visual_t vis)
{
	return 0;
}

//static swrast_tri_func ggimesa_stubs_get_triangle_func(GLcontext *ctx);

int GGIsetup_driver(ggi_mesa_context_t ggi_ctx)
{
	struct swrast_device_driver *swdd =
		_swrast_GetDeviceDriverReference(ggi_ctx->gl_ctx);

	GGIMESADPRINT_CORE("stubs: setup_driver\n");
	
	swdd->WriteRGBASpan	= GGIwrite_rgba_span;
	swdd->WriteRGBSpan	= GGIwrite_rgb_span;
	swdd->WriteMonoRGBASpan = GGIwrite_mono_rgba_span;
	swdd->WriteRGBAPixels   = GGIwrite_rgba_pixels;
	swdd->WriteMonoRGBAPixels = GGIwrite_mono_rgba_pixels;

	swdd->WriteCI32Span     = GGIwrite_ci32_span;
	swdd->WriteCI8Span      = GGIwrite_ci8_span;
	swdd->WriteMonoCISpan	= GGIwrite_mono_ci_span;
	swdd->WriteCI32Pixels   = GGIwrite_ci32_pixels;
	swdd->WriteMonoCIPixels = GGIwrite_mono_ci_pixels;

	swdd->ReadCI32Span 	= GGIread_ci32_span;
	swdd->ReadRGBASpan	= GGIread_rgba_span;
	swdd->ReadCI32Pixels	= GGIread_ci32_pixels;
	swdd->ReadRGBAPixels	= GGIread_rgba_pixels;
	
	return 0;
}

void GGIupdate_state(ggi_mesa_context_t *ctx)
{
	//ctx->Driver.TriangleFunc = _swsetup_Triangle;
}

/*
void GGItriangle_flat(GLcontext *ctx, const SWvertex *v0, const SWvertex *v1, const SWvertex *v2)
{
//#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE	  
	
#define SETUP_CODE	       	\
	ggi_color color;	\
	color.r = v0->color[0];	\
	color.g = v0->color[1];	\
	color.b = v0->color[2];	\
	color.a = v0->color[3];	\
	ggiSetGCForeground(VIS, ggiMapColor(VIS, &color));

#define INNER_LOOP(LEFT,RIGHT,Y)				\
		ggiDrawHLine(VIS,LEFT,FLIP(Y),RIGHT-LEFT);	
		
#include "swrast/s_tritemp.h"
}


static void GGItriangle_flat_depth(GLcontext *ctx, const SWvertex *v0, const SWvertex *v1, const SWvertex *v2)
{
#define INTERP_Z 1
#define DEPTH_TYPE DEFAULT_SOFTWARE_DEPTH_TYPE

#define SETUP_CODE	       	\
	ggi_color color;	\
	color.r = v0->color[0];	\
	color.g = v0->color[1];	\
	color.b = v0->color[2];	\
	color.a = v0->color[3];	\
	ggiSetGCForeground(VIS, ggiMapColor(VIS, &color));
	
#define INNER_LOOP(LEFT,RIGHT,Y)					\
	{								\
	GLint i,xx=LEFT,yy=FLIP(Y),n=RIGHT-LEFT,length=0;		\
	GLint startx=xx;						\
	for (i=0;i<n;i++){						\
		GLdepth z=FixedToDepth(ffz);				\
		if (z<zRow[i])						\
		{							\
			zRow[i]=z;					\
			length++;					\
		}							\
		else							\
		{							\
			if (length)					\
			{ 						\
				ggiDrawHLine(VIS,startx,yy,length);	\
				length=0;				\
			}						\
			startx=xx+i+1;					\
		}							\
		ffz+=fdzdx;						\
	}								\
	if (length) ggiDrawHLine(VIS,startx,yy,length);			\
	}

#include "swrast/s_tritemp.h"
}


static swrast_tri_func ggimesa_stubs_get_triangle_func(GLcontext *ctx)
{
	if (ctx->Stencil.Enabled) return NULL;
	if (ctx->Polygon.SmoothFlag) return NULL;
	if (ctx->Polygon.StippleFlag) return NULL;
	if (ctx->Texture._ReallyEnabled) return NULL;  
	if (ctx->Light.ShadeModel==GL_SMOOTH) return NULL;
	if (ctx->Depth.Test && ctx->Depth.Func != GL_LESS) return NULL;

	if (ctx->Depth.Test) 
	  return GGItriangle_flat_depth;

	return GGItriangle_flat;	
}
*/
static int GGIopen(ggi_visual_t vis, struct ggi_dlhandle *dlh,
			const char *args, void *argptr, uint32 *dlret)
{
       	LIBGGI_MESAEXT(vis)->update_state = GGIupdate_state;
	LIBGGI_MESAEXT(vis)->setup_driver = GGIsetup_driver;

	*dlret = GGI_DL_OPDRAW;
	return 0;
}

int MesaGGIdl_stubs(int func, void **funcptr)
{
	switch (func) {
	case GGIFUNC_open:
		*funcptr = GGIopen;
		return 0;
	case GGIFUNC_exit:
	case GGIFUNC_close:
		*funcptr = NULL;
		return 0;
	default:
		*funcptr = NULL;
	}
	return GGI_ENOTFOUND;
}