#include <conio.h>
#include <stdio.h>
#include <dos.h>
#include "vincent.h"

#define Getmaxx 319
#define Getmaxy 199

#define MAX_X 319
#define MAX_Y 199

#define PALETTE_INDEX       0x03c8
//---defines---
#define     vga256        0x13      // 320  x 200 x 256
#define     text          0x03      // Text mode

#define     ROM_CHAR_SET_SEG 0xF000
#define     ROM_CHAR_SET_OFF 0xFA6E

#define     SCREEN_WIDTH   (unsigned int)320
#define     SCREEN_HEIGHT  (unsigned int)200
#define     CHAR_WIDTH   (unsigned int)8
#define     CHAR_HEIGHT  (unsigned int)8


	#define PALETTE_MASK			0x3c6
	#define PALETTE_REGISTER_RD		0x3c7
	#define PALETTE_REGISTER_WR		0x3c8
	#define PALETTE_DATA			0x3c9


		   ///////////////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////
// G L O B A L S //////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////

unsigned char far *double_buffer = NULL;
unsigned int 	   buffer_height = SCREEN_HEIGHT;
unsigned int       buffer_size = SCREEN_WIDTH*SCREEN_HEIGHT;

D3DSurface* back_buffer = NULL;
D3DTexture* render_target = NULL;
D3DPalette* d3dpal = NULL;
	 byte* tbuffer = NULL;


/////////////////////////////////////////////////////////////////////////////
// F U N C T I O N  P R O T O T Y P E S /////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////


	  ////////// structures
   typedef struct RGB_color_typ
	      {

	      unsigned char red;
	      unsigned char green;
	      unsigned char blue;

	      }RGB_color, *RGB_color_ptr;

   /* Transformed, lit vertex */
#define D3DFVF_TLVERTEX	 D3DFVF_XYZRHW|D3DFVF_DIFFUSE|D3DFVF_TEX1

typedef struct _D3DTLVERTEX 
{
    float sx; /* Screen coordinates */
    float sy;
    float sz;
    float rhw; /* Reciprocal of homogeneous w */
    D3DCOLOR color; /* Vertex color */
    float tu; /* Texture coordinates */
    float tv;
    _D3DTLVERTEX() { }
    _D3DTLVERTEX(const D3DVECTOR& v, float _rhw,
                 D3DCOLOR _color, 
                 float _tu, float _tv)
    { sx = v.x; sy = v.y; sz = v.z; rhw = _rhw;
      color = _color; 
      tu = _tu; tv = _tv;
    }
} D3DTLVERTEX, *LPD3DTLVERTEX;

RGB_color pal[256];

// These are the pointers to hold the addresses

	 unsigned char far *video_buffer = (unsigned char far *)0xA0000000L;
	 unsigned char far *rom_char_set = (unsigned char far *)vincent_data; //0xF000FA6EL;



    class cgraphics
     {
       private:
       // no assignments

       public:
       void plot_pixel_db ( int x, int y, unsigned char color );

	//////////////////////// game programming
void cgraphics::plot_circle_db (int xc, int yc, int r, char color);
void cgraphics::wait(int clicks);
int  cgraphics::create_double_buffer(void);
void cgraphics::show_double_buffer (void);
void cgraphics::delete_double_buffer(void);
void cgraphics::HLine(int x,int y,int color,int length,int thickness);
void cgraphics::VLine(int x,int y,int color,int length,int thickness);
     };

      /////////////FUNCTION DECLARATIONS
void cgraphics::plot_pixel_db ( int x, int y, unsigned char color )
	{
	double_buffer [((y<<8) + (y<<6)) + x] = color;
	}
void cgraphics::plot_circle_db (int xc, int yc, int r, char color)
{
      int x = 0, y = r, d = 2 * (1 - r);

      while(y > 0)
      {
	    plot_pixel_db(xc + x, yc + y, color);
	    plot_pixel_db(xc + x, yc - y, color);
	    plot_pixel_db(xc - x, yc + y, color);
	    plot_pixel_db(xc - x, yc - y, color);
	    if(d + y > 0)
	    {
		  y -= 1;
		  d -= (2 * y * SCREEN_WIDTH / SCREEN_HEIGHT) - 1;
	    }
	    if(x > d)
	    {
		  x += 1;
		  d += (2 * x) + 1;
	    }
      }
}

