#ifndef _COMMON_WINGBLT_HPP_
#define _COMMON_WINGBLT_HPP_
#ifndef _WING_WING_HPP_
#include <wing/wing.hpp>
#endif
#ifndef _COMMON_WINDOWS_HPP_
#include <common/windows.hpp>
#endif
#ifndef _COMMON_PUREDEVICE_HPP_
#include <common/purehdc.hpp>
#endif
#ifndef _COMMON_BITMAP_HPP_
#include <common/bitmap.hpp>
#endif
#ifndef _COMMON_BITMAPINFO_HPP_
#include <common/bminfo.hpp>
#endif

class WINGBlt
{
public:
	typedef enum Orientation{TopDown=-1,BottomUp=1};
	WINGBlt(WORD width,WORD height,PurePalette &somePurePalette);
	~WINGBlt();
	WORD size(WORD width,WORD height);
	operator PurePalette &(void);
  WINGBlt &operator=(const WINGBlt &someWINGBlt);
	WINGBlt &operator=(Bitmap &someBitmap);
	WINGBlt &operator=(PurePalette &somePurePalette);
	WINGBlt &operator+=(BitmapOverlay &someBitmapOverlay);
	Orientation orientation(void)const;
	WORD width(void)const;
	WORD height(void)const;
	DWORD imageExtent(void)const;
	WORD bitBlt(PureDevice &somePureDevice);
  void line(const Point &firstPoint,const Point &secondPoint,BYTE byteValue);
  void setBitmapBits(BYTE byteValue);
	void setByte(WORD row,WORD col,BYTE byteValue);
	BYTE getByte(WORD row,WORD col)const;
	WORD getRow(WORD row,char FAR *lpRowData)const;
	WORD setRow(WORD row,char FAR *lpRowData);
	WORD getCol(WORD col,char FAR *lpColData)const;
	WORD setCol(WORD col,char FAR *lpColData);
  WORD setCol(WORD col,WORD row,char FAR *lpColData,WORD wDataLength);
	WORD isOkay(void)const;
	UHUGE *getDataPtr(void)const;
private:
	typedef enum Disposition{KeepDC,DeleteDC,InvalidDC};
	typedef HDC (WINGAPI *LPFNWINGCREATEDC)(void);
	typedef HBITMAP (WINGAPI *LPFNWINGCREATEBITMAP)(HDC WinGDC,BITMAPINFO const FAR *lpBitmapInfo,void FAR FAR *lppBytes);
	typedef BOOL (WINGAPI *LPFNWINGBITBLT)(HDC hdcDest,int nXOriginDest,int nYOriginDest,int nWidthDest,int nHeightDest,HDC hdcSrc,int nXOriginSrc,int nYOriginSrc);
	typedef BOOL (WINGAPI *LPFNWINGSTRETCHBLT)(HDC hdcDest,int nXOriginDest,int nYOriginDest,int nWidthDest,int nHeightDest,HDC hdcSrc,int nXOriginSrc,int nYOriginSrc,int nWidthSrc,int nHeightSrc);
  typedef BOOL (WINGAPI *LPFNWINGRECOMMENDDIBFORMAT)(BITMAPINFO FAR *lpBitmapInfo);
	void copyBits(UHUGE *destination,UHUGE *source,DWORD length)const;
  void setBits(UHUGE *lpSrcPtr,DWORD srcExtent,BYTE charByte);
	void getRequiredWidth(WORD &desiredWidth,WORD desiredHeight);
	void setByteBottomUp(WORD row,WORD col,BYTE byteValue);
	BYTE getByteBottomUp(WORD row,WORD col)const;
	void setByteTopDown(WORD row,WORD col,BYTE byteValue);
	BYTE getByteTopDown(WORD row,WORD col)const;
	void setColTopDown(WORD col,WORD row,char FAR *lpColData,WORD wDataLength);
	void setColBottomUp(WORD col,WORD row,char FAR *lpColData,WORD wDataLength);
	void setColBottomUp(WORD col,char FAR *lpColData);
	void getColBottomUp(WORD col,char FAR *lpColData)const;
	void setRowBottomUp(WORD row,char FAR *lpRowData);
	void getRowBottomUp(WORD row,char FAR *lpRowData)const;
	void setColTopDown(WORD col,char FAR *lpColData);
	void getColTopDown(WORD col,char FAR *lpColData)const;
	void setRowTopDown(WORD row,char FAR *lpColData);
	void getRowTopDown(WORD row,char FAR *lpColData)const;
  void getDIBFormat(void);
	void destroyBitmap(void);
	void destroyDevice(void);
	void freeLibrary(void);
	void createDevice(void);
	WORD initWING(void);
  WORD createWINGBitmap(WORD width,WORD height);
	LPFNWINGCREATEDC mlpfnWINGCreateDC;
	LPFNWINGCREATEBITMAP mlpfnWINGCreateBitmap;
  LPFNWINGSTRETCHBLT mlpfnWINGStretchBlt;
	LPFNWINGBITBLT mlpfnWINGBitBlt;
  LPFNWINGRECOMMENDDIBFORMAT mlpfnWINGRecommendDIBFormat;
  HINSTANCE mhLibrary;
	Disposition mDisposition;
	PurePalette mWINGPalette;
	BitmapInfo mWINGBitmapInfo;
  DWORD mWINGBitmapExtent;
  Orientation mOrientation;
  UHUGE *mlpWINGData;
	WORD mIsOkay;
	HDC mhWINGDC;
	HBITMAP mhWINGBitmap;
  HBITMAP mhOldBitmap;
};

