#include <common/pathfnd.hpp>
#include <common/wingblt.hpp>
#include <common/except.hpp>

WINGBlt::WINGBlt(WORD width,WORD height,PurePalette &somePurePalette)
: mhWINGDC(0), mDisposition(InvalidDC), mhWINGBitmap(0), mIsOkay(FALSE), mhLibrary(0),
	mlpfnWINGCreateDC(0),	mlpfnWINGCreateBitmap(0), mlpWINGData(0), mlpfnWINGBitBlt(0),
	mlpfnWINGStretchBlt(0), mOrientation(BottomUp), mWINGBitmapExtent(0L), mhOldBitmap(0)
{
	if(!initWING())return;
  getDIBFormat();
	mWINGPalette=somePurePalette;
	mWINGPalette.identityPalette();
  mWINGBitmapInfo=mWINGPalette;
  createWINGBitmap(width,height);
}

WINGBlt::~WINGBlt()
{
	destroyBitmap();
	destroyDevice();
  freeLibrary();
}

WORD WINGBlt::size(WORD width,WORD height)
{
	if(!isOkay())return FALSE;
  return createWINGBitmap(width,height);
}

void WINGBlt::getDIBFormat(void)
{
	BitmapInfo bitmapInfo;

	(*mlpfnWINGRecommendDIBFormat)(bitmapInfo);
  mOrientation=(Orientation)bitmapInfo.height();
}

WORD WINGBlt::createWINGBitmap(WORD width,WORD height)
{
	destroyBitmap();
	createDevice();
	getRequiredWidth(width,height);
	mWINGBitmapExtent=(DWORD)width*(DWORD)height;
	mWINGBitmapInfo.width(width);
	mWINGBitmapInfo.height(height*(int)mOrientation);
	mhWINGBitmap=(*mlpfnWINGCreateBitmap)(mhWINGDC,mWINGBitmapInfo,(void**)&mlpWINGData);
  if(!mhWINGBitmap)return mIsOkay=FALSE;
	setBits(mlpWINGData,mWINGBitmapExtent,0);
	mhOldBitmap=(HBITMAP)::SelectObject(mhWINGDC,mhWINGBitmap);
	return mIsOkay=TRUE;
}

WINGBlt &WINGBlt::operator+=(BitmapOverlay &someBitmapOverlay)
{
	Point placementPoint;
	DWORD newSrcY;
	DWORD newDstY;
	DWORD srcIndex;
	DWORD dstIndex;
	UINT srcRow;
  UINT srcCol;

	if(!isOkay())return *this;
  placementPoint=someBitmapOverlay.placementPoint();
	switch(mOrientation)
	{
		case TopDown :
			for(srcRow=0;srcRow<someBitmapOverlay.bitmap().height();srcRow++)
			{
				for(srcCol=0;srcCol<someBitmapOverlay.bitmap().width();srcCol++)
				{
					newSrcY=someBitmapOverlay.bitmap().height();
					srcIndex=newSrcY*someBitmapOverlay.bitmap().width()+srcCol;
					newDstY=srcRow+placementPoint.y();
					dstIndex=newDstY*mWINGBitmapInfo.width()+srcCol+placementPoint.x();
          *(mlpWINGData+dstIndex)=*(someBitmapOverlay.bitmap().getDataPtr()+srcIndex);
				}
			}
			break;
		case BottomUp :
			for(srcRow=0;srcRow<someBitmapOverlay.bitmap().height();srcRow++)
			{
				for(srcCol=0;srcCol<someBitmapOverlay.bitmap().width();srcCol++)
				{
					newSrcY=srcRow;
					srcIndex=newSrcY*someBitmapOverlay.bitmap().width()+srcCol;
					newDstY=mWINGBitmapInfo.height()-someBitmapOverlay.bitmap().height()-placementPoint.y()+srcRow;
					dstIndex=newDstY*mWINGBitmapInfo.width()+srcCol+placementPoint.x();
          *(mlpWINGData+dstIndex)=*(someBitmapOverlay.bitmap().getDataPtr()+srcIndex);
				}
			}
      break;
	}
	return *this;
}