/* draws random plotlines and plotcircles until a key is pressed in mode 13h */
/* (draws in colors 0 - 63 only) */

void cgraphics::wait(int clicks)
{
#if 0
// this function uses the internal timekeeper (the one that runs
// at 18.2 clicks/sec to time a delay).

unsigned char far *clock = (unsigned char far *)0x0000046CL;

unsigned int now;

// get the current time

now = *clock;

// wait until the time has gone past the cuurent time plus the
// amount we wanted to wait. Note that each tick is
// approximately 55 millisecond.

while (abs(*clock - now) < clicks) {}
#endif

Sleep( clicks * 55 );


} // end delay   */
///////////////////////////////////////////////////////////////////////////

void blit_rect( D3DTexture* src, float left, float top, 
					   float right, float bottom, D3DCOLOR col,float z,
					   float maxu, float maxv)
{
    // calculate rhw
    float rhw=1.0f/(z*990.0f+10.0f);

    // set up rectangle
    D3DTLVERTEX verts[4];
    verts[0]=D3DTLVERTEX(D3DXVECTOR3(left-0.5f,  top-0.5f,    z),rhw,col,0.0f,0.0f); 
    verts[1]=D3DTLVERTEX(D3DXVECTOR3(right-0.5f, top-0.5f,    z),rhw,col,maxu,0.0f);
    verts[2]=D3DTLVERTEX(D3DXVECTOR3(right-0.5f, bottom-0.5f, z),rhw,col,maxu,maxv); 
    verts[3]=D3DTLVERTEX(D3DXVECTOR3(left-0.5f,  bottom-0.5f, z),rhw,col,0.0f,maxv);

	// set the palette
	D3DDevice_SetPalette(0,d3dpal);
    // set the texture
    D3DDevice_SetTexture(0,src);

    // configure shader for vertex type
    D3DDevice_SetVertexShader(D3DFVF_TLVERTEX);

    // draw the rectangle
    D3DDevice_DrawVerticesUP(D3DPT_TRIANGLEFAN,4,verts,sizeof(D3DTLVERTEX));
}

int cgraphics::create_double_buffer(void)
{
	/* get back buffer */
	back_buffer = D3DDevice_GetBackBuffer2(0);

	/* create a texture large enough to handle the screen */
	render_target = D3DDevice_CreateTexture2( 512, 512, 1, 1, 0, D3DFMT_P8, D3DRTYPE_TEXTURE );

	/* create a temporary buffer */
	tbuffer = (byte*) malloc( 512 * 512 );

	if ((double_buffer = (unsigned char far *)
	farmalloc (SCREEN_WIDTH * (SCREEN_HEIGHT+1)))==NULL)
	return(0);
	_fmemset(double_buffer, 0, SCREEN_WIDTH * SCREEN_HEIGHT);

	return(1);
}


///////////////////////////////////////////////////////////////////////////

 void cgraphics::show_double_buffer (void)
 {
#if 0
	 D3DLOCKED_RECT lock;
	 byte* buffer = NULL;

	 /* Update the direct3d palette */
	 D3DCOLOR* colours = NULL;
	 D3DPalette_Lock( d3dpal, &colours, 0 );
	for( int i = 0; i < 256; i++ )
		colours[i] = D3DCOLOR_XRGB( pal[i].red, pal[i].green, pal[i].blue );

	/* Clear the screen */
	D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
		0, 1.0f, 0 );
	
	/* Lock the rendering texture */
	D3DTexture_LockRect( render_target, 0, &lock, NULL, 0 );
	
	/* Manually copy the contents from the double buffer */
	buffer = (byte*) malloc( 512 * 512 );
	XGUnswizzleRect( lock.pBits, 512, 512, NULL, buffer, lock.Pitch, NULL, 1 );

	for( int y = 0; y < SCREEN_HEIGHT; y++ )
		for( int x = 0; x < SCREEN_WIDTH; x++ )
			buffer[((y<<8) + (y<<6)) + x] = double_buffer[((y<<8) + (y<<6)) + x];
	//memcpy( buffer, double_buffer, 320*200 );

	XGSwizzleRect( buffer, lock.Pitch, NULL, lock.pBits, 512, 512, NULL, 1 );

	/* unlock the texture */
	D3DTexture_UnlockRect( render_target, 0 );
	free( buffer );

	/* Render the texture */
	blit_rect( render_target, 30, 30, 610, 450, 0xFFFFFFFF,
		0.0f, float(SCREEN_WIDTH)/512.0f, float(SCREEN_HEIGHT)/512.0f );

	/* Swap the direct3d double buffer */
	D3DDevice_Swap(0);
