#include <string.h>

#include <math.h>

#include "Render.h"


// #define USEBOUNDINGBOX

const double PI = 3.141592654;


Renderer::Renderer( HWND hWnd, GameWad* Wad )
{
	
	// Copy over the items we want to know about in the outside world.

	WadFile = Wad;
	ghWnd = hWnd;

	// Allocate the vertex cache.

	VerticesCache = new Vertex[WadFile->NbrVertices];
	BitMapInfo = NULL;
	
	// Initialize the lookup tables.

	initTables();
	
	// Initialize the double buffer DIB.

	init_double_buffer();
}

Renderer::~Renderer(void)
{
	// Clean up the vertex cache.

	if( NULL != VerticesCache)
	{
		delete VerticesCache;
		VerticesCache = NULL;
	}

	return;
}

short Renderer::initTables( void )
{
	double rads;
	int    i;

	// Generate the trig tables.

	for( i=0; i < 8192; ++i )
	{
		rads = (i * PI)/4096.0;
		SinTable[i] = (long) floor(::sin(rads) * 65536.0);
		CosTable[i] = (long) floor(::cos(rads) * 65536.0);
		TanTable[i] = (long) floor(::tan(rads) * 65536.0);
	}

	// Generate the column angle table (for texturing walls)

	for( i=0; i< 640; ++i )
	{
		colangle[i] = (short)((i-320) * 0x4000/960.0);
	}

	// Calculate the inverse tangent table (Slope to Angle) 


	// Calculate the texture scaling table.

	for( i = 1; i <= 8193; ++i )
		DeltaTxtTbl[i-1] = 2097152l / i;

	return 1;
}

long Renderer::sin(short Ang)
{
	// This takes an angle (Binary Angle Measurement, or BAM), and returns the sine function from the table.

	//


	return SinTable[((unsigned short)Ang)>>3];
	//return 65536l * ::sin(Ang * PI / (184.044444 * 180));

}

long Renderer::cos(short Ang)
{
	// This takes a BAM angle, and returns the cosine, from the SinTable.

	//


	return CosTable[((unsigned short)Ang)>>3];
	// return 65536l * ::cos(Ang * PI / (184.044444 * 180));

}

long Renderer::tan(short Ang)
{
	// This takes a BAM angle, and returns the tangent, from the TanTable.

	//


	return TanTable[((unsigned short)Ang)>>3];
	//return 65536l * ::tan(Ang * PI / (184.044444 * 180));

}

short Renderer::invTan(long y, long x)
{
	// This will take the rise and run and calculate the angle (inverse tangent)


	return (short)((180.0 * atan2((float)y,(float)x) * 184.044444)/PI) ;

}

void Renderer::init_double_buffer(void)
{
	//

	// Initialize the double buffer and windows bitmaps that will be used to 

	// render the double buffer.

	//

	HDC hDC;

	if ( NULL == BitMapInfo )
		BitMapInfo = (BITMAPINFO *)malloc(sizeof(BITMAPINFOHEADER) + (256 * sizeof(RGBQUAD)));

	hDC = GetDC(ghWnd);

	BitMapInfo->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); 
	BitMapInfo->bmiHeader.biWidth = 640; 
	BitMapInfo->bmiHeader.biHeight = -400; 
	BitMapInfo->bmiHeader.biPlanes = 1; 
	BitMapInfo->bmiHeader.biBitCount = 8; 
	BitMapInfo->bmiHeader.biCompression = BI_RGB; 
	BitMapInfo->bmiHeader.biSizeImage  = 0; 
	BitMapInfo->bmiHeader.biXPelsPerMeter = 0; 
	BitMapInfo->bmiHeader.biYPelsPerMeter = 0; 
	BitMapInfo->bmiHeader.biClrUsed = 0;
	BitMapInfo->bmiHeader.biClrImportant = 0;
    
	//	Load the palette

	for (int i= 0; i < 256; ++i)
	{
		BitMapInfo->bmiColors[i].rgbRed = WadFile->GamePalettes[0][i*3]; 
		BitMapInfo->bmiColors[i].rgbGreen = WadFile->GamePalettes[0][i*3+1]; 
		BitMapInfo->bmiColors[i].rgbBlue = WadFile->GamePalettes[0][i*3+2]; 
	}
	if (DblBuff != NULL)
  		DeleteObject(DblBuff);

	DblBuff = CreateDIBSection(hDC, (BITMAPINFO *)BitMapInfo, DIB_RGB_COLORS, (VOID **)&DoubleBuffer, 0, 0);

	ReleaseDC(ghWnd,hDC);
}

void Renderer::clearToColor(char c) 
{ 
	//

	// This function clears te screen to the color "C", by blasting the 

	// byte to the double buffer. This used to use a rep movsw, but now we're

	// in windows, and I don't know if that will work anymore.

	//

	memset(DoubleBuffer, c, 256000ul);
} 

void Renderer::ChangePalette ( short PaletteNbr )
{
	//

	// This modifies the double buffers palette. It is used to select one of the 14 palettes

	// stored in the WAD file.

	//

	HDC hDC, hMemDC;
	HBITMAP DefaultBitmap;
 	 RGBQUAD tmpPalette[256];
 	
	if ((PaletteNbr > 14) || (PaletteNbr < 1))
		return;

 	hDC = GetDC(ghWnd);
 	
 	// Create memory device context compatible with the engines window dc

 	hMemDC = CreateCompatibleDC(GetDC(ghWnd));
 
 	// Save the default bitmap and select the engines double buffer into the memory dc

 	DefaultBitmap = SelectObject(hMemDC, DblBuff);
 
 	// Now get the palette for the selected bitmap.

 	GetDIBColorTable(hMemDC, 0, 256, tmpPalette);
  
 	// Modify the palette by the percentage.

 	for (int i=0; i<256; ++i)
 	{
 		tmpPalette[i].rgbRed = (unsigned char )WadFile->GamePalettes[PaletteNbr][i*3]; 
 		tmpPalette[i].rgbGreen = (unsigned char )WadFile->GamePalettes[PaletteNbr][i*3+1]; 
 		tmpPalette[i].rgbBlue = (unsigned char )WadFile->GamePalettes[PaletteNbr][i*3+2]; 
 	}
 	
 	// and now put it back into the bitmap.

 	SetDIBColorTable(hMemDC, 0, 256, tmpPalette);
 
 	// put back the default bitmap

 	SelectObject(hMemDC, DefaultBitmap);
 	
 	// delete the memory dc

 	DeleteDC(hMemDC);
 
 	// delete the default bitmap

 	DeleteObject(DefaultBitmap);
 
 	// release the engines window dc 

 	ReleaseDC(ghWnd,hDC);
 
 }

