/* $Id: dosmesa.c 1.2 1996/12/08 16:13:45 Charlie Exp Charlie $ */

/*
 * Mesa 3-D graphics library
 * Version:  2.0
 * Copyright (C) 1995-1996  Brian Paul
 *
 * 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.
 */


/*
 * $Log: dosmesa.c $
 * Revision 1.2  1996/12/08  16:13:45  Charlie
 * Added VESA support via Scitechs SVGA kit.
 *
 * Revision 1.1  1996/12/08  16:09:52  Charlie
 * Initial revision
 *
 */


/*
 * DOS VGA/VESA/MGL/Mesa interface.
 *
 */

/*
 *
 * TODO: (cw)
 *	 Improve the colour matcher for rgb non vesa modes, its pretty bad and incorrect
 *	 Keyboard interrupt.
 *  Comments and tidy up.
 *  Add support for VESA without SVGAKIT.
 *  DirectX Support.
 *  Better GLIDE Support.
 *  Clear up the #ifdef madness.
 */

#ifdef DOSVGA

#if defined(DOSVGA) && !defined(GLIDE) && !defined(DJGPP) && !defined(UNIVBE) && !defined(MGL)

/* Should help cut down on the crazy #if`s */
#define MODE13 1

#else
#undef MODE13
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string.h>
#include <dos.h>

#ifdef DJGPP
#include <go32.h>
#endif

#ifdef MGL
#include <mgraph.h>
#endif

#include "GL/DOSmesa.h"
#include "context.h"
#include "matrix.h"
#include "types.h"

#ifdef GLIDE

static void glideshutdown( void );
#include "vb.h"
#include "glide.h"

/* the glide modes available, set one */

//#define GLIDE_MODE GR_RESOLUTION_
#define GLIDE_MODE GR_RESOLUTION_800x600
//#define GLIDE_MODE GR_RESOLUTION_640x480

static GrVertex gr_vtx,gr_vtx1,gr_vtx2;

#endif

#ifdef UNIVBE
/* You get this file from Scitechs VESA development kit */
#include "svga.h"

/*
   Set these to the VESA mode you require, the first is for 256 colour modes,
   the other is High colour modes, they must both be the same XRes and YRes.
 */

#define VESA_256COLOUR_MODE 0x11c
#define VESA_HICOLOUR_MODE 0x11f

#endif

struct DOSmesa_context {
   GLcontext *gl_ctx;			/* the core Mesa context */
   GLvisual *gl_vis;				/* describes the color buffer */
   GLframebuffer *gl_buffer;	/* the ancillary buffers */
   GLuint index;					/* current color index */
   GLint red, green, blue;		/* current rgb color */
   GLint width, height;			/* size of color buffer */
   GLint depth;					/* bits per pixel (8,16,24 or 32) */
};

static DOSMesaContext DOSMesa = NULL;    /* the current context */

#ifdef UNIVBE
SV_devCtx *DC=NULL;
int useLinear = TRUE;
SV_modeInfo *mi=NULL;
unsigned long modeNumber = 0;

int activePage = 0;
int visualPage = 1;

#endif

#if defined(MODE13)

/* DOSVGA With no UniVBE support */

unsigned char *video_buffer = (unsigned char *)0xa0000;

#define VID_BUF(x,y) *(video_buffer+x+(y*320))

#if defined(__WATCOMC__) && defined(__386__) && defined(__DOS__)

void setupcopy(void);
void copyscr(void);

/* Watcom C specfic, screen copy and clear */

#pragma aux setupcopy = \
	".386P"\
	"push ebp" \
	"mov esi,video_buffer" \
	"mov edi,0xa0000" \
	"mov ecx,2000" \
	"xor ebp,ebp";

#pragma aux copyscr = \
	".386P" \
	"lop1: mov eax,[esi]"\
	"mov ebx,[esi+4]"\
	"mov edx,[esi+8]"\
	"mov [edi],eax"\
	"mov eax,[esi+12]"\
	"mov dword ptr [esi],ebp"\
	"mov dword ptr [esi+4],ebp"\
	"mov dword ptr [esi+8],ebp"\
	"mov dword ptr [esi+12],ebp"\
	"mov [edi+4],ebx"\
	"mov [edi+8],edx"\
	"mov [edi+12],eax"\
	"mov eax,[esi+16]"\
	"mov ebx,[esi+4+16]"\
	"mov edx,[esi+8+16]"\
	"mov [edi+16],eax"\
	"mov eax,[esi+12+16]"\
	"mov dword ptr [esi+16],ebp"\
	"mov dword ptr [esi+4+16],ebp"\
	"mov dword ptr [esi+8+16],ebp"\
	"mov dword ptr [esi+12+16],ebp"\
	"mov [edi+4+16],ebx"\
	"mov [edi+8+16],edx"\
	"mov [edi+12+16],eax"\
	"add esi,32"\
	"add edi,32"\
	"dec ecx"\
	"jnz lop1"\
	"pop ebp"\
	modify exact [edi esi eax ebx ecx] ;