#else
	 __asm int 3;	/* this should never get called */
#endif

 /*_fmemcpy(&video_buffer[0],
 &double_buffer[0],
 SCREEN_WIDTH*SCREEN_HEIGHT);*/

 }

///////////////////////////////////////////////////////////////////////////

void cgraphics::delete_double_buffer(void)
{
	if( tbuffer )
		free( tbuffer );

	if( back_buffer )
		D3DSurface_Release( back_buffer );

	if (double_buffer)
	  farfree(double_buffer);

}

	 // end class
// system


////////////////// 	inline assembly functions
///////////////////////////////////////////////////////////////////////////

void cgraphics::HLine(int x,int y,int color,int length,int thickness)
    {
    int offset = (y<<6)+(y<<8)+x;
    int i;

    for  (i=0; i<=thickness ; i++)
    {
    memset(double_buffer+offset,color,length);
    offset+=SCREEN_WIDTH;
    }
    }

void cgraphics::VLine(int x,int y,int color,int length,int thickness)
    {
    int offset = (y<<6)+(y<<8)+x;
    int i;

    for  (i=0; i<=length ; i++)
    {
    memset(double_buffer+offset,color,thickness);
    offset+=SCREEN_WIDTH;
    }
    }

///////////////////////////////////////////////////////////////////////////

void waitretrace(void)
{
	D3DDevice_BlockUntilVerticalBlank();

  /*_DX = 0x03DA;

  l1: asm{
	in  al,dx;
	and al,0x08;
	jnz l1;
      }

  l2: asm{
	in  al,dx;
	and al,0x08;
	jz  l2;
      }*/
}



void show_double_buffer(void)
{
// this function copies the double buffer into the video buffer
	D3DLOCKED_RECT lock;

	 /* Update the direct3d palette */
	 D3DCOLOR* colours = NULL;
	 D3DPalette_Lock( d3dpal, &colours, 0 );
	for( int i = 0; i < 256; i++ )
		colours[i] = D3DCOLOR_XRGB( pal[i].red, pal[i].green, pal[i].blue );

	/* Clear the screen */
	D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
		0, 1.0f, 0 );
	
	/* Lock the rendering texture */
	D3DTexture_LockRect( render_target, 0, &lock, NULL, 0 );
	
	/* Manually copy the contents from the double buffer */
	for( int y = 0; y < SCREEN_HEIGHT; y++ )
		for( int x = 0; x < SCREEN_WIDTH; x++ )
			tbuffer[x+(y*lock.Pitch)] = double_buffer[((y<<8) + (y<<6)) + x];

	XGSwizzleRect( tbuffer, lock.Pitch, NULL, lock.pBits, 512, 512, NULL, 1 );

	/* unlock the texture */
	D3DTexture_UnlockRect( render_target, 0 );

	/* Render the texture */
	blit_rect( render_target, 30, 30, 610, 450, 0xFFFFFFFF,
		0.0f, float(SCREEN_WIDTH)/512.0f, float(SCREEN_HEIGHT)/512.0f );

	/* Swap the direct3d double buffer */
	D3DDevice_Swap(0);

	/*_asm{
	     push ds			;
	     mov cx,buffer_size	        ;
	     les di,video_buffer        ;
	     lds si,double_buffer       ;

	     cld                        ;
	     rep movsb                  ;
	     pop ds                     ;
	     }*/
}// end