void Renderer::copyscrn (char *Buffer) 
{ 
	//

	// This method copies a loaded buffer (read PCX file) into the double buffer.

	//

	 memcpy(DoubleBuffer, Buffer, 266000ul);
}

int Renderer::Save_Pcx(char *filename)
{
	return WadFile->Save_Pcx(filename, DoubleBuffer, 640, 400);
}

long Renderer::DistToPoint( long x, long y )
{
    long	dx;
    long	dy;
    long	dist;
	
    dx = abs(x);
    dy = abs(y);
	
	if( dy == 0 )
		dist = dx;
	else if (dx == 0)
		dist = dy;
	else
	{
		

//		dist = (long)(65536l * sqrt ( dx * dx + dy * dy ));


		short ang = invTan(dy,dx);
		dist = (dx * 65536l) / cos(ang);	
	}

    return dist;
}

//

// Project the bounding box, and determine if the area is at least partially

// visible.

//

short Renderer::CheckBoundingBox ( short UpperX, short UpperY,
							       short LowerX, short LowerY )
{
#ifdef USEBOUNDINGBOX


	// There will be 4 points associated with the bounding box:

	short P1x = UpperX - ViewerX;
	short P1y = UpperY - ViewerY;
	short P2x = P1x;
	short P2y = LowerY - ViewerY;
	short P3x = LowerX - ViewerX;
	short P3y = P2y;
	short P4x = P3x;
	short P4y = P1y;
	long  tempX;
	long  tempY;
	
#else


	return 1;

#endif


	
#ifdef USEBOUNDINGBOX


	// Step 2. Rotate the points.

	register long cosine = this->cos(-ViewerAngle);
	register long sine = this->sin(-ViewerAngle);

	//  Transform all vertices to view coordinates, and place new vertices in cache.

	// Now rotate the vertex so that the player is looking down the positive X axis.

	tempX = (long)P1x * cosine - (long)P1y * sine;
	tempY = (long)P1x * sine + (long)P1y * cosine;
	P1x = (short)(tempX >> 16);
	P1y = (short)(tempY >> 16);	

	tempX = (long)P2x * cosine - (long)P2y * sine;
	tempY = (long)P2x * sine + (long)P2y * cosine;
	P2x = (short)(tempX >> 16);
	P2y = (short)(tempY >> 16);	

	tempX = (long)P3x * cosine - (long)P3y * sine;
	tempY = (long)P3x * sine + (long)P3y * cosine;
	P3x = (short)(tempX >> 16);
	P3y = (short)(tempY >> 16);	

	tempX = (long)P4x * cosine - (long)P4y * sine;
	tempY = (long)P4x * sine + (long)P4y * cosine;
	P4x = (short)(tempX >> 16);
	P4y = (short)(tempY >> 16);	
		
	// If the entire box is behind us, there is no need to continue...

	if( P1x < 1 && P2x < 1 && P3x < 1 && P4x < 1 )
		return 0;

	if( P1x >= 1 )
		P1y  = 320l - ((long)P1y * 554l) / (long)P1x;
	else
		P1y = 0;

	if( P2x >= 1 )
		P2y  = 320l - ((long)P2y * 554l) / (long)P2x;
	else
		P2y = 0;

	if( P3x >= 1 )
		P3y  = 320l - ((long)P3y * 554l) / (long)P3x;
	else
		P3y = 0;

	if( P4x >= 1 )
		P4y  = 320l - ((long)P4y * 554l) / (long)P4x;
	else
		P4y = 0;

	// Step 4. Are there any points visible on the screen?

	//         If so, then we need to render this area.

	if ((P1y > 0 && P1y < 640) || (P2y > 0 && P2y < 640) ||
		(P3y > 0 && P3y < 640) || (P4y > 0 && P4y < 640))
		return 1;

	// Else, return False (0).

	return 0;

#endif

}

short Renderer::render( short x, short y )
{
	//

	// This method will use the loaded WAD file, mainly the nodes, sidedefs, and textures, and render

	// a view of the walls from position <x,v> looking at the specified angle. Height will be 64 plus

	// the sector height of the sector we're standing in. The vertices will have already been translated 

	// and rotated by this point.

	//


	// Clear the double buffer.

	clearToColor(0);
	
	// Call TraverseBSPTree() with the root node of the BSP tree.

	return traverseBSPTree(WadFile->MaxNode);
}




short Renderer::getSideofCutPlane ( short X, short Y, short Node )
{
	//

	// Determines which side of a particular Node cutting plane a point <X,Y> is on.

	// returns: 0 - Left

	//          1 - Right

	long left;
	long right;

	if( 0 == WadFile->Nodes[Node].CutPlaneDy )
	{
		if( 0 < WadFile->Nodes[Node].CutPlaneDx )
			return ( Y < WadFile->Nodes[Node].CutPlaneStartY );
		else
			return ( Y > WadFile->Nodes[Node].CutPlaneStartY );
	}
	else if( 0 == WadFile->Nodes[Node].CutPlaneDx )
	{
		if ( 0 < WadFile->Nodes[Node].CutPlaneDy )
			return ( X > WadFile->Nodes[Node].CutPlaneStartX );
		else
			return ( X < WadFile->Nodes[Node].CutPlaneStartX );
	}
	else
	{
		left = (long)WadFile->Nodes[Node].CutPlaneDy * (long)(X - WadFile->Nodes[Node].CutPlaneStartX);
		right = (long)WadFile->Nodes[Node].CutPlaneDx * (long)(Y - WadFile->Nodes[Node].CutPlaneStartY);
		return ( left > right );
	}
}