#endif // WATCOM

#endif // MODE13

/*
 * Convert Mesa window Y coordinate to VGA screen Y coordinate:
 */
#define FLIP(Y)  (DOSMesa->height-(Y)-1)

unsigned short vga_cindex = 32768 ;

static points_func choose_points_function( void );
static line_func choose_line_function( void );
static triangle_func choose_polygon_function( void );
static void fast_tri(GLcontext *ctx,GLuint v0, GLuint v1, GLuint v2, GLuint pv );

static points_func choose_points_function( void )
{
	return NULL;
}

static line_func choose_line_function( void )
{
	return NULL;
}

static triangle_func choose_triangle_function( void )
{
	#if defined(MODE13)
		return	NULL;
	#endif

	#if defined(GLIDE)
		return fast_tri;
	#endif

	return	NULL;
}


#if defined(MODE13)

void setgfxmode(void);
void settextmode(void);

#ifndef DJGPP
#pragma aux setgfxmode = \
	"mov	ax,13h" \
	"int	10h" \
	modify [eax];

#pragma aux settextmode = \
	"mov	ax,3h" \
	"int	10h" \
	modify [eax];
#else
void setgfxmode(void)
{
	union REGS in_regs,out_regs;

	in_regs.x.ax = 0x13;
	int386(0x10,&in_regs,&out_regs);
}

void settextmode(void)
{
	union REGS in_regs,out_regs;

	in_regs.x.ax = 0x3;
	int386(0x10,&in_regs,&out_regs);
}

#endif

int set_video_mode(unsigned short x,unsigned short y,char mode)
{
	setgfxmode();
	return 1;	/* likelyhood of this failing is very small */
}

void restore_video_mode(void)
{
	settextmode();
}

int vga_getcolors(void)
{
	return vga_cindex;
}

int vga_getxdim(void)
{
	return 320;
}

int vga_getydim(void)
{
	return 200;
}

static unsigned short num_rgb_alloc = 1; /* start from 1, zero is black */

/* an unlikely colour */
static unsigned char last_r=1,last_g=255,last_b=99;

extern void set_onecolor(int index,int R,int G,int B);

static unsigned char rgbtable[64][64][64];

void vga_setrgbcolor(int r,int g,int b)
{

/*
 * make this into a translation table
 */

	DOSMesa->red = r;
	DOSMesa->green = g ;
	DOSMesa->blue = b ;

	r/=4; g/=4; b/=4;

	if( (r == last_r) && (g == last_g) && (b == last_b) ) return;

	last_r = r ;
	last_g = g ;
	last_b = b ;

	if(r+g+b == 0 ) {
		DOSMesa->index = 0 ;
		return ;
	}

	if( rgbtable[r][g][b] == 0 ) {
		/* not allocated yet */
		if(num_rgb_alloc<256) {
			DOSMesa->index = num_rgb_alloc;
			set_onecolor(num_rgb_alloc,r,g,b);
			rgbtable[r][g][b] = num_rgb_alloc;
			num_rgb_alloc++;
			return;

		} else {
			/* need to search for a close colour */
			{
				unsigned short pass ;

				for(pass=0;pass<64;pass++) {
					if(r-pass>0) {
						if( rgbtable[r-pass][g][b] !=0 ) {
							rgbtable[r][g][b] = rgbtable[r-pass][g][b];
							DOSMesa->index = rgbtable[r-pass][g][b];
							return;
						}
					}
					if(r+pass<64) {
						if( rgbtable[r+pass][g][b] !=0 ) {
							rgbtable[r][g][b] = rgbtable[r+pass][g][b];
							DOSMesa->index = rgbtable[r+pass][g][b];
							return;
						}
					}
				}
			}
			rgbtable[r][g][b] = rand()%255;
		}
	}
	DOSMesa->index = rgbtable[r][g][b];
}

#if defined(DJGPP)
static int dos_seg;
#endif