void default_palette()
{
#if 0
	for (int i = 0; i < 256; i++)
    {
        pal[i].red = (BYTE) (((i >> 5) & 0x07) * 255 / 7);
        pal[i].green = (BYTE) (((i >> 2) & 0x07) * 255 / 7);
        pal[i].blue = (BYTE) (((i >> 0) & 0x03) * 255 / 3);
    }
#else
	memset( pal, 0, sizeof( RGB_color ) * 256 );
#endif
}

void set_palette_register( int index, RGB_color_ptr color )
{
	pal[index].red = color->red;
	pal[index].green = color->green;
	pal[index].blue = color->blue;

	///	      	sets the palette
	///		tell the vga card to update the palette
	//outp (PALETTE_MASK, 0xff);
	////		tell the vga card the number
	//outp (PALETTE_REGISTER_WR ,index);
	//// 		update the rbg
	//outp(PALETTE_DATA , color-> red   );
	//outp(PALETTE_DATA , color-> green );
	//outp(PALETTE_DATA , color-> blue  );
	//  		palette set
}


void get_palette_register( int index, RGB_color_ptr color )
{
	color->red = pal[index].red;
	color->green = pal[index].green;
	color->blue = pal[index].blue;

	///	      	sets the palette
	///		tell the vga card to update the palette
	//outp (PALETTE_MASK, 0xff);
	////		tell the vga card the number
	//outp (PALETTE_REGISTER_RD ,index);
	//// 		update the rbg
	//color-> red   = inp(PALETTE_DATA);
	//color-> green = inp(PALETTE_DATA);
	//color-> blue  = inp(PALETTE_DATA);
	//  		palette set
}

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
RGB_color SystemPalette[256];

    void GetSystemPalette (void)
     {
       for (int index=0;index<256;index++)
		{
		//get the color to fade
	get_palette_register(index,(RGB_color_ptr)&SystemPalette[index]);
		}

     }

     void SetSystemPalette (void)
     {
       for (int index=0;index<256;index++)
		{
		//get the color to fade
	set_palette_register(index,(RGB_color_ptr)&SystemPalette[index]);
		}

     }
/////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
void set_double_buffer(int color)
       {
   memset(double_buffer,color,SCREEN_WIDTH * (SCREEN_HEIGHT));
		 //  DWORD colour = // TODO
		//   D3DDevice_Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL,
		//	   0x00000000, 1.0f, 0 );
       }




void set_video_mode ( int mode )
	{
	 /*union REGS regs_in , regs_out;
	 regs_in.h.ah = 0;
	 regs_in.h.al = (unsigned char)mode;
	 int86 ( 0x10, &regs_in, &regs_out );*/
	
		if( mode == 0x03 )
		{
			D3DSurface_Release(back_buffer);
			D3DTexture_Release(render_target);
			D3DPalette_Release(d3dpal);
			D3DDevice_Release();
			return;
		}

		/* initialize direct3d */
	D3DPRESENT_PARAMETERS d3dpp;
	::ZeroMemory( &d3dpp, sizeof( d3dpp ) );
	d3dpp.BackBufferWidth = 640;
	d3dpp.BackBufferHeight = 480;
	d3dpp.BackBufferCount = 1;
	d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
	d3dpp.EnableAutoDepthStencil = TRUE;
	d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
	d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;

	HRESULT hr = Direct3D_CreateDevice( 0, D3DDEVTYPE_HAL, NULL, 
		D3DCREATE_HARDWARE_VERTEXPROCESSING, &d3dpp, &D3D__pDevice );

	/* set default render states */
	D3DDevice_SetRenderState( D3DRS_LIGHTING, FALSE );
	D3DDevice_SetRenderState( D3DRS_ALPHABLENDENABLE, FALSE );
	D3DDevice_SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
	D3DDevice_SetRenderState( D3DRS_DITHERENABLE, TRUE );
	D3DDevice_SetRenderState( D3DRS_ALPHATESTENABLE, FALSE );
	D3DDevice_SetRenderState( D3DRS_ZENABLE, TRUE );
	D3DDevice_SetRenderState( D3DRS_ZWRITEENABLE, TRUE );

	/* initialize default palette */
	 default_palette();

	 /* create a default direct3d palette */
	d3dpal = D3DDevice_CreatePalette2( D3DPALETTE_256 );

	/* fill the palette with the default palette colours */
	D3DCOLOR* colours = NULL;
	D3DPalette_Lock( d3dpal, &colours, 0 );
	for( int i = 0; i < 256; i++ )
		colours[i] = D3DCOLOR_XRGB( pal[i].red, pal[i].green, pal[i].blue );
	}