WINGBlt &WINGBlt::operator=(Bitmap &someBitmap)
{
	if(!isOkay())return *this;
	if(!createWINGBitmap(someBitmap.width(),someBitmap.height()))return *this;
	copyBits(mlpWINGData,someBitmap.getDataPtr(),mWINGBitmapExtent);
	return *this;
}

WINGBlt &WINGBlt::operator=(PurePalette &somePurePalette)
{
	mWINGPalette=somePurePalette;
	mWINGPalette.identityPalette();
  return *this;
}

void WINGBlt::createDevice(void)
{
	destroyDevice();
	mhWINGDC=(*mlpfnWINGCreateDC)();
  mDisposition=DeleteDC;
}

void WINGBlt::copyBits(UHUGE *destination,UHUGE *source,DWORD length)const
{
	for(DWORD count=0;count<length;count++)*destination++=*source++;
}

void WINGBlt::setBits(UHUGE *lpSrcPtr,DWORD srcExtent,BYTE charByte)
{
  for(DWORD count=0;count<srcExtent;count++)*lpSrcPtr++=charByte;
}

void WINGBlt::setByte(WORD row,WORD col,BYTE byteValue)
{
	if(!isOkay())return;
	if(row>=mWINGBitmapInfo.height()||col>=mWINGBitmapInfo.width())return;
	if(TopDown==mOrientation)setByteTopDown(row,col,byteValue);
	else setByteBottomUp(row,col,byteValue);
}

BYTE WINGBlt::getByte(WORD row,WORD col)const
{
	if(!isOkay())return (BYTE)FALSE;
	if(row>=mWINGBitmapInfo.height()||col>=mWINGBitmapInfo.width())return (BYTE)FALSE;
	if(TopDown==mOrientation)return getByteTopDown(row,col);
	else return getByteBottomUp(row,col);
}

void WINGBlt::line(const Point &firstPoint,const Point &secondPoint,BYTE byteValue)
{
	LONG xRunning((LONG)firstPoint.x()<<0x10);
	LONG yRunning((LONG)firstPoint.y()<<0x10);
	LONG xDelta;
	LONG yDelta;
	short xDir(1);
	short yDir(1);
	short steps;

	if(secondPoint.x()<firstPoint.x())xDir=-1;
	if(secondPoint.y()<firstPoint.y())yDir=-1;
	xDelta=secondPoint.x()-firstPoint.x();
	yDelta=secondPoint.y()-firstPoint.y();
	if(xDelta<0)xDelta=-xDelta;
  if(yDelta<0)yDelta=-yDelta;
	if(xDelta<yDelta)
	{
		xDelta<<=0x10;
		if(yDelta)xDelta/=yDelta;
    else xDelta=1L;
		steps=yDelta;
    yDelta=0x10000;
	}
	else
	{
		yDelta<<=0x10;
		if(xDelta)yDelta/=xDelta;
    else yDelta=1L;
		steps=xDelta;
    xDelta=0x10000;
	}
	if(-1==xDir&&-1==yDir)
	{
		for(short stepIndex=0;stepIndex<steps;stepIndex++)
		{
      setByte(yRunning>>0x10,xRunning>>0x10,byteValue);
      xRunning-=xDelta;
			yRunning-=yDelta;
		}
	}
	else if(-1==xDir&&1==yDir)
	{
		for(short stepIndex=0;stepIndex<steps;stepIndex++)
		{
      setByte(yRunning>>0x10,xRunning>>0x10,byteValue);
			xRunning-=xDelta;
      yRunning+=yDelta;
    }
	}
	else if(1==xDir&&-1==yDir)
	{
		for(short itemIndex=0;itemIndex<steps;itemIndex++)
		{
      setByte(yRunning>>0x10,xRunning>>0x10,byteValue);
			xRunning+=xDelta;
			yRunning-=yDelta;
		}
	}
	else if(1==xDir&&1==yDir)
	{
		for(short itemIndex=0;itemIndex<steps;itemIndex++)
		{
      setByte(yRunning>>0x10,xRunning>>0x10,byteValue);
			xRunning+=xDelta;
      yRunning+=yDelta;
    }
  }
}