short Renderer::getSideofLinedef( short X, short Y, short LineDef )
{
	//

	// Determines which side of a particular LineDef a point <X,Y> is on.

	// returns: 0 - Left

	//          1 - Right

	
	long left;
	long right;
	short StartX, StartY;
	short Dx, Dy;

	StartX = WadFile->Vertices[WadFile->Linedefs[LineDef].StartVertex].x;
	StartY = WadFile->Vertices[WadFile->Linedefs[LineDef].StartVertex].y;
	Dx = WadFile->Vertices[WadFile->Linedefs[LineDef].EndVertex].x - StartX;
	Dy = WadFile->Vertices[WadFile->Linedefs[LineDef].EndVertex].y - StartY;

	if( 0 == Dy )
	{
		if( 0 < Dx )
			return ( Y < StartY );
		else
			return ( Y > StartY );
	}
	else if( 0 == Dx )
	{
		if ( 0 < Dy )
			return ( X > StartX );
		else
			return ( X < StartX );
	}
	else
	{
		left = ((long)Dy) * (long)(X - StartX);
		right = ((long)Dx) * (long)(Y - StartY);
		return ( left > right );
	}
}

short Renderer::getSectorfromXY( short X, short Y )
{
	// 

	// This method will traverse the BSP tree and determine which sector the <X,Y> point falls into.

	//

	short CurrentNode;
	short SegNbr;
	short LinedefNbr;

	// Start at the top node of the BSP tree.

	CurrentNode = WadFile->MaxNode;

	// Traverse the tree (in Forward order) until a SSector is reached.

	// a SSector is indicated with bit 16.

	
	while(0 == (unsigned short)(CurrentNode & 0x8000))
	{
		if ( 0 == getSideofCutPlane(X, Y, CurrentNode) )
			CurrentNode = WadFile->Nodes[CurrentNode].LeftChild;
		else
			CurrentNode = WadFile->Nodes[CurrentNode].RightChild;
	}
	
	// Return the sector number from the SSector

	// by way of the Seg, Linedef, and appropriate SideDef...

	SegNbr = WadFile->SSectors[(unsigned)(CurrentNode & 0x7fff)].StartSeg;
	LinedefNbr = WadFile->Segs[SegNbr].Linedef;
	
	if( 1 == (getSideofLinedef(X, Y, LinedefNbr)))
	{
		// If on the right side

		return WadFile->Sidedefs[WadFile->Linedefs[LinedefNbr].RightSideDef].SectorNumber;
	}

	// Else left side

	return WadFile->Sidedefs[WadFile->Linedefs[LinedefNbr].LeftSideDef].SectorNumber;
}

short Renderer::traverseBSPTree( short RootNode )
{
	short CurrentNode;
	short NextNode;
	
	//		Check this pointer. Is it a node or a ssector.

	//			If Node, then recursively call traversal again on it..

	//			If SSector, then call RenderSSector()

	if ( 0 != (unsigned short)(RootNode & 0x8000) )
		return renderSSector( RootNode & 0x7fff );

	// Start at the root node. Determine if we're on the left side, or the right side of it.

	if(0 == getSideofCutPlane(ViewerX, ViewerY, RootNode))
	{
		// If on the left side, traverse the right sub tree first, then the left. 

		CurrentNode = WadFile->Nodes[RootNode].RightChild;
		NextNode = WadFile->Nodes[RootNode].LeftChild;
	}
	else
	{
		// Else on the right, then traverse the left sub tree first, then the right.

		CurrentNode = WadFile->Nodes[RootNode].LeftChild;
		NextNode =  WadFile->Nodes[RootNode].RightChild;
	}


	// 

	//  Check bounding box to see of we even need consider this node.

	// 

	if ( CheckBoundingBox( WadFile->Nodes[RootNode].LeftBBUpperX,
						   WadFile->Nodes[RootNode].LeftBBUpperY,
						   WadFile->Nodes[RootNode].LeftBBLowerX,
						   WadFile->Nodes[RootNode].LeftBBLowerY ))
		traverseBSPTree(CurrentNode);

	// 

	//  Check bounding box to see of we even need consider this node. 

	// 

	if ( CheckBoundingBox( WadFile->Nodes[RootNode].LeftBBUpperX,
						   WadFile->Nodes[RootNode].LeftBBUpperY,
						   WadFile->Nodes[RootNode].LeftBBLowerX,
						   WadFile->Nodes[RootNode].LeftBBLowerY ))
		traverseBSPTree(NextNode);
	return 1;
}