void blit_char_db (int xc,int yc,char c,int color,int trans_flag)
{

//this uses the rom char set to blit a character.
//Bits r extracted out of each character byte 2 represent char
#if 1
	int offset, x, y;
	unsigned char far *work_char;
	unsigned char bit_mask=0x80; //starting offset in ROM table

	work_char = rom_char_set + c * CHAR_HEIGHT;
	//COMPUTE THe offset of the video buffer
	offset=(yc<<8)+(yc<<6)+xc;
	for (y=0;y<CHAR_HEIGHT;y++)
	{
	//reset bit mask
		bit_mask=0x80;
		for(x=0;x<CHAR_WIDTH;x++)
		{
			//test for transparent pixel 0 else draw
			if ((*work_char & bit_mask))
			double_buffer[offset+x]=color;
			else if (!trans_flag)
			double_buffer[offset+x]=0;
			//takes care of transparency
			//shift bit mask
			bit_mask= (bit_mask >> 1);
		}//end for
	//move to next line
	offset+=SCREEN_WIDTH;
	work_char++;
	}//end for y
#endif
}//end blit char

void  write(int x,int y,int color,char *string,int trans_flag)
{
//the function blits a string using blit char
	int index;
	for (index=0;string[index]!=0;index++)
	{
	blit_char_db(x+(index<<3),y,string[index],color,trans_flag);
	}//end for
}//end blit strring

void plot_pixel_db ( int x, int y, unsigned char color )
	{
	double_buffer [((y<<8) + (y<<6)) + x] = color;
	}


void putpixel( int x, int y, unsigned char color )
	{
	if ((x>0) && (y>0) && (x<SCREEN_WIDTH) && (y<SCREEN_HEIGHT))
	double_buffer [((y<<8) + (y<<6)) + x] = color;
	}

    int getpixel (int x,int y)
     {
     return ( double_buffer[(y<<6)+(y<<8)+x] );
     }

void plot_line_db (int x, int y, int x2, int y2, char color)
{
      int i, steep = 0, sx, sy, dx, dy, e;

      dx = abs(x2 - x);
      sx = ((x2 - x) > 0) ? 1 : -1;
      dy = abs(y2 - y);
      sy = ((y2 - y) > 0) ? 1 : -1;

      if(dy > dx)
      {
	    steep = 1;
	    x ^= y;  /* swap x and y */
	    y ^= x;
	    x ^= y;
	    dx ^= dy; /* swap dx and dy */
	    dy ^= dx;
	    dx ^= dy;
	    sx ^= sy; /* swap sx and sy */
	    sy ^= sx;
	    sx ^= sy;
      }

      e = 2 * dy - dx;
      for(i = 0;i < dx;i++)
      {
	    if(steep)
		  plot_pixel_db(y, x, color);
	    else  plot_pixel_db(x, y, color);
	    while(e >= 0)
	    {
		  y += sy;
		  e -= 2 * dx;
	    }
	    x += sx;
	    e += 2 * dy;
      }
      plot_pixel_db(x2, y2, color);
}

   int CheckPoint(int xx,int yy)
   {
      if   ((xx > 0) && (xx < Getmaxx))
      { if ((yy > 0) && (yy < Getmaxy))
	{ return (1); } }

     return(0);

   }


   void square_db (int x,int y,int width,int color)
   {
      int xm, ym;
      int xp, yp;

      xm=x-(width>>1);
      xp=x+(width>>1);
      ym=y-(width>>1);
      yp=y+(width>>1);

    if (CheckPoint(xm,ym))
    if (CheckPoint(xp,yp))
    plot_line_db(xm,ym,xp,ym,color);

    if (CheckPoint(xm,ym))
    if (CheckPoint(xp,yp))
    plot_line_db(xm,ym,xm,yp,color);

    if (CheckPoint(xm,ym))
    if (CheckPoint(xp,yp))
    plot_line_db(xp,ym,xp,yp,color);

    if (CheckPoint(xm,ym))
    if (CheckPoint(xp,yp))
    plot_line_db(xm,yp,xp,yp,color);
   }