void WINGBlt::setColTopDown(WORD col,WORD row,char FAR *lpColData,WORD wDataLength)
{
	UHUGE *lpImage=mlpWINGData+(LONG)col;
	lpImage+=((LONG)row*mWINGBitmapInfo.width());
	for(;(short)row>=0&&wDataLength;row--)
	{
		*lpImage=*lpColData;
		lpImage-=mWINGBitmapInfo.width();
		lpColData--;
    wDataLength--;
	}
	return;
}

void WINGBlt::setColBottomUp(WORD col,WORD row,char FAR *lpColData,WORD wDataLength)
{
	UHUGE *lpImage=mlpWINGData+(LONG)col;
	lpImage+=(mWINGBitmapInfo.width()*((mWINGBitmapInfo.height()-1L)-(LONG)row));
	for(;(short)row>=0&&wDataLength;row--)
	{
		*lpImage=*lpColData;
		lpImage+=mWINGBitmapInfo.width();
		lpColData--;
    wDataLength--;
	}
}

void WINGBlt::setColBottomUp(WORD col,char FAR *lpColData)
{
	UHUGE *lpImage=mlpWINGData+mWINGBitmapInfo.width()*mWINGBitmapInfo.height();
	lpImage=(lpImage-mWINGBitmapInfo.width())+(LONG)col;
	for(short rowIndex=0;rowIndex<mWINGBitmapInfo.height();rowIndex++)
	{
		*lpImage=*lpColData;
		lpImage-=mWINGBitmapInfo.width();
    lpColData++;
	}
}

void WINGBlt::getColBottomUp(WORD col,char FAR *lpColData)const
{
	UHUGE *lpImage=mlpWINGData+mWINGBitmapInfo.width()*mWINGBitmapInfo.height();
	lpImage=(lpImage-mWINGBitmapInfo.width())+(LONG)col;
	for(short rowIndex=0;rowIndex<mWINGBitmapInfo.height();rowIndex++)
	{
		*lpColData=*lpImage;
		lpImage-=mWINGBitmapInfo.width();
    lpColData++;
	}
}

void WINGBlt::setRowBottomUp(WORD row,char FAR *lpRowData)
{
	UHUGE *lpImage=mlpWINGData+mWINGBitmapInfo.width()*mWINGBitmapInfo.height();
	copyBits((lpImage-(((LONG)row*mWINGBitmapInfo.width())+mWINGBitmapInfo.width())),(UHUGE*)lpRowData,mWINGBitmapInfo.width());
}

void WINGBlt::getRowBottomUp(WORD row,char FAR *lpRowData)const
{
	UHUGE *lpImage=mlpWINGData+mWINGBitmapInfo.width()*mWINGBitmapInfo.height();
	copyBits((UHUGE*)lpRowData,(lpImage-(((LONG)row*mWINGBitmapInfo.width())+mWINGBitmapInfo.width())),mWINGBitmapInfo.width());
}

void WINGBlt::setColTopDown(WORD col,char FAR *lpColData)
{
	UHUGE *lpImage=mlpWINGData+(LONG)col;
	for(short rowIndex=0;rowIndex<mWINGBitmapInfo.height();rowIndex++)
	{
		*lpImage=*lpColData;
		lpImage+=mWINGBitmapInfo.width();
    lpColData++;
	}
}

void WINGBlt::getColTopDown(WORD col,char FAR *lpColData)const
{
	UHUGE *lpImage=mlpWINGData+(LONG)col;
	for(short rowIndex=0;rowIndex<mWINGBitmapInfo.height();rowIndex++)
	{
		*lpColData=*lpImage;
		lpImage+=mWINGBitmapInfo.width();
		lpColData++;
	}
}

void WINGBlt::setRowTopDown(WORD row,char FAR *lpRowData)
{
	UHUGE *lpImage=mlpWINGData+((LONG)row*mWINGBitmapInfo.width());
	copyBits(lpImage,(UHUGE*)lpRowData,mWINGBitmapInfo.width());
}

void WINGBlt::getRowTopDown(WORD row,char FAR *lpRowData)const
{
	UHUGE *lpImage=mlpWINGData+((LONG)row*mWINGBitmapInfo.width());
	copyBits((UHUGE*)lpRowData,lpImage,mWINGBitmapInfo.width());
}