short Renderer::renderSSector( short SSector )
{
	short SegNbr;
	short SegDir;
	short LinedefNbr;
	short ThisSide;
	short BackSide;
	short ThisCeilHeight;
	short BackCeilHeight;
	short ThisFloorHeight;
	short BackFloorHeight;
	long  Pdist;
	long  BOffset;
	
	// For each SEG in the SSector

	for (int i = 0; i < WadFile->SSectors[SSector].NumSegs; ++i)
	{
		// Get the segment number, and the segment direction.

		SegNbr = WadFile->SSectors[SSector].StartSeg + i;
		SegDir = WadFile->Segs[SegNbr].Direction;
			
		// Get the Linedef for the segment

		LinedefNbr = WadFile->Segs[SegNbr].Linedef;
		
		// Get the Sidedef(s) for this Seg and Direction.

		if (1 == SegDir)
		{
			ThisSide = WadFile->Linedefs[LinedefNbr].LeftSideDef;
			BackSide = WadFile->Linedefs[LinedefNbr].RightSideDef;
		}
		else
		{
			ThisSide = WadFile->Linedefs[LinedefNbr].RightSideDef;
			BackSide = WadFile->Linedefs[LinedefNbr].LeftSideDef;
		}

		ThisCeilHeight = WadFile->Sectors[WadFile->Sidedefs[ThisSide].SectorNumber].CeilingHeight;
		ThisFloorHeight = WadFile->Sectors[WadFile->Sidedefs[ThisSide].SectorNumber].FloorHeight;

		// Calculate the shortest distance to the line from the view point.

		short StartAngle = invTan( VerticesCache[WadFile->Segs[SegNbr].StartVertex].y,
			                       VerticesCache[WadFile->Segs[SegNbr].StartVertex].x );

		short NormalAngle = WadFile->Segs[SegNbr].Angle + ANGLE_90 - ViewerAngle;

		long distToStart = DistToPoint(VerticesCache[WadFile->Segs[SegNbr].StartVertex].x, 
			                           VerticesCache[WadFile->Segs[SegNbr].StartVertex].y);

		Pdist = (distToStart * sin(ANGLE_90 + NormalAngle - StartAngle)) >> 16;

		// Now calculate the offset from the above point to the start of the wall.

		BOffset = (distToStart * sin(NormalAngle - StartAngle)) >> 16;

		// Calculate side we're on for backface removal.

		if( 1 == getSideofLinedef(ViewerX,ViewerY,LinedefNbr) ^ SegDir)
			{
				// If 2 sided linedef, then

				if( BackSide > 0)
				{
					//	Get the ceiling and floor height of this sector and the adjoining sector.

					BackCeilHeight = WadFile->Sectors[WadFile->Sidedefs[BackSide].SectorNumber].CeilingHeight;
					BackFloorHeight = WadFile->Sectors[WadFile->Sidedefs[BackSide].SectorNumber].FloorHeight;

					//	If the ceiling heights are different,

					if ( ThisCeilHeight != BackCeilHeight )
					{
						// Build upper polygon (Length of seg, from back height to front height)

						drawWallTopPegged( VerticesCache[WadFile->Segs[SegNbr].StartVertex].x,
									 VerticesCache[WadFile->Segs[SegNbr].StartVertex].y,
									 VerticesCache[WadFile->Segs[SegNbr].EndVertex].x,
									 VerticesCache[WadFile->Segs[SegNbr].EndVertex].y,
								     ThisCeilHeight,
									 BackCeilHeight,
									 WadFile->Segs[SegNbr].Angle,
									 WadFile->Sidedefs[ThisSide].UpperTxtName,
									 WadFile->Sidedefs[ThisSide].TxtureXOffset + 
									 WadFile->Segs[SegNbr].StartOffset,
									 WadFile->Sidedefs[ThisSide].TxtureYOffset,
									 Pdist,
									 BOffset );
					}
				
					//  If the floor heights are different,

					if( ThisFloorHeight != BackFloorHeight) 
					{
						// Build lower polygon (Length of seg, from back floor height to front floor height)

						drawWallBtmPegged( VerticesCache[WadFile->Segs[SegNbr].StartVertex].x,
								 	 VerticesCache[WadFile->Segs[SegNbr].StartVertex].y,
									 VerticesCache[WadFile->Segs[SegNbr].EndVertex].x,
									 VerticesCache[WadFile->Segs[SegNbr].EndVertex].y,
									 BackFloorHeight,
									 ThisFloorHeight,
									 WadFile->Segs[SegNbr].Angle,
									 WadFile->Sidedefs[ThisSide].LowerTxtName,
									 WadFile->Sidedefs[ThisSide].TxtureXOffset +
									 WadFile->Segs[SegNbr].StartOffset,
									 WadFile->Sidedefs[ThisSide].TxtureYOffset,
									 Pdist,
									 BOffset );						
					}
				}
	
					// Now draw the center texture.

					drawWallTopPegged( VerticesCache[WadFile->Segs[SegNbr].StartVertex].x,
								 VerticesCache[WadFile->Segs[SegNbr].StartVertex].y,
								 VerticesCache[WadFile->Segs[SegNbr].EndVertex].x,
								 VerticesCache[WadFile->Segs[SegNbr].EndVertex].y,
								 ThisCeilHeight,
								 ThisFloorHeight,
								 WadFile->Segs[SegNbr].Angle,
								 WadFile->Sidedefs[ThisSide].NormalTxtName,
								 WadFile->Sidedefs[ThisSide].TxtureXOffset + 
								 WadFile->Segs[SegNbr].StartOffset,
								 WadFile->Sidedefs[ThisSide].TxtureYOffset,
								 Pdist,
								 BOffset );
		}
	}

//	drawFlat(WadFile->SSectors[SSector].StartSeg, WadFile->SSectors[SSector].NumSegs, ThisCeilHeight);

//  drawFlat(WadFile->SSectors[SSector].StartSeg, WadFile->SSectors[SSector].NumSegs, ThisFloorHeight);



	//  *** Implement Later ***

	//		Z-sort the list of Things in this SSector

	//		For each Thing, render it.


	return 1;
}

struct Edge {
	long StartR, StartC;
	long EndR, EndC;
	Edge* nextEdge;

	Edge( long Sr, long Sc, long Er, long Ec )
	{ StartR = Sr; StartC = Sc; EndR = Er; EndC = Ec;};
	~Edge( void)
	{ if( NULL != nextEdge ) delete nextEdge; };
};

