// Filename:-	clipboard.cpp
//
#include "stdafx.h"
#include "includes.h"
//#include <windows.h>
//#include <stdio.h>
//#include <assert.h>
//#include "oddbits.h"
#include "text.h"
//
#include "clipboard.h"



#define APP_HWND AppVars.hWnd



BOOL ClipBoard_SendDIB(LPVOID pvData, int iBytes)
{
	HGLOBAL hXferBuffer = GlobalAlloc((UINT)GMEM_MOVEABLE|GMEM_DDESHARE,(DWORD)iBytes);	
	if (hXferBuffer)
	{
   		char *psLockedDest = (char*) GlobalLock(hXferBuffer);	
		memcpy(psLockedDest,pvData,iBytes);
		GlobalUnlock(psLockedDest);	
	
		if (OpenClipboard(APP_HWND))
		{
			EmptyClipboard();												// empty it (all handles to NULL);
			if((SetClipboardData((UINT)CF_DIB,hXferBuffer))==NULL)
			{
				CloseClipboard();												
				ErrorBox("ClipBoard_SendDIB(): Dammit, some sort of problem writing to the clipboard...");
				return FALSE;	// hmmmm... Oh well.
			}
			CloseClipboard();
			return TRUE;
		}
	}

	ErrorBox(va("ClipBoard_SendDIB(): Dammit, I can't allocate %d bytes for some strange reason (reboot, then try again, else tell me - Ste)",iBytes));
	return FALSE;			
}


BOOL Clipboard_SendString(LPCSTR psString)
{
	HGLOBAL hXferBuffer = GlobalAlloc((UINT)GMEM_MOVEABLE|GMEM_DDESHARE,(DWORD)strlen(psString)+1);	
	if (hXferBuffer)
	{
   		char *psLockedDest = (char*) GlobalLock(hXferBuffer);	
		memcpy(psLockedDest,psString,strlen(psString)+1);
		GlobalUnlock(psLockedDest);	
	
		if (OpenClipboard(APP_HWND))
		{
			EmptyClipboard();												// empty it (all handles to NULL);
			if((SetClipboardData((UINT)CF_TEXT,hXferBuffer))==NULL)
			{
				CloseClipboard();												
				ErrorBox("Clipboard_SendString(): Dammit, some sort of problem writing to the clipboard...");
				return FALSE;	// hmmmm... Oh well.
			}
			CloseClipboard();
			return TRUE;
		}
	}

	ErrorBox(va("Clipboard_SendString(): Dammit, I can't allocate %d bytes for some strange reason (reboot, then try again, else tell me - Ste)",strlen(psString)+1));
	return FALSE;			
}








////////////////////////////////////////////////////////////////////////
//
// from this point on is a bunch of crap that's not strictly clipboard stuff, 
//	but is used only in conjunction with it and therefore may as well go here.
//
//	This also includes another some more BMP code. This is here purely because
//	we need to be able to write BMPs, not just read them as the other code does...
//


#include <pshpack1.h>
typedef struct
{
	BYTE r,g,b;	// R&B different order to windoze's RGBTRIPLE struct
} GLRGBBYTES,*LPGLRGBBYTES;
#include <poppack.h>

typedef struct
{		
	BITMAPINFOHEADER BMPInfoHeader;
	RGBTRIPLE RGBData[1];	// a label just for addressing purposes, the actual array size depends on screen dims
} MEMORYBMP, *PMEMORYBMP;


static bool BMP_FlipTrueColour(LPCSTR psFilename);


static int iBMP_PixelWriteOffset;
static PMEMORYBMP pBMP = NULL;
static int iBMP_MallocSize;
//
static FILE *fhBMP = 0;


bool BMP_GetMemDIB(void *&pvAddress, int &iBytes)
{
	if (pBMP)
	{
		pvAddress = pBMP;
		iBytes = iBMP_MallocSize;
		return true;
	}

	return false;
}


void BMP_Free(void)
{
	if (pBMP)
	{
		free(pBMP);
		pBMP = NULL;
	}	
}