void vga_clear(void)
{

/* Check if we`re using watcom and DOS */
#if defined(__WATCOMC__) && defined(__386__) && defined(__DOS__)
	setupcopy();
	copyscr();
#else

#if !defined(UNIVBE) && defined (DJGPP) && !defined(GLIDE)


	asm ("
		pusha
		pushw	%es

		movw	_dos_seg, %es

		movl	_video_buffer, %esi
		movl	$0xa0000, %edi

		movl	$64000, %ecx

		rep	; movsb

		popw	%es
		popa
	");

#endif

#if defined(MODE13)
	/* copy the RAM buffer to the Video memory */
	memcpy((unsigned char *)0xa0000,video_buffer, vga_getxdim()*vga_getydim() );
#endif

#if defined(DJGPP)
	/* clear the RAM buffer */
	memset(video_buffer,0, vga_getxdim()*vga_getydim() );
#endif

#endif

}

#ifndef DEBUG
#define vga_drawpixel(x,y) { VID_BUF(x,y) = DOSMesa->index; }
#else
void vga_drawpixel(x,y)
{
	VID_BUF(x,y) = DOSMesa->index;
}
#endif

int vga_getpixel(int x,int y)
{
	return 1;
}

void vga_flip(void)
{

}

void vga_setcolor(int index)
{
	/* does something, what i`ve no idea */
	DOSMesa->index = index;

}

#endif

#if defined(UNIVBE)

/* UniVBE VESA support */

void set_video_mode(unsigned short x,unsigned short y,char mode)
{
	if( setup_vesa_mode(x,y,mode) == FALSE ) {
		fprintf(stderr,"VESA: Set mode failed\n");
		exit(1);
	}
}

/*
   This is problematic as we don`t know what resolution the user program
   wants when we reach here. This is why the 256 colour and HiColour modes
	should be the same resolution, perhaps i`ll make this an environment
	variable
 */


void check_mi(void)
{
	if(mi!=0) return;

	if(DC==NULL) {
		DC = SV_init( TRUE );
	}

	if(modeNumber == 0 ) {
		modeNumber = VESA_HICOLOUR_MODE;
	}

	SV_getModeInfo(modeNumber,mi);

	return;
}

int setup_vesa_mode(short height,short width,short depth)
{
	if(DC==NULL) {
		DC = SV_init( TRUE );
		/* how many bits per pixel */
		if( depth == 0)
			modeNumber = VESA_256COLOUR_MODE;
		else
			modeNumber = VESA_HICOLOUR_MODE;
	}

	/* Check if correct VESA Version is available */
	if( !DC || DC->VBEVersion < 0x0102) {
		fprintf(stderr,"Require a VESA VBE version 1.2 or higher\n");
		return FALSE;
	}

	/* Check for LFB Supprt */
	if(DC->VBEVersion < 0x0200 ) {
		useLinear = FALSE;
	} else {
		useLinear = TRUE ;
	}

 	/* Get desired mode info */
   if(!SV_getModeInfo( modeNumber, mi))
		return FALSE;

	/* Set VESA mode */
	if(!SV_setMode(modeNumber | svMultiBuffer, FALSE, TRUE, mi->NumberOfPages) )
		return FALSE;

	return TRUE;
}

void restore_video_mode(void)
{
	SV_restoreMode();
}

void vga_clear(void)
{
	SV_clear(0);
}

void vga_flip(void)
{
	activePage = 1-activePage;
	visualPage = 1-activePage;

	SV_setActivePage(activePage);

	/*
	   Change false to true if you`re getting flickering
	   even in double buffer mode, ( sets wait for Vertical retrace  )
	*/
	SV_setVisualPage(visualPage,false);
}

int vga_getcolors(void)
{
	check_mi();
	switch ( mi->BitsPerPixel ) {
		case 8:
			return 256;
		case 15:
		case 16:
			return 32768;
		default:
			return 64000;
	 }
}

int vga_getxdim(void)
{
	check_mi();
	return mi->XResolution;
}

int vga_getydim(void)
{
	check_mi();
	return mi->YResolution;
}

unsigned long current_color = 255;

void vga_setrgbcolor(int r,int g,int b)
{
	DOSMesa->red = r;
	DOSMesa->green = g ;
	DOSMesa->blue = b ;
	current_color = SV_rgbColor(r,g,b);
}

void vga_setcolor(int index)
{
	DOSMesa->index = index;
	current_color = index;
}

void vga_drawpixel(x,y)
{
	SV_putPixel(x,y,current_color);
}

/* TODO: */
int vga_getpixel(x,y)
{
/*	return (int)SV_getPixel(x,y); */
	fprintf(stderr,"vga_getpixel: Not implemented yet\n");
	return 1;
}

/* End of UNIVBE section */
#endif

/* Scitechs MegaGraphicsLibrary http://www.scitechsoft.com/ */

#if defined(MGL)

/* MGL support */
struct MI {
	unsigned short BitsPerPixel;
	unsigned long XResolution;
	unsigned long YResolution;
};

struct MI*mi;

static MGLDC*DC;
static int activePage = 0;
static int visualPage = 1;
static int modeNumber = 0;

void set_video_mode(unsigned short xres,unsigned short yres,char mode)
{
    int     i,driver = grDETECT,dmode = grDETECT;
    event_t evt;

	/* Start the MGL with only the SVGA 16m driver active */
	MGL_registerDriver(MGL_SVGA16NAME,SVGA16_driver);
	if (!MGL_init(&driver,&dmode,"..\\..\\"))
		MGL_fatalError(MGL_errorMsg(MGL_result()));
	if ((DC = MGL_createDisplayDC(false)) == NULL)
		MGL_fatalError(MGL_errorMsg(MGL_result()));
	MGL_makeCurrentDC(DC);
}

/*
   This is problematic as we don`t know what resolution the user program
   wants when we reach here. This is why the 256 colour and HiColour modes
	should be the same resolution, perhaps i`ll make this an environment
	variable
 */

#define MGL_HICOLOUR_MODE 0


void check_mi(void)
{
	if(mi!=0) return;

	if(DC==NULL) {
//		DC = SV_init( TRUE );
	}

	if(modeNumber == 0 ) {
		modeNumber = MGL_HICOLOUR_MODE;
	}

//	SV_getModeInfo(modeNumber,mi);

	return;
}

void restore_video_mode(void)
{
	MGL_exit();
}

void vga_clear(void)
{
	MGL_clearDevice();
}

void vga_flip(void)
{
	activePage = 1-activePage;
	visualPage = 1-activePage;

//	SV_setActivePage(activePage);

	/*
	   Change false to true if you`re getting flickering
	   even in double buffer mode, ( sets wait for Vertical retrace  )
	*/
//	SV_setVisualPage(visualPage,false);
}

int vga_getcolors(void)
{
	check_mi();
	switch ( mi->BitsPerPixel ) {
		case 8:
			return 256;
		case 15:
		case 16:
			return 32768;
		default:
			return 64000;
	 }
}

int vga_getxdim(void)
{
	check_mi();
	return mi->XResolution;
}

int vga_getydim(void)
{
	check_mi();
	return mi->YResolution;
}

unsigned long current_color = 255;

void vga_setrgbcolor(int r,int g,int b)
{
	DOSMesa->red = r;
	DOSMesa->green = g ;
	DOSMesa->blue = b ;
	current_color = MGL_rgbColor(DC,r,g,b);
}

void vga_setcolor(int index)
{
	DOSMesa->index = index;
	current_color = index;
}

void vga_drawpixel(x,y)
{
	MGL_pixelCoord(x,y);
}

/* TODO: */
int vga_getpixel(x,y)
{
/*	return (int)SV_getPixel(x,y); */
	fprintf(stderr,"vga_getpixel: Not implemented yet\n");
	return 1;
}

/* End of UNIVBE section */
#endif

#ifdef GLIDE

/* GLIDE support */

static GrHwConfiguration hwconfig;

void set_video_mode(unsigned short x,unsigned short y,char mode)
{
	grGlideInit();
	if( grSstQueryHardware( &hwconfig ) ) {
		grSstSelect( 0 ) ;
		if( !grSstOpen( GLIDE_MODE,
							GR_REFRESH_60Hz,
							GR_COLORFORMAT_ABGR,
							GR_ORIGIN_UPPER_LEFT,
							GR_SMOOTHING_ENABLE,
							2 ) ) {
			fprintf(stderr,"Detected 3DFX board, but couldn`t initialize!");
			exit(1);
		}

		grBufferClear( 0, 0, GR_WDEPTHVALUE_FARTHEST);

		grDisableAllEffects();
		atexit( glideshutdown );

//		guColorCombineFunction( GR_COLORCOMBINE_ITRGB );
//		grTexCombineFunction( GR_TMU0, GR_TEXTURECOMBINE_ZERO);
	}
}

void restore_video_mode(void)
{
}

static void glideshutdown( void )
{
	grGlideShutdown() ;
}

void vga_clear(void)
{
	grBufferSwap(0);
	grBufferClear( 0, 0, GR_WDEPTHVALUE_FARTHEST);
}

void vga_flip(void)
{
}

int vga_getcolors(void)
{
	return 32768;
}

int vga_getxdim(void)
{
#if GLIDE_MODE == GR_RESOLUTION_800x600
		return 800;
#else
		return 640;
#endif
}

int vga_getydim(void)
{
#if GLIDE_MODE == GR_RESOLUTION_800x600
		return 600;
#else
		return 480;
#endif
}

unsigned long current_color = 255;

void vga_setrgbcolor(int r,int g,int b)
{
	DOSMesa->red = r;
	DOSMesa->green = g ;
	DOSMesa->blue = b ;
}

void vga_setcolor(int index)
{
	DOSMesa->index = index;
}

void vga_drawpixel(x,y)
{

	gr_vtx.x = x;
	gr_vtx.y = y;
	gr_vtx.z = 0;
	gr_vtx.r	= DOSMesa->red;
	gr_vtx.g	= DOSMesa->green;
	gr_vtx.b	= DOSMesa->blue;

	grDrawPoint( &gr_vtx );
}

static void fast_tri(GLcontext *ctx,GLuint v0, GLuint v1, GLuint v2, GLuint pv )
{
   struct vertex_buffer *VB = ctx->VB;
	GLuint shift = ctx->ColorShift  ;

	gr_vtx.z = 0;
	gr_vtx1.z = 0;
	gr_vtx2.z = 0;

   if (VB->MonoColor) {
	  	gr_vtx.r = DOSMesa->red;
   	gr_vtx.g = DOSMesa->green;
   	gr_vtx.b = DOSMesa->blue;
   	gr_vtx1.r = DOSMesa->red;
   	gr_vtx1.g = DOSMesa->green;
   	gr_vtx1.b = DOSMesa->blue;
   	gr_vtx2.r = DOSMesa->red;
   	gr_vtx2.g = DOSMesa->green;
   	gr_vtx2.b = DOSMesa->blue;
   } else {
		if(ctx->Light.ShadeModel == GL_SMOOTH ) {
		  	gr_vtx.r  = FixedToInt( VB->Color[v0][0] );
		  	gr_vtx.g  = FixedToInt( VB->Color[v0][1] );
		  	gr_vtx.b  = FixedToInt( VB->Color[v0][2] );

		  	gr_vtx1.r = FixedToInt( VB->Color[v1][0] );
		  	gr_vtx1.g = FixedToInt( VB->Color[v1][1] );
		  	gr_vtx1.b = FixedToInt( VB->Color[v1][2] );

		  	gr_vtx2.r = FixedToInt( VB->Color[v2][0] );
		  	gr_vtx2.g = FixedToInt( VB->Color[v2][1] );
		  	gr_vtx2.b = FixedToInt( VB->Color[v2][2] );
		} else {
		  	gr_vtx.r  = VB->Color[pv][0] >> shift ;
		  	gr_vtx.g  = VB->Color[pv][1] >> shift ;
		  	gr_vtx.b  = VB->Color[pv][2] >> shift ;

		  	gr_vtx1.r = VB->Color[pv][0] >> shift ;
		  	gr_vtx1.g = VB->Color[pv][1] >> shift ;
		  	gr_vtx1.b = VB->Color[pv][2] >> shift ;

		  	gr_vtx2.r = VB->Color[pv][0] >> shift ;
		  	gr_vtx2.g = VB->Color[pv][1] >> shift ;
		  	gr_vtx2.b = VB->Color[pv][2] >> shift ;
		}
	}

   gr_vtx.x  =       (VB->Win[v0][0] );
   gr_vtx.y  = FLIP( (VB->Win[v0][1] ) );
   gr_vtx1.x =       (VB->Win[v1][0] );
   gr_vtx1.y = FLIP( (VB->Win[v1][1] ) );
   gr_vtx2.x =       (VB->Win[v2][0] );
   gr_vtx2.y = FLIP( (VB->Win[v2][1] ) );

	if(gr_vtx.x <0 || gr_vtx.x > 639 )
		return;
	if(gr_vtx1.x <0 || gr_vtx1.x > 639 )
		return;
	if(gr_vtx2.x <0 || gr_vtx2.x > 639 )
		return;

	if(gr_vtx.y <0 || gr_vtx.y > 479 )
		return;
	if(gr_vtx1.y <0 || gr_vtx1.y > 479 )
		return;
	if(gr_vtx2.y <0 || gr_vtx2.y > 479 )
		return;

	grDrawTriangle( &gr_vtx,&gr_vtx1,&gr_vtx2);
}

void fast_plot(GLcontext *ctx,GLuint first,GLuint last )
{
	struct vertex_buffer *VB = ctx->VB;
	register GLuint i;

	if(VB->MonoColor) {
		/* all same color */

		gr_vtx.r = DOSMesa->red;
		gr_vtx.g = DOSMesa->green;
		gr_vtx.b = DOSMesa->blue;

		for(i=first;i<last;i++) {
			if(VB->Unclipped[i]) {
				gr_vtx.x = VB->Win[i][0];
				gr_vtx.y = FLIP(VB->Win[i][1]);
			}
		}
	}
}

/* TODO: */
int vga_getpixel(x,y)
{
/*	return (int)SV_getPixel(x,y); */
	fprintf(stderr,"vga_getpixel: Not implemented yet\n");
	return 1;
}

/* End of GLIDE section */

#endif // GLIDE
/**********************************************************************/
/*****                 Miscellaneous functions                    *****/
/**********************************************************************/


static void get_buffer_size( GLcontext *ctx, GLuint *width, GLuint *height )
{
   *width = DOSMesa->width = vga_getxdim();
   *height = DOSMesa->height = vga_getydim();
}


/* Set current color index */
static void set_index( GLcontext *ctx, GLuint index )
{
   DOSMesa->index = index;
   /*vga_setcolor( index );*/
}


/* Set current drawing color */
static void set_color( GLcontext *ctx,
                       GLubyte red, GLubyte green,
                       GLubyte blue, GLubyte alpha )
{
   DOSMesa->red = red;
   DOSMesa->green = green;
   DOSMesa->blue = blue;
   vga_setrgbcolor( red, green, blue );
}


static void clear_index( GLcontext *ctx, GLuint index )
{
   /* TODO: Implements glClearIndex() */
}


static void clear_color( GLcontext *ctx,
                         GLubyte red, GLubyte green,
                         GLubyte blue, GLubyte alpha )
{
   /* TODO: Implements glClearColor() */
}


static void clear( GLcontext *ctx,
                   GLboolean all,
                   GLint x, GLint y, GLint width, GLint height )
{
   vga_clear();
}


static GLboolean set_buffer( GLcontext *ctx,
                             GLenum mode )
{
   /* TODO: implement double buffering and use this function to select */
   /* between front and back buffers. */
   return GL_TRUE;
}




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


static void write_index_span( GLcontext *ctx,
                              GLuint n, GLint x, GLint y,
                              const GLuint index[],
                              const GLubyte mask[] )
{
   int i;
   y = FLIP(y);
#ifdef UNIVBE
	SV_beginPixel();
#endif
   for (i=0;i<n;i++,x++) {
      if (mask[i]) {
#ifdef UNIVBE
		SV_putPixelFast(x,y,current_color);
#else
         vga_setcolor( index[i] );
         vga_drawpixel( x, y );
#endif
      }
   }

#ifdef UNIVBE
	SV_endPixel();
#endif

}



static void write_monoindex_span( GLcontext *ctx,
                                  GLuint n, GLint x, GLint y,
                                  const GLubyte mask[] )
{
   int i;
   y = FLIP(y);
   /* use current color index */
   vga_setcolor( DOSMesa->index );
#ifdef UNIVBE
	SV_beginPixel();
#endif
   for (i=0;i<n;i++,x++) {
      if (mask[i]) {
#ifdef UNIVBE
			SV_putPixelFast(x,y,current_color);
#else
         vga_drawpixel( x, y );
#endif
      }
   }
#ifdef UNIVBE
	SV_endPixel();
#endif
}



static void write_color_span( GLcontext *ctx,
                              GLuint n, GLint x, GLint y,
                              const GLubyte red[], const GLubyte green[],
                              const GLubyte blue[], const GLubyte alpha[],
                              const GLubyte mask[] )
{
   int i;
   y=FLIP(y);
#ifdef UNIVBE
	SV_beginPixel();
#endif
   if (mask) {
      /* draw some pixels */
      for (i=0; i<n; i++, x++) {
         if (mask[i]) {
#ifdef UNIVBE
				SV_putPixelFast(x,y,SV_rgbColor(red[i], green[i], blue[i]) );
#else
            vga_setrgbcolor( red[i], green[i], blue[i] );
            vga_drawpixel( x, y );
#endif
         }
      }
   }
   else {
      /* draw all pixels */
      for (i=0; i<n; i++, x++) {
#ifdef UNIVBE
			SV_putPixelFast(x,y,SV_rgbColor(red[i], green[i], blue[i]) );
#else
         vga_setrgbcolor( red[i], green[i], blue[i] );
         vga_drawpixel( x, y );
#endif
      }
   }
#ifdef UNIVBE
	SV_endPixel();
#endif
}



static void write_monocolor_span( GLcontext *ctx,
                                  GLuint n, GLint x, GLint y,
                                  const GLubyte mask[])
{
   int i;
   y=FLIP(y);
#ifdef UNIVBE
	SV_beginPixel();
#endif
   /* use current rgb color */
   vga_setrgbcolor( DOSMesa->red, DOSMesa->green, DOSMesa->blue );
   for (i=0; i<n; i++, x++) {
      if (mask[i]) {
#ifdef UNIVBE
			SV_putPixelFast(x,y,current_color);
#else
         vga_drawpixel( x, y );
#endif
      }
   }
#ifdef UNIVBE
	SV_endPixel();
#endif
}



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


static void read_index_span( GLcontext *ctx,
                             GLuint n, GLint x, GLint y, GLuint index[])
{
   int i;
   y = FLIP(y);
   for (i=0; i<n; i++,x++) {
      index[i] = vga_getpixel( x, y );
   }
}



static void read_color_span( GLcontext *ctx,
                             GLuint n, GLint x, GLint y,
                             GLubyte red[], GLubyte green[],
                             GLubyte blue[], GLubyte alpha[] )
{
   int i;
   for (i=0; i<n; i++, x++) {
      /* TODO */
   }
}



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


static void write_index_pixels( GLcontext *ctx,
                                GLuint n, const GLint x[], const GLint y[],
                                const GLuint index[], const GLubyte mask[] )
{
   int i;
#ifdef UNIVBE
	SV_beginPixel();
#endif
   for (i=0; i<n; i++) {
      if (mask[i]) {
#ifdef UNIVBE
			SV_putPixelFast(x[i], FLIP(y[i]), index[i] );
#else
         vga_setcolor( index[i] );
         vga_drawpixel( x[i], FLIP(y[i]) );
#endif
      }
   }
#ifdef UNIVBE
	SV_endPixel();
#endif
}



static void write_monoindex_pixels( GLcontext *ctx,
                                    GLuint n,
                                    const GLint x[], const GLint y[],
                                    const GLubyte mask[] )
{
   int i;
   /* use current color index */
   vga_setcolor( DOSMesa->index );
#ifdef UNIVBE
	SV_beginPixel();
#endif
   for (i=0; i<n; i++) {
      if (mask[i]) {
#ifdef UNIVBE
			SV_putPixelFast(x[i], FLIP(y[i]), DOSMesa->index);
#else
         vga_drawpixel( x[i], FLIP(y[i]) );
#endif
      }
   }
#ifdef UNIVBE
	SV_endPixel();
#endif
}



static void write_color_pixels( GLcontext *ctx,
                                GLuint n, const GLint x[], const GLint y[],
                                const GLubyte r[], const GLubyte g[],
                                const GLubyte b[], const GLubyte a[],
                                const GLubyte mask[] )
{
   int i;
#ifdef UNIVBE
	SV_beginPixel();
#endif
   for (i=0; i<n; i++) {
      if (mask[i]) {
#ifdef UNIVBE
			SV_putPixelFast(x[i], FLIP(y[i]), SV_rgbColor(r[i], g[i], b[i]) );
#else
         vga_setrgbcolor( r[i], g[i], b[i] );
         vga_drawpixel( x[i], FLIP(y[i]) );
#endif
      }
   }
#ifdef UNIVBE
	SV_endPixel();
#endif
}

static void write_monocolor_pixels( GLcontext *ctx,
                                    GLuint n,
                                    const GLint x[], const GLint y[],
                                    const GLubyte mask[] )
{
   int i;
   /* use current rgb color */
   vga_setrgbcolor( DOSMesa->red, DOSMesa->green, DOSMesa->blue );
#ifdef UNIVBE
	SV_beginPixel();
#endif
   for (i=0; i<n; i++) {
      if (mask[i]) {
#ifdef UNIVBE
			SV_putPixelFast(x[i], FLIP(y[i]), current_color );
#else
         vga_drawpixel( x[i], FLIP(y[i]) );
#endif
      }
   }
#ifdef UNIVBE
	SV_endPixel();
#endif
}

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

/* Read an array of color index pixels. */
static void read_index_pixels( GLcontext *ctx,
                               GLuint n, const GLint x[], const GLint y[],
                               GLuint index[], const GLubyte mask[] )
{
   int i;
   for (i=0; i<n; i++) {
      index[i] = vga_getpixel( x[i], FLIP(y[i]) );
   }
}



static void read_color_pixels( GLcontext *ctx,
                               GLuint n, const GLint x[], const GLint y[],
                               GLubyte red[], GLubyte green[],
                               GLubyte blue[], GLubyte alpha[],
                               const GLubyte mask[] )
{
   /* TODO */
}

static void DOSmesa_setup_DD_pointers( GLcontext *ctx )
{
   /* Initialize all the pointers in the DD struct.  Do this whenever */
   /* a new context is made current or we change buffers via set_buffer! */

   ctx->Driver.UpdateState = DOSmesa_setup_DD_pointers;

   ctx->Driver.ClearIndex = clear_index;
   ctx->Driver.ClearColor = clear_color;
   ctx->Driver.Clear = clear;

   ctx->Driver.Index = set_index;
   ctx->Driver.Color = set_color;

   ctx->Driver.SetBuffer = set_buffer;
   ctx->Driver.GetBufferSize = get_buffer_size;

   ctx->Driver.PointsFunc = choose_points_function();
   ctx->Driver.LineFunc = choose_line_function();
   ctx->Driver.TriangleFunc = choose_triangle_function();


   /* Pixel/span writing functions: */
   /* TODO: use different funcs for 8, 16, 32-bit depths */
   ctx->Driver.WriteColorSpan       = write_color_span;
   ctx->Driver.WriteMonocolorSpan   = write_monocolor_span;
   ctx->Driver.WriteColorPixels     = write_color_pixels;
   ctx->Driver.WriteMonocolorPixels = write_monocolor_pixels;
   ctx->Driver.WriteIndexSpan       = write_index_span;
   ctx->Driver.WriteMonoindexSpan   = write_monoindex_span;
   ctx->Driver.WriteIndexPixels     = write_index_pixels;
   ctx->Driver.WriteMonoindexPixels = write_monoindex_pixels;

   /* Pixel/span reading functions: */
   /* TODO: use different funcs for 8, 16, 32-bit depths */
   ctx->Driver.ReadIndexSpan = read_index_span;
   ctx->Driver.ReadColorSpan = read_color_span;
   ctx->Driver.ReadIndexPixels = read_index_pixels;
   ctx->Driver.ReadColorPixels = read_color_pixels;
}

/*
 * Create a new VGA/Mesa context and return a handle to it.
 */
DOSMesaContext DOSMesaCreateContext( void )
{
   DOSMesaContext ctx;
   GLboolean rgb_flag;
   GLfloat redscale, greenscale, bluescale, alphascale;
   GLboolean db_flag = GL_FALSE;
   GLboolean alpha_flag = GL_FALSE;
   int colors;
   GLint index_bits;

#if !defined(UNIVBE) && !defined(GLIDE) && !defined(MGL)
	video_buffer = (unsigned char *) malloc( vga_getxdim() * vga_getydim() );

	memset(video_buffer,0, vga_getxdim() * vga_getydim() );

	memset(rgbtable,0,sizeof( rgbtable ) );
#endif

#if defined( DJGPP ) && !defined(UNIVBE) && !defined(GLIDE)
	dos_seg = _go32_conventional_mem_selector();
#endif

   /* determine if we're in RGB or color index mode */
   colors = vga_getcolors();
   if (colors==32768) {
      rgb_flag = GL_TRUE;
      redscale = greenscale = bluescale = alphascale = 255.0;
      index_bits = 0;
   }
   else if (colors==256) {
      rgb_flag = GL_FALSE;
      redscale = greenscale = bluescale = alphascale = 0.0;
      index_bits = 8;
   }
   else {
		restore_video_mode();
      fprintf(stderr,"[%d] >16 bit color not implemented yet!\n",colors);
      return NULL;
   }

   ctx = (DOSMesaContext) calloc( 1, sizeof(struct DOSmesa_context) );
   if (!ctx) {
      return NULL;
   }

   ctx->gl_vis = gl_create_visual( rgb_flag,
                                   alpha_flag,
                                   db_flag,
                                   16,   /* depth_size */
                                   8,    /* stencil_size */
                                   16,   /* accum_size */
                                   index_bits,
                                   redscale,
                                   greenscale,
                                   bluescale,
                                   alphascale );

   ctx->gl_ctx = gl_create_context( ctx->gl_vis,
                                    NULL,  /* share list context */
                                    (void *) ctx
                                  );

   ctx->gl_buffer = gl_create_framebuffer( ctx->gl_vis );

   ctx->index = 1;
   ctx->red = ctx->green = ctx->blue = 255;

   ctx->width = ctx->height = 0;  /* temporary until first "make-current" */

   return ctx;
}

/*
 * Destroy the given VGA/Mesa context.
 */
void DOSMesaDestroyContext( DOSMesaContext ctx )
{
   if (ctx) {
      gl_destroy_visual( ctx->gl_vis );
      gl_destroy_context( ctx->gl_ctx );
      gl_destroy_framebuffer( ctx->gl_buffer );
      free( ctx );
      if (ctx==DOSMesa) {
         DOSMesa = NULL;
      }
   }
}



/*
 * Make the specified VGA/Mesa context the current one.
 */
void DOSMesaMakeCurrent( DOSMesaContext ctx )
{
   DOSMesa = ctx;
   gl_make_current( ctx->gl_ctx, ctx->gl_buffer );
   DOSmesa_setup_DD_pointers( ctx->gl_ctx );

   if (ctx->width==0 || ctx->height==0) {
      /* setup initial viewport */
      ctx->width = vga_getxdim();
      ctx->height = vga_getydim();
      gl_Viewport( ctx->gl_ctx, 0, 0, ctx->width, ctx->height );
   }
}



/*
 * Return a handle to the current VGA/Mesa context.
 */
DOSMesaContext DOSMesaGetCurrentContext( void )
{
   return DOSMesa;
}


/*
 * Swap front/back buffers for current context if double buffered.
 */
void DOSMesaSwapBuffers( void )
{
#if !defined(UNIVBE)
/* Assume double buffering is available if in UNIVBE,
   if it isn`t its taken care of anyway */
//   if (DOSMesa->gl_vis->DBflag)
#endif
 	{
      vga_flip();
   }
}


#else

/*
 * Need this to provide at least one external definition when DOS is
 * not defined on the compiler command line.
 */

int gl_DOS_dummy_function(void)
{
   return 0;
}

#endif  /*DOS*/