short Renderer::drawFlat(unsigned short StartSeg, unsigned short numSegs, short Height)
{

	// A flat is a convex polygon bounded by the segments in a SSector. It has a height, and a texture.

	// The texture is a 64x64 bitmap, and it's origin lies on a 64x64 boundary on the x/y plane. We will 

	// probably use a general polygon filling routine here, noting that X (depth) is constant across the rows.

	//

	// Note: This version does not do the texturing, and is still a work-in-progress...

	//

	short cntr;
	short currSeg;
	short StartX; 
	short StartY;
	short EndX; 
	short EndY;
	long  ColStart;
	long  ColEnd;
	long  RowStart;
	long  RowEnd;
	long  DeltaL;
	long  DeltaR;
	long  StartCol = 0;
	long  EndCol = 0;
	long EndRow = 0;
	Edge  *tmpEdgeList = NULL;
	Edge  *newEdge = NULL;
	
#ifdef DEBUG

	// DEBUG: ********************************************************************

	FILE* OutFile;
	OutFile = fopen("c:\\temp\\edgelist.out","a+");
	fprintf(OutFile, " ----------------------NEW POLYGON----------------------- \n");
	// ***************************************************************************

#endif


	// Step 1. Create a sorted list of SEGs, sorted on startX (view space)... Insertion Sort. 

	//         * Remove any horizontal edges.

	//         * Remove any edges with startX < 1 && endX < 1

	//         * Clip any partially visible edges against X=1 plane.

	for( cntr = 0; cntr < numSegs; ++cntr )
	{
		currSeg = StartSeg + cntr;
		
		// Get the points of interest for this next edge.


		StartX = VerticesCache[WadFile->Segs[currSeg].StartVertex].x;
		StartY = VerticesCache[WadFile->Segs[currSeg].StartVertex].y;
		EndX = VerticesCache[WadFile->Segs[currSeg].EndVertex].x;
		EndY = VerticesCache[WadFile->Segs[currSeg].EndVertex].y;

#ifdef DEBUG

		// DEBUG: ******************************************************************************

		fprintf(OutFile, "Seg: <%d,%d,%d> -> <%d,%d,%d> \n", StartY, Height, StartX, EndY, Height, EndX);
		// **************************************************************************************

#endif


		if (( StartX < 1) && (EndX < 1 ))
			continue;
		
		// We need to clip the incoming segment at the X=1 plane...

		if( StartX < 1 && EndX >= 1 )
		{
			if( StartY != EndY )
				StartY = StartY + ((1-StartX) * ( EndY - StartY )) / (EndX - StartX);	
			StartX = 1;
		}
		if( StartX >= 1 && EndX < 1)
		{
			if( StartY != EndY )
				EndY = StartY + ((1-StartX) * ( EndY - StartY )) / (EndX - StartX);
			EndX = 1;
		}		
#ifdef DEBUG

		// DEBUG: ******************************************************************************

		fprintf(OutFile, "Clipped: <%d,%d,%d> -> <%d,%d,%d> \n", StartY, (Height - ViewerHeight), StartX, EndY, (Height - ViewerHeight), EndX);
		// **************************************************************************************

#endif


		// Transform the points.

		ColStart  =  320l - (((long)StartY * 554l) / (long)StartX);
		RowStart  =  240l - (((long)(Height - ViewerHeight) * 554l) / (long)StartX);
		ColEnd    =  320l - (((long)EndY * 554l) / (long)EndX);
		RowEnd    =  240l - (((long)(Height - ViewerHeight) * 554l) / (long)EndX);

#ifdef DEBUG

		// DEBUG: ******************************************************************************

		fprintf(OutFile, "Xformed: <%ld,%ld> -> <%ld,%ld> \n", ColStart,RowStart,ColEnd,RowEnd);
		// **************************************************************************************

#endif


		// More trivial rejection (Clipping polygons that can not be seen).

		if ((ColStart == ColEnd) || (( ColStart < 0) && (ColEnd < 0)) || ((ColStart > 640) && (ColEnd > 640)))
			continue;

		// Now, insert the edge into the edge list.

		if (RowStart != RowEnd )
		{
			if (RowStart > RowEnd )
				newEdge = new Edge( RowEnd, ColEnd, RowStart, ColStart );
			else
				newEdge = new Edge( RowStart, ColStart, RowEnd, ColEnd );

			if (RowEnd > EndRow)
				EndRow = RowEnd;
			if (RowStart > EndRow)
				EndRow = RowStart;
			

			// Now insert the edge into the list.

			if( tmpEdgeList == NULL)
			{
				newEdge->nextEdge = NULL;
				tmpEdgeList = newEdge;
			}
			else
			{
				Edge* srch = tmpEdgeList;
				Edge* prev = NULL;
				while(srch != NULL) 
				{
					if( srch->StartR < newEdge->StartR )
					{ 
						// insert Node Here.

						if (NULL == prev)
						{
							newEdge->nextEdge = tmpEdgeList;
							tmpEdgeList = newEdge;
						}
						else
						{
							newEdge->nextEdge = srch;
							prev->nextEdge = newEdge;
						}
						break;
					}
					prev = srch;
					srch = srch->nextEdge;
				}
				if( srch == NULL)
				{
					newEdge->nextEdge = srch;
					prev->nextEdge = newEdge;
				}
			}
		}

	}

#ifdef DEBUG

	// DEBUG: ************************************************************************************************

	for (Edge* node = tmpEdgeList; node != NULL; node = node->nextEdge)
		fprintf(OutFile, " Edge: <%ld,%ld> -> <%ld,%ld> \n",node->StartC, node->StartR, node->EndC, node->EndR);
	fclose(OutFile);
	// *******************************************************************************************************

#endif


	// Step 2. Determine if the SSector is partially visible. If not, then return.

	//		   ( If the list contains any edges )


	if (( tmpEdgeList != NULL ) && ( tmpEdgeList->nextEdge != NULL ))
	{
		Edge *left, *right, *nextEdge;

		// Step 3. Start with the 1st 2 edges. Calculate the deltas.

		if( tmpEdgeList->StartC < (tmpEdgeList->nextEdge)->StartC )
		{
			left = tmpEdgeList;
			right = tmpEdgeList->nextEdge;
			nextEdge = right->nextEdge;
		}
		else
		{
			right = tmpEdgeList;
			left = tmpEdgeList->nextEdge;
			nextEdge = left->nextEdge;
		}		
		StartCol = left->StartC * 65536l;
		EndCol = right->StartC * 65536l;
		DeltaL = (65536l * (left->EndC - left->StartC)) / (left->EndR - left->StartR);
		DeltaR = (65536l * (right->EndC - right->StartC)) / (right->EndR - right->StartR);

		// Step 4. Start rendering the polygon...

		EndRow = (EndRow > 400)?400: EndRow;
		short StartRow = ((left->StartR) > 0)? (short)(left->StartR):0;
		for(short row = StartRow; row < EndRow; ++row)
		{
			// Step 4a. If we go past an edge, get the next one in the sorted list, and 

			//          recompute the deltas.

			if (( row > left->EndR ) && (row <= right->EndR ))
			{
				if ( nextEdge == NULL ) return 1;

				left = nextEdge;
				nextEdge = left->nextEdge;
			
				StartCol = left->StartC  * 65536l;
				DeltaL = (65536l * (left->EndC - left->StartC)) / (left->EndR - left->StartR);
			}
			else if ((row > right->EndR) && (row <= left->EndR))
			{
				if ( nextEdge == NULL ) return 1;

				right = nextEdge;
				nextEdge = right->nextEdge;

				EndCol = right->StartC * 65536l;
				DeltaR = (65536l * (right->EndC - right->StartC)) / (right->EndR - right->StartR);
			}
			else if ((row > left->EndR) && (row > right->EndR))
			{
				if ( nextEdge == NULL || nextEdge->nextEdge == NULL) return 1;

				if( nextEdge->StartC < (nextEdge->nextEdge)->StartC )
				{
					left = nextEdge;
					right = left->nextEdge;
					nextEdge = right->nextEdge;
				}
				else
				{
					right = nextEdge;
					left = right->nextEdge;
					nextEdge = left->nextEdge;
				}		
				StartCol = left->StartC * 65536l;
				EndCol = right->StartC * 65536l;
				DeltaL = (65536l * (left->EndC - left->StartC)) / (left->EndR - left->StartR);
				DeltaR = (65536l * (right->EndC - right->StartC)) / (right->EndR - right->StartR);
			}

			// Step 4b. Render the row sliver.

			for (short col = (short)(StartCol>>16); col <= (short)(EndCol>>16); ++col)
			{
				if(( col > 0 ) && ( col < 640))
				{
					*(DoubleBuffer + col + row * 640l) = 30;
				}
			}

			StartCol += DeltaL;
			EndCol += DeltaR;
		}
	}

	return 1;
}