// open 24-bit RGB file either to disk or memory
//
// if psFileName == NULL, open memory file instead
//
static bool BMP_Open(LPCSTR psFilename, int iWidth, int iHeight)
{
	BITMAPFILEHEADER BMPFileHeader;
	BITMAPINFOHEADER BMPInfoHeader;

	int iPadBytes	= (4-((iWidth * sizeof(RGBTRIPLE))%4))&3;
	int iWidthBytes =     (iWidth * sizeof(RGBTRIPLE))+iPadBytes;
		
	BMP_Free();
	fhBMP = NULL;
	
	if (psFilename)
	{
		extern void CreatePath (const char *path);
		CreatePath(psFilename);

		fhBMP = fopen(psFilename,"wb");
		if (!(int)fhBMP)
			return false;
	}
	else
	{
		iBMP_MallocSize = sizeof(BITMAPINFOHEADER) + (iWidthBytes * iHeight);
		pBMP = (PMEMORYBMP) malloc ( iBMP_MallocSize );
		if (!pBMP)
			return false;
	}

	memset(&BMPFileHeader, 0, sizeof(BITMAPFILEHEADER));
	BMPFileHeader.bfType=(WORD)('B'+256*'M');
//	int iPad= ((sizeof(RGBTRIPLE)*iWidth)%3)*iHeight;	
//	BMPFileHeader.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(sizeof(RGBTRIPLE)*iWidth*iHeight);//+iPad;
	BMPFileHeader.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+(iWidthBytes * iHeight);

	BMPFileHeader.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);	// No palette
	
	if (fhBMP)
	{
		fwrite	(&BMPFileHeader,sizeof(BMPFileHeader),1,fhBMP);
	}
	else
	{
		// memory version doesn't use the BITMAPFILEHEADER structure
	}

	memset(&BMPInfoHeader, 0, sizeof(BITMAPINFOHEADER));
	BMPInfoHeader.biSize=sizeof(BITMAPINFOHEADER);
	BMPInfoHeader.biWidth=iWidth;
	BMPInfoHeader.biHeight=iHeight;
	BMPInfoHeader.biPlanes=1;
	BMPInfoHeader.biBitCount=24;
	BMPInfoHeader.biCompression=BI_RGB; 
	BMPInfoHeader.biSizeImage=0;// BMPFileHeader.bfSize - (sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER));	// allowed for BI_RGB bitmaps
	BMPInfoHeader.biXPelsPerMeter=0;	// don't know about these
	BMPInfoHeader.biYPelsPerMeter=0;
	BMPInfoHeader.biClrUsed=0;
	BMPInfoHeader.biClrImportant=0;

	if (fhBMP)
	{
		fwrite	(&BMPInfoHeader,sizeof(BMPInfoHeader),1,fhBMP);
	}
	else
	{
		pBMP->BMPInfoHeader = BMPInfoHeader;	// struct copy
		iBMP_PixelWriteOffset = 0;
	}

	return true;
}

static bool BMP_WritePixel(byte Red, byte Green, byte Blue)
{
	RGBTRIPLE Trip = {0,0,0};

	Trip.rgbtRed	= Red;
	Trip.rgbtGreen	= Green;
	Trip.rgbtBlue	= Blue;

	if (fhBMP)
	{
		fwrite(&Trip, sizeof(RGBTRIPLE), 1, fhBMP);
	}
	else
	{
		RGBTRIPLE *pDest = (RGBTRIPLE *) ((byte *)pBMP->RGBData + iBMP_PixelWriteOffset);

		*pDest = Trip;

		iBMP_PixelWriteOffset += sizeof(RGBTRIPLE);
	}

	return true;
}

// BMP files need padding to 4-byte boundarys after writing each scan line... (which sucks, and messes up pixel indexing)
//
static bool BMP_WriteLinePadding(int iPixelsPerLine)
{
	static char cPad[4]={0};

	int iPadBytes = (4-((iPixelsPerLine * sizeof(RGBTRIPLE))%4))&3;

	if (iPadBytes)
	{
		if (fhBMP)
		{
			fwrite( &cPad, iPadBytes, 1, fhBMP);
		}
		else
		{
			iBMP_PixelWriteOffset += iPadBytes;	// <g>, can't be bothered padding with zeroes
		}
	}
	return true;
}

// BMP files are stored upside down, but if we're writing this out as a result of doing an OpenGL pixel read, then
//	the src buffer will be upside down anyway, so I added this flip-bool -Ste
//
// (psFilename can be NULL for mem files)
//
static bool BMP_Close(LPCSTR psFilename, bool bFlipFinal)
{
	if (fhBMP)
	{
		fclose (fhBMP);
		fhBMP = NULL;
	}
	else
	{
		#if 1

		int iPadBytes	= (4-((pBMP->BMPInfoHeader.biWidth * sizeof(RGBTRIPLE))%4))&3;
		int iWidthBytes =     (pBMP->BMPInfoHeader.biWidth * sizeof(RGBTRIPLE))+iPadBytes;

		assert(iBMP_PixelWriteOffset == iWidthBytes * pBMP->BMPInfoHeader.biHeight);
		assert((iBMP_PixelWriteOffset + (int)sizeof(BITMAPINFOHEADER)) == iBMP_MallocSize);

		#endif
	}

	if (bFlipFinal)
	{
		if (psFilename)
		{
			if (!BMP_FlipTrueColour(psFilename))
				return false;
		}
	}

	return true;
}