////////////////////////////////////////////////////////////////////////
void FadeAway (void)
{
//this function fades the light by slowly decresing the
//color values in all color registers.

int pal_reg,index;
RGB_color color;

	for (index=0;index<64;index++)
	{
		for (pal_reg=0;pal_reg<256;pal_reg++)
		{
		//get the color to fade
		get_palette_register(pal_reg,(RGB_color_ptr)&color);

		if (color.red > 5) color.red -=1;
		else
			color.red = 0;

		if (color.green > 5) color.green -=1;
		else
			color.green= 0;

		if (color.blue > 5) color.blue -=1;
		else
			color.blue = 0;

		//set the color to a diminished intensity.

		set_palette_register(pal_reg,(RGB_color_ptr)&color);

		} // end for pal_reg
		show_double_buffer();	// TODO
	// wait a bit
	waitretrace();
	}

	 set_double_buffer(0);
	 show_double_buffer();


}

////////////////////////////////////////////////////////////////////////////

void FadeIn (void)
{
  //this function fades the light by slowly incresing the
  //color values in all color registers.

  int i,j,index;
  RGB_color orignal[256],initial[256];

   // secondary easy way

	//find decrement
	for(i=0; i<256;i++)
		{
 get_palette_register(i,(RGB_color_ptr)&orignal[i]);

		}

	//0 out palette
	for(i=0; i<256;i++)
	{
	initial[i].red=0;
	initial[i].green=0;
	initial[i].blue=0;
	}
	//fade in

	for(i=0;i<256;i++)
	{
	set_palette_register(i,(RGB_color_ptr)&initial[i]);
	}

	for (index=0;index<64;index++)
	{
	    for(j=0;j<256;j++)
	    {

		if (initial[j].red < orignal[j].red)
		{
		initial[j].red+=1;
		}

		if (initial[j].green < orignal[j].green)
		{
		initial[j].green+=1;
		}

		if (initial[j].blue < orignal[j].blue)
		{
		initial[j].blue+=1;
		}

		set_palette_register(j,(RGB_color_ptr)&initial[j]);
	     }

	waitretrace();
     }
   }

////////////////////////////////////////////////////////////////////////////

////////////// closing the screen with lines ////////////////////////////

void smooth (void)
{ int y;

  for (y=1 ; y<99 ; y+=2)
  {
  plot_line_db(0,y,320,y,0);
  plot_line_db(0,199-y,320,199-y,0);
  sleep(20);
  waitretrace();
  show_double_buffer();
  }
}

    void ChangePalette (char col)
{
//this function fades the light by slowly decresing the
//color values in all color registers.

int index;
RGB_color color[256];


		for (index=0;index<256;index++)
		{
		//get the color to fade
	get_palette_register(index,(RGB_color_ptr)&color[index]);
		}
		int count;

	  for (count=0;count<40;count++)
	      {
		for (index=0;index<256;index++)
		{

		if (col == 'b')
		if (color[index].blue  > 0) color[index].blue  -=1;
		if (col == 'g')
		if (color[index].blue  > 0) color[index].green  -=1;
		if (col == 'r')
		if (color[index].blue  > 0) color[index].red  -=1;
		//set the color to a diminished intensity.
		set_palette_register(index,(RGB_color_ptr)&color[index]);
		} // end for pal_reg
	waitretrace();

	     }
	}