short Renderer::drawWallTopPegged(short StartX, short StartY, short EndX, short EndY, short top, short bottom, short Angle, char* TxtName, short XOffset, short YOffset, long Pdist, long BOffset )
{
	//

	// This will actually draw the textured wall polygon on the screen buffer. (320x200 buffer size).

	//

	long  ColStart;       // Starting column for the polygon.

	long  ColEnd;         // Ending column for the polygon.

	long  StartTop;       // Starting row for the 1st column.

	long  StartBtm;		  // Ending row for the 1st column.

	long  EndTop;		  // Starting row for the last column.

	long  EndBtm;         // Ending row for the last column.

	long  DeltaTop;       // Amount to change Ytop for each column.

	long  DeltaBtm;       // Amount to change the Ysize for each column.

	long  CurrentRow;
	long  CurrentCol;
	long  CurrentTop;
	long  CurrentBtm;
	short TxtCol = 0;
	short txtwidth = 0;
	short txtheight = 0;
	char* txtPtr = NULL;
	char* txtrowPtr = NULL;
	long  DeltaTxt = 0;
	long  currTxtl = 0;
	unsigned char * ScrnPtr;
	long tmptop;
	long tmpbottom;
	long CurrentScale;
	long EndScale;
	long DeltaScale;
	long TempTop;

	// Try to do trivial rejection (If this is a clear polygon, or it is completely behind the view window).

	if ((TxtName[0] == '-') || ( StartX < 1 && EndX < 1 ))
		return 0;
	
	// We need to clip the incoming segment at the X=1 plane...

	if( StartX < 1 && EndX >= 1 )
	{
		if( StartY != EndY )
			StartY = StartY + ((1-StartX) * ( EndY - StartY )) / (EndX - StartX);	
		StartX = 1;
	}
	if( StartX >= 1 && EndX < 1)
	{
		if( StartY != EndY )
			EndY = StartY + ((1-StartX) * ( EndY - StartY )) / (EndX - StartX);
		EndX = 1;
	}		

	// Transform the starting / ending points to screen coordinates. Looking down +X axis.

	//

	tmptop = ((long)top - ViewerHeight) * 554l;
	tmpbottom =((long)bottom - ViewerHeight) * 554l;
	
	ColStart    =  320l - ((long)StartY * 554l) / (long)StartX;
	ColEnd  =  320l - ((long)EndY * 554l) / (long)EndX;

	EndTop  =  240l - (tmptop / (long)EndX);
	StartTop    =  240l - (tmptop / (long)StartX);

	EndBtm  =  240l - (tmpbottom / (long)EndX);
	StartBtm    =  240l - (tmpbottom / (long)StartX);
	
	// More trivial rejection (Clipping polygons that can not be seen).

	if ((ColStart == ColEnd) || (( ColStart < 0) && (ColEnd < 0)) || ((ColStart > 640) && (ColEnd > 640)))
		return 0;
	
	// Calculate the deltas to interpolate the Ystart and Yend for each screen column.

	//

	//  Future change: Use a stepping table (1<<14)/DeltaWidth, where DeltaWidth is in [1,1024].

	//

	txtPtr = WadFile->getTexturePtr(TxtName, txtwidth, txtheight);

	CurrentTop = StartTop <<14;
	CurrentBtm = StartBtm <<14;
	DeltaTop = ((EndTop - StartTop) * 1l<<14) / (ColEnd - ColStart);
	DeltaBtm = ((EndBtm - StartBtm) * 1l<<14) / (ColEnd - ColStart);
	CurrentScale = (1161822208l / (long)StartX); // ( 128 * 554 * 1<<14 ) / Distance

	EndScale = (1161822208l / (long)EndX );
	DeltaScale = ( EndScale - CurrentScale ) / (ColEnd - ColStart);

	
	// For each column of the polygon,

	for ( CurrentCol = ColStart; CurrentCol <= ColEnd; ++CurrentCol )
	{
		// Scale the texture column to size (Yend-Ystart), starting at row Ystart.

		if ((CurrentCol > 0) && (CurrentCol < 640))
		{
			//	The texture column is given by TxtCol = (Pdist * Tan(Angle of column ray to Pdist vector)) - Bo

			TxtCol = (short)(((Pdist * tan((colangle[CurrentCol] + (Angle - ViewerAngle + ANGLE_90)))) - BOffset)>> 16) - XOffset;					
			TxtCol = (unsigned)TxtCol % txtwidth;
			txtrowPtr = txtPtr + (long)TxtCol * txtheight;
			if (( (CurrentBtm - CurrentTop) >> 14) > 0)
			{
				//

				// This could be in a table of (1<<14)/dHeight, dHeight goes from 1 to ...

				// Then DeltaTxt = txtheight * ScaleTable[( CurrentBtm - CurrentTop) >> 14].

				//

				DeltaTxt = 2097152l /(CurrentScale >> 14);
				currTxtl = ((long)-YOffset) << 14;

				if(( currTxtl >> 14 ) < 0 )
					currTxtl += (txtheight - YOffset) << 14;	

				if ( ( currTxtl >> 14 ) > txtheight )
					currTxtl -= ((long)txtheight) << 14;

				
						TempTop = CurrentTop >> 14;

				if ( TempTop < 0l )
				{
					currTxtl += DeltaTxt * (0 - TempTop);

					if(( currTxtl >> 14 ) < 0 )
					currTxtl += (txtheight - YOffset) << 14;	
					if ( ( currTxtl >> 14 ) > txtheight )
					currTxtl -= ((long)txtheight) << 14;
					if ( ( currTxtl >> 14 ) > txtheight )
					currTxtl -= ((long)txtheight) << 14;
					if ( ( currTxtl >> 14 ) > txtheight )
					currTxtl -= ((long)txtheight) << 14;

					TempTop = 0l;
				}

				ScrnPtr = (DoubleBuffer + CurrentCol + TempTop * 640l);

				for ( CurrentRow = TempTop; CurrentRow < (CurrentBtm >> 14) && CurrentRow < 400; ++CurrentRow )
				{
					*(ScrnPtr) = *(txtrowPtr + (currTxtl >> 14)  );
					ScrnPtr += 640l;					
					currTxtl += DeltaTxt;
					if(( currTxtl >> 14 ) < 0 )
						currTxtl += (txtheight - YOffset) << 14;	
					if ( ( currTxtl >> 14 ) > txtheight )
						currTxtl -= ((long)txtheight) << 14;
				}
			}
		}
		
		// Increment the deltas for the top and bottom edges.

		CurrentTop += DeltaTop;
		CurrentBtm += DeltaBtm;		
		CurrentScale += DeltaScale;

	}
	return 1;
}