static bool BMP_FlipTrueColour(LPCSTR psFilename)
{
	BITMAPFILEHEADER BMPFileHeader;
	BITMAPINFOHEADER BMPInfoHeader;
	RGBTRIPLE *RGBTriples, *tTopLine, *tBottomLine;//, *AfterLastLine;
	BYTE  *byTopLine, *byBottomLine, *byAfterLastLine;
	RGBTRIPLE Trip;
	int x,y;
	int iPadBytes,iRealWidth;


// reopen it to flip it
	fhBMP=fopen(psFilename,"rb");	// checked fopen
	if (!(int)fhBMP)
		return false;

	fread	(&BMPFileHeader,sizeof(BMPFileHeader),1,fhBMP);
	fread	(&BMPInfoHeader,sizeof(BMPInfoHeader),1,fhBMP);
	iPadBytes = (4-((BMPInfoHeader.biWidth * sizeof(RGBTRIPLE))%4))&3;
	iRealWidth=(sizeof(RGBTRIPLE)*BMPInfoHeader.biWidth)+iPadBytes;

	RGBTriples=(RGBTRIPLE *)malloc(iRealWidth*BMPInfoHeader.biHeight);
	fread	(RGBTriples,iRealWidth*BMPInfoHeader.biHeight,1,fhBMP);

	fclose (fhBMP);

	byTopLine=(BYTE *)RGBTriples;
	byAfterLastLine=byTopLine+iRealWidth*BMPInfoHeader.biHeight;

// actually flip it
	for (y=0;	y<BMPInfoHeader.biHeight/2;	y++)
	{
		byBottomLine=byAfterLastLine-((y+1)*iRealWidth);

		tTopLine=(RGBTRIPLE *)byTopLine;
		tBottomLine=(RGBTRIPLE *)byBottomLine;

		for (x=0;	x<BMPInfoHeader.biWidth;	x++)
		{
			Trip=tTopLine[x];
			tTopLine[x]=tBottomLine[x];
			tBottomLine[x]=Trip;
		}

		byTopLine+=iRealWidth;
	}

	
// rewrite it flipped
	fhBMP=fopen(psFilename,"wb");	// checked fopen
	fwrite	(&BMPFileHeader,sizeof(BMPFileHeader),1,fhBMP);
	fwrite	(&BMPInfoHeader,sizeof(BMPInfoHeader),1,fhBMP);
	fwrite	(RGBTriples,(iRealWidth)*BMPInfoHeader.biHeight,1,fhBMP);
	fclose (fhBMP);
	free(RGBTriples);

	return true;
}












// if psFilename == NULL, takes a memory screenshot in DIB format (for copying to clipboard)
//
bool ScreenShot(LPCSTR psFilename,			// else NULL = take memory snapshot (for clipboard)
				LPCSTR psCopyrightMessage,	// /* = NULL */
				int iWidth,					// /* = <screenwidth>  */
				int iHeight					// /* = <screenheight> */
				)
{
	bool bReturn = false;

	int iOldPack;	
	glGetIntegerv(GL_PACK_ALIGNMENT,&iOldPack);
	glPixelStorei(GL_PACK_ALIGNMENT,1);
	
	void *pvGLPixels = malloc (iWidth * iHeight * 3);	// 3 = R,G,B

	if (pvGLPixels)
	{
		if (psCopyrightMessage)
		{
			bool bOldInhibit = gbTextInhibit;
			gbTextInhibit = false;
			Text_DisplayFlat(psCopyrightMessage, 0, (iHeight-TEXT_DEPTH)-1,255,255,255);	// y-1 = aesthetic only
			gbTextInhibit = bOldInhibit;
		}

		glReadPixels(	0,					// x
						0,					// y (from bottom left)
						iWidth,				// width
						iHeight,			// height
						GL_RGB,				// format
						GL_UNSIGNED_BYTE,	// type
						pvGLPixels			// buffer ptr 			
						);

		// save area is valid size...
		//
		if (BMP_Open(psFilename, iWidth, iHeight))
		{
			for (int y=0; y<iHeight; y++)				
			{
				LPGLRGBBYTES 
				lpGLRGBBytes = (LPGLRGBBYTES) pvGLPixels;
				lpGLRGBBytes+= y * iWidth;

				for (int x=0; x<iWidth; x++, lpGLRGBBytes++)
				{
					BMP_WritePixel(lpGLRGBBytes->r,lpGLRGBBytes->g,lpGLRGBBytes->b);
				}

				BMP_WriteLinePadding(iWidth);	// arg is # pixels per row	
			}

			BMP_Close(psFilename,false);	// false = bFlipFinal			

			bReturn = true;
		}

		free(pvGLPixels);
		pvGLPixels = NULL;	// yeah...yeah
	}	

	glPixelStorei(GL_PACK_ALIGNMENT,iOldPack);

	return bReturn;
}


//////////////// eof ////////////////