void WINGBlt::getRequiredWidth(WORD &desiredWidth,WORD desiredHeight)
{
	DWORD imageExtent;

	imageExtent=(((((LONG)desiredWidth*8)+31)&~31)>>3)*(LONG)desiredHeight;
	if(imageExtent==(LONG)desiredWidth*(LONG)desiredHeight)return;
	desiredWidth=(WORD)(imageExtent/(LONG)desiredHeight);
}

#if defined(__FLAT__)
WORD WINGBlt::initWING(void)
{
	String wingDLLFileName("WING32.DLL");
	String wingDLLPathFileName;
	DWORD wingBitBltOrdinal(1L);
	DWORD wingStretchBltOrdinal(10L);
	DWORD wingCreateBitmapOrdinal(2L);
	DWORD wingCreateDCOrdinal(3L);
  DWORD wingRecommendDIBFormatOrdinal(8);
	PathFind pathFind;

	if(!pathFind.findFile(wingDLLFileName,wingDLLPathFileName))return FALSE;
	if((mhLibrary=::LoadLibrary(wingDLLPathFileName))<(HINSTANCE)HINSTANCE_ERROR)return FALSE;
	mlpfnWINGCreateDC=(LPFNWINGCREATEDC)::GetProcAddress(mhLibrary,(LPCSTR)wingCreateDCOrdinal);
	mlpfnWINGCreateBitmap=(LPFNWINGCREATEBITMAP)::GetProcAddress(mhLibrary,(LPCSTR)wingCreateBitmapOrdinal);
	mlpfnWINGBitBlt=(LPFNWINGBITBLT)::GetProcAddress(mhLibrary,(LPCSTR)wingBitBltOrdinal);
	mlpfnWINGStretchBlt=(LPFNWINGSTRETCHBLT)::GetProcAddress(mhLibrary,(LPCSTR)wingStretchBltOrdinal);
  mlpfnWINGRecommendDIBFormat=(LPFNWINGRECOMMENDDIBFORMAT)::GetProcAddress(mhLibrary,(LPCSTR)wingRecommendDIBFormatOrdinal);
	if(!mlpfnWINGCreateDC)throw(NullError());
	if(!mlpfnWINGCreateBitmap)throw(NullError());
	if(!mlpfnWINGBitBlt)throw(NullError());
	if(!mlpfnWINGStretchBlt)throw(NullError());
  if(!mlpfnWINGRecommendDIBFormat)throw(NullError());
	return TRUE;
}
#else
WORD WINGBlt::initWING(void)
{
  String wingDLLFileName("WING.DLL");
  String wingDLLPathFileName;
  DWORD wingBitBltOrdinal(1010L);
  DWORD wingStretchBltOrdinal(1009L);
  DWORD wingCreateBitmapOrdinal(1003L);
  DWORD wingCreateDCOrdinal(1001L);
  DWORD wingRecommendDIBFormatOrdinal(1002);
  PathFind pathFind;

  if(!pathFind.findFile(wingDLLFileName,wingDLLPathFileName))return FALSE;
	if((mhLibrary=::LoadLibrary(wingDLLPathFileName))<HINSTANCE_ERROR)return FALSE;
	mlpfnWINGCreateDC=(LPFNWINGCREATEDC)::GetProcAddress(mhLibrary,(LPCSTR)wingCreateDCOrdinal);
	mlpfnWINGCreateBitmap=(LPFNWINGCREATEBITMAP)::GetProcAddress(mhLibrary,(LPCSTR)wingCreateBitmapOrdinal);
	mlpfnWINGBitBlt=(LPFNWINGBITBLT)::GetProcAddress(mhLibrary,(LPCSTR)wingBitBltOrdinal);
	mlpfnWINGStretchBlt=(LPFNWINGSTRETCHBLT)::GetProcAddress(mhLibrary,(LPCSTR)wingStretchBltOrdinal);
  mlpfnWINGRecommendDIBFormat=(LPFNWINGRECOMMENDDIBFORMAT)::GetProcAddress(mhLibrary,(LPCSTR)wingRecommendDIBFormatOrdinal);
	if(!mlpfnWINGCreateDC)throw(NullError());
	if(!mlpfnWINGCreateBitmap)throw(NullError());
	if(!mlpfnWINGBitBlt)throw(NullError());
	if(!mlpfnWINGStretchBlt)throw(NullError());
  return TRUE;
}
#endif