short Renderer::drawWallBtmPegged(short StartX, short StartY, short EndX, short EndY, short top, short bottom, short Angle, char* TxtName, short XOffset, short YOffset, long Pdist, long BOffset )
{
	//

	// This will actually draw the textured wall polygon on the screen buffer. (320x200 buffer size).

	//

	long  ColStart;       // Starting column for the polygon.

	long  ColEnd;         // Ending column for the polygon.

	long  StartTop;       // Starting row for the 1st column.

	long  StartBtm;		  // Ending row for the 1st column.

	long  EndTop;		  // Starting row for the last column.

	long  EndBtm;         // Ending row for the last column.

	long  DeltaTop;       // Amount to change Ytop for each column.

	long  DeltaBtm;       // Amount to change the Ysize for each column.

	long  CurrentRow;
	long  CurrentCol;
	long  CurrentTop;
	long  CurrentBtm;
	short TxtCol = 0;
	short txtwidth = 0;
	short txtheight = 0;
	char* txtPtr = NULL;
	char* txtRowPtr = NULL;
	long  DeltaTxt = 0;
	long  currTxtl = 0;
	unsigned char * ScrnPtr;
	long tmptop;
	long tmpbottom;
	long CurrentScale;
	long EndScale;
	long DeltaScale;
	long TempBottom;
	
	// Try to do trivial rejection (If this is a clear polygon, or it is completely behind the view window).

	if ((TxtName[0] == '-') || ( StartX < 1 && EndX < 1 ))
		return 0;
	
	// We need to clip the incoming segment at the X=1 plane...

	if( StartX < 1 && EndX >= 1 )
	{
		if( StartY != EndY )
			StartY = StartY + ((1-StartX) * ( EndY - StartY )) / (EndX - StartX);	
		StartX = 1;
	}
	if( StartX >= 1 && EndX < 1)
	{
		if( StartY != EndY )
			EndY = StartY + ((1-StartX) * ( EndY - StartY )) / (EndX - StartX);
		EndX = 1;
	}		

	// Transform the starting / ending points to screen coordinates. Looking down +X axis.

	//

	// Optimization: Possibly precalculate 65536/StartX and 65536/EndX...

	//

	tmptop = ((long)top - ViewerHeight) * 554l;
	tmpbottom =((long)bottom - ViewerHeight) * 554l;
	
	ColStart    =  320l - ((long)StartY * 554l) / (long)StartX;
	ColEnd  =  320l - ((long)EndY * 554l) / (long)EndX;

	EndTop  =  240l - (tmptop / (long)EndX);
	StartTop    =  240l - (tmptop / (long)StartX);

	EndBtm  =  240l - (tmpbottom / (long)EndX);
	StartBtm    =  240l - (tmpbottom / (long)StartX);
	
	// More trivial rejection (Clipping polygons that can not be seen).

	if ((ColStart == ColEnd) || (( ColStart < 0) && (ColEnd < 0)) || ((ColStart > 640) && (ColEnd > 640)))
		return 0;
	
	// Calculate the deltas to interpolate the Ystart and Yend for each screen column.

	//

	//  Future change: Use a stepping table (1<<14)/DeltaWidth, where DeltaWidth is in [1,1024].

	//

	CurrentTop = StartTop <<14;
	CurrentBtm = StartBtm <<14;
	DeltaTop = ((EndTop - StartTop) * 1l<<14) / (ColEnd - ColStart);
	DeltaBtm = ((EndBtm - StartBtm) * 1l<<14) / (ColEnd - ColStart);

	// We can do this because 1/z is linear in screen space.

	CurrentScale = (1161822208l / (long)StartX); // 128 (texture height) * 554 (scaling factor) * (1 << 14) / Distance.

	EndScale = (1161822208l / (long)EndX );
	DeltaScale = ( EndScale - CurrentScale ) / (ColEnd - ColStart);

	txtPtr = WadFile->getTexturePtr(TxtName, txtwidth, txtheight);

	// For each column of the polygon,

	for ( CurrentCol = ColStart; CurrentCol <= ColEnd; ++CurrentCol )
	{
		// Scale the texture column to size (Yend-Ystart), starting at row Ystart.

		if ((CurrentCol > 0) && (CurrentCol < 640))
		{
			//	The texture column is given by TxtCol = (Pdist * Tan(Angle of column ray to Pdist vector)) - Bo

			TxtCol = (unsigned short)((((Pdist * tan((colangle[CurrentCol] + (Angle - ViewerAngle + ANGLE_90)))) - BOffset)>> 16) - XOffset) % txtwidth;				
			txtRowPtr = txtPtr + (long)TxtCol * txtheight;

			if (( (CurrentBtm - CurrentTop) >> 14) > 0)
			{
				//

				// This could be in a table of (1<<14)/dHeight, dHeight goes from 1 to 1024...

				// Then DeltaTxt = txtheight * ScaleTable[( CurrentBtm - CurrentTop) >> 14].

				//

				// Currently DeltaTxt = ( 128 * 1<<14 ) / Height of the texture at the specific distance.

				// since 128 is guarenteed to be the texture height, and we're using 18:14 fixed point.

			
				DeltaTxt = 2097152l /(CurrentScale >> 14);
				currTxtl = (txtheight + YOffset) << 14;

				if(( currTxtl >> 14 ) < 0 )
					currTxtl += (txtheight - YOffset) << 14;	
				if ( ( currTxtl >> 14 ) > txtheight )
					currTxtl -= ((long)txtheight) << 14;


				TempBottom = CurrentBtm >> 14;
				if (  TempBottom > 399 )
				{
					currTxtl -= DeltaTxt * (TempBottom - 399l);

					if(( currTxtl >> 14 ) < 0 )
						currTxtl += (long)txtheight << 14;	
					if ( ( currTxtl >> 14 ) > txtheight )
						currTxtl -= (long)txtheight << 14;
					if ( ( currTxtl >> 14 ) > txtheight )
						currTxtl -= (long)txtheight << 14;
					if ( ( currTxtl >> 14 ) > txtheight )
						currTxtl -= (long)txtheight << 14;
					if ( ( currTxtl >> 14 ) > txtheight )
						currTxtl -= (long)txtheight << 14;

					TempBottom = 399l;
				}

				ScrnPtr = (DoubleBuffer + CurrentCol + TempBottom * 640l);
				for ( CurrentRow = TempBottom; CurrentRow >= (CurrentTop >> 14) && CurrentRow >= 0; --CurrentRow )
				{
					*(ScrnPtr) = *( txtRowPtr + (currTxtl >> 14) );
					ScrnPtr -= 640l;					
					currTxtl -= DeltaTxt;
					if(( currTxtl >> 14 ) < 0 )
						currTxtl += (long)txtheight << 14;	
					if ( ( currTxtl >> 14 ) > txtheight )
						currTxtl -= (long)txtheight << 14;
				}
			}
		}
		
		// Increment the deltas for the top and bottom edges.

		CurrentTop += DeltaTop;
		CurrentBtm += DeltaBtm;		
		CurrentScale += DeltaScale;

	}
	return 1;
}