inline
void WINGBlt::destroyBitmap(void)
{
  if(mhOldBitmap&&mhWINGDC){::SelectObject(mhWINGDC,mhOldBitmap);mhOldBitmap=0;}
	if(mhWINGBitmap){::DeleteObject(mhWINGBitmap);mhWINGBitmap=0;mlpWINGData=0;mIsOkay=FALSE;}
}

inline
void WINGBlt::destroyDevice(void)
{
  if(mhWINGDC&&DeleteDC==mDisposition){::DeleteDC(mhWINGDC);mhWINGDC=0;mDisposition=InvalidDC;}
}

inline
void WINGBlt::freeLibrary(void)
{
	if(mhLibrary){::FreeLibrary(mhLibrary);mhLibrary=0;}
}

inline
WORD WINGBlt::isOkay(void)const
{
  return mIsOkay;
}

inline
WORD WINGBlt::width(void)const
{
	return mWINGBitmapInfo.width();
}

inline
WORD WINGBlt::height(void)const
{
	return mWINGBitmapInfo.height();
}

inline
DWORD WINGBlt::imageExtent(void)const
{
  return mWINGBitmapExtent;
}

inline
UHUGE *WINGBlt::getDataPtr(void)const
{
  return mlpWINGData;
}

inline
WINGBlt::operator PurePalette &(void)
{
  return mWINGPalette;
}

inline
WINGBlt::Orientation WINGBlt::orientation(void)const
{
  return mOrientation;
}

inline
WINGBlt &WINGBlt::operator=(const WINGBlt &/*someWINGBlt*/)
{
	return *this;
}

inline
void WINGBlt::setBitmapBits(BYTE byteValue)
{
	if(!isOkay())return;
	setBits(mlpWINGData,mWINGBitmapExtent,byteValue);
}

inline
void WINGBlt::setByteBottomUp(WORD row,WORD col,BYTE byteValue)
{
	UHUGE *lpImage=mlpWINGData+mWINGBitmapInfo.width()*mWINGBitmapInfo.height();
	*((lpImage-((LONG)row*mWINGBitmapInfo.width()+mWINGBitmapInfo.width()))+(LONG)col)=byteValue;
}

inline
BYTE WINGBlt::getByteBottomUp(WORD row,WORD col)const
{
	UHUGE *lpImage=mlpWINGData+mWINGBitmapInfo.width()*mWINGBitmapInfo.height();
	return *((lpImage-((LONG)row*mWINGBitmapInfo.width()+mWINGBitmapInfo.width()))+(LONG)col);
}

inline
void WINGBlt::setByteTopDown(WORD row,WORD col,BYTE byteValue)
{
	*(mlpWINGData+(((LONG)row*mWINGBitmapInfo.width())+(LONG)col))=byteValue;
}

inline
BYTE WINGBlt::getByteTopDown(WORD row,WORD col)const
{
	return *(mlpWINGData+(((LONG)row*mWINGBitmapInfo.width())+(LONG)col));
}

inline
WORD WINGBlt::getRow(WORD row,char FAR *lpRowData)const
{
	if(!isOkay()||row>=mWINGBitmapInfo.height())return FALSE;
	if(TopDown==mOrientation)getRowTopDown(row,lpRowData);
  else getRowBottomUp(row,lpRowData);
  return TRUE;
}

inline
WORD WINGBlt::setRow(WORD row,char FAR *lpRowData)
{
	if(!isOkay()||row>=mWINGBitmapInfo.height())return FALSE;
	if(TopDown==mOrientation)setRowTopDown(row,lpRowData);
	else setRowBottomUp(row,lpRowData);
	return TRUE;
}

inline
WORD WINGBlt::getCol(WORD col,char FAR *lpColData)const
{
	if(!isOkay()||col>=mWINGBitmapInfo.width())return FALSE;
	if(TopDown==mOrientation)getColTopDown(col,lpColData);
  else getColBottomUp(col,lpColData);
  return TRUE;
}

inline
WORD WINGBlt::setCol(WORD col,char FAR *lpColData)
{
	if(!isOkay()||col>=mWINGBitmapInfo.width())return FALSE;
	if(TopDown==mOrientation)setColTopDown(col,lpColData);
	else setColBottomUp(col,lpColData);
	return TRUE;
}

inline
WORD WINGBlt::setCol(WORD col,WORD row,char FAR *lpColData,WORD wDataLength)
{
	if(!isOkay()||col>=mWINGBitmapInfo.width()||row>=mWINGBitmapInfo.height())return FALSE;
	if(TopDown==mOrientation)setColTopDown(col,row,lpColData,wDataLength);
	else setColBottomUp(col,row,lpColData,wDataLength);
	return TRUE;
}

inline
WORD WINGBlt::bitBlt(PureDevice &somePureDevice)
{
  if(!isOkay())return FALSE;
  (*mlpfnWINGBitBlt)(somePureDevice,0,0,mWINGBitmapInfo.width(),mWINGBitmapInfo.height(),mhWINGDC,0,0);
  return TRUE;
}
#endif