short Renderer::changeViewPosn( short X, short Y, short Angle )
{

	// This routine has the effect of translating and rotating all of the vertices 

	// and placing them in a temporary holding area, which will be used in rendering the

	// final view.

	long tempX;
	long tempY;
	
	register long cosine = this->cos(-Angle);
	register long sine = this->sin(-Angle);

	//  Transform all vertices to view coordinates, and place new vertices in cache.

	for (int i=0; i < WadFile->NbrVertices; ++i)
	{
		// Copy the world vertex to the cache.

		VerticesCache[i].x = WadFile->Vertices[i].x - X;
		VerticesCache[i].y = WadFile->Vertices[i].y - Y;

		// Now rotate the vertex so that the player is looking down the positive X axis.

		tempX = (long)VerticesCache[i].x * cosine - (long)VerticesCache[i].y * sine;
		tempY = (long)VerticesCache[i].x * sine + (long)VerticesCache[i].y * cosine;

		VerticesCache[i].x = (short)(tempX >> 16);
		VerticesCache[i].y = (short)(tempY >> 16);
	}

	// We need to get the floor height at this place, and set it.

	ViewerX = X;
	ViewerY = Y;
	ViewerHeight = WadFile->Sectors[getSectorfromXY(X, Y)].FloorHeight + 56;
	ViewerAngle = Angle;

	return ViewerHeight;
}