/*************************************************************************

   Copyright 2011 Yuri Dario <mc6530@mclink.it>

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

 ************************************************************************/

#define INCL_WIN
#include <svpm.h>

#ifndef _OS2CLIPBOARD_HXX_
#include "Os2Clipboard.hxx"
#endif

// same typedefs from win32 sdk
typedef unsigned short WORD;
typedef unsigned long DWORD;

#pragma pack(push, 1)

typedef struct {
  PM_BYTE rgbBlue;
  PM_BYTE rgbGreen;
  PM_BYTE rgbRed;
  PM_BYTE rgbReserved;
} RGBQUAD, *LPRGBQUAD;

typedef struct
{
    WORD    bfType;
    DWORD   bfSize;
    WORD    bfReserved1;
    WORD    bfReserved2;
    DWORD   bfOffBits;
} W32_BITMAPFILEHEADER, *PW32_BITMAPFILEHEADER;

typedef struct
{
    DWORD 	biSize;
    LONG  	biWidth;
    LONG  	biHeight;
    WORD 	biPlanes;
    WORD 	biBitCount;
    DWORD 	biCompression;
    DWORD 	biSizeImage;
    LONG  	biXPelsPerMeter;
    LONG  	biYPelsPerMeter;
    DWORD 	biClrUsed;
    DWORD 	biClrImportant;
} W32_BITMAPINFOHEADER, *PW32_BITMAPINFOHEADER;

#pragma pack(pop)

// store screen bitcount
LONG	lBitCountScreen;

/*
 * Convert an OOo bitmap to an OS/2 bitmap handle
 *
 * An OOo bitmap is a BITMAPFILEHEADER structure followed by a Windows DIB 
 *
 * OS/2 InfoHeader is a superset of Win32 InhoHeader, so we can just copy
 * the win32 memory over the os2 memory and fix the cbFix field.
 * colortable and bitmap data share the same format.
 *
*/
HBITMAP	OOoBmpToOS2Handle( Any &aAnyB)
{
	// copy bitmap to clipboard
	Sequence<sal_Int8> ByteStream;
	aAnyB >>= ByteStream;

	// get w32 file header data
	PW32_BITMAPFILEHEADER pbfh = (PW32_BITMAPFILEHEADER)ByteStream.getArray();
	// get w32 info header
	PW32_BITMAPINFOHEADER pbih = (PW32_BITMAPINFOHEADER) (pbfh+1);

	// create os2 infoheader2 (same fields of w32)
	BITMAPINFOHEADER2 bih2;
	memset( &bih2, 0, sizeof( bih2));
	memcpy( &bih2, pbih, pbih->biSize);
	bih2.cbFix = sizeof(bih2);

	// Determine size of color table
	int iNumColors, numbits=bih2.cPlanes * bih2.cBitCount;
	if (numbits != 24)
		iNumColors = bih2.cclrUsed ? bih2.cclrUsed : 2<<numbits;
	else
		iNumColors = bih2.cclrUsed;
	int iColorTableSize = iNumColors*sizeof(RGB2);

	// allocate bitmap info2 (header2+colortable)
	PBITMAPINFO2 pbi2=(PBITMAPINFO2) malloc( sizeof(BITMAPINFOHEADER2)+iColorTableSize);
	// setup header fields
	memcpy( pbi2, &bih2, sizeof(BITMAPINFOHEADER2));
	// copy color palette (follows pbih)
	memcpy( &pbi2->argbColor[0], (pbih+1), iColorTableSize);

	// get bitmap data
	PBYTE pbPelData = (PBYTE)ByteStream.getArray() + pbfh->bfOffBits;
	HPS hps = WinGetPS(HWND_DESKTOP);
	HBITMAP hbm = GpiCreateBitmap( hps, &bih2, CBM_INIT, pbPelData, pbi2);
	debug_printf( "OOoBmpToOS2Handle hbm %x\n", hbm);
	WinReleasePS(hps);

	// return handle
	return hbm;
}

/*
 * Convert an OS/2 bitmap handle to OOo bitmap
 *
 * First we need to copy the bitmap to a PS, then we can get bitmap data. 
 *
*/
int	OS2HandleToOOoBmp( HBITMAP hbm, Sequence< sal_Int8 >* OOoDIBStream)
{
	HAB 	hab = WinQueryAnchorBlock(HWND_DESKTOP);
	HDC 	hdc; 
	SIZEL 	sizl; 
	HPS 	hps;
	PM_BYTE*	pbBuffer;
	ULONG	cbBuffer;

	struct {
		BITMAPINFOHEADER2 bmp2;
		RGB2 argb2Color[0x100];
	} bm;
	
	if (!lBitCountScreen) {
		HPS hps = WinGetPS(HWND_DESKTOP);
		HDC hdc = GpiQueryDevice(hps);
		DevQueryCaps(hdc, CAPS_COLOR_BITCOUNT, 1L, &lBitCountScreen);
		WinReleasePS(hps);
	}

	// STEP 1: get OS/2 bitmap data and header
	// get bitmap header
	memset(&(bm.bmp2), 0, sizeof(bm.bmp2));
	bm.bmp2.cbFix = 16;
	GpiQueryBitmapInfoHeader(hbm, &bm.bmp2);

	/* Data only actually stored in clipboard quality */
	if ( lBitCountScreen < bm.bmp2.cBitCount )
		bm.bmp2.cBitCount = lBitCountScreen;

	if ( bm.bmp2.cBitCount == 16 )
		bm.bmp2.cBitCount = 24;

	if ( bm.bmp2.cPlanes != 1 ) {
		return 0;
	}

	if ( (hdc = DevOpenDC(hab, OD_MEMORY, "*", 0L, (PDEVOPENDATA) NULL, (HDC) NULL)) == (HDC) NULL ) {
		return 0;
	}

	sizl.cx = bm.bmp2.cx;
	sizl.cy = bm.bmp2.cy;
	if ( (hps = GpiCreatePS(hab, hdc, &sizl, PU_PELS | GPIF_DEFAULT | GPIT_MICRO | GPIA_ASSOC)) == (HPS) NULL ) {
		DevCloseDC(hdc);
		return 0;
	}
	// copy bitmap to hps
	GpiSetBitmap(hps, hbm);
	
	// buffer lengths
	cbBuffer = (((bm.bmp2.cBitCount * bm.bmp2.cx) + 31) / 32) * 4 * bm.bmp2.cy * bm.bmp2.cPlanes;
	pbBuffer = (PM_BYTE*) malloc( cbBuffer);
	// now get bitmap data
	GpiQueryBitmapBits(hps, 0L, (LONG) bm.bmp2.cy, pbBuffer, (BITMAPINFO2*)&bm);
	// free OS/2 resources
	GpiSetBitmap(hps, (HBITMAP) NULL);
	GpiDestroyPS(hps);
	DevCloseDC(hdc);

	// STEP 2: now convert to Win32 DIB
	// Determine size of color table
	int iNumColors, numbits=bm.bmp2.cPlanes * bm.bmp2.cBitCount;
	if (numbits != 24)
		iNumColors = bm.bmp2.cclrUsed ? bm.bmp2.cclrUsed : 2<<numbits;
	else
		iNumColors = bm.bmp2.cclrUsed;
	int iColorTableSize = iNumColors*sizeof(RGBQUAD);
	
	// reallocate data stream object size
	OOoDIBStream->realloc( sizeof( W32_BITMAPFILEHEADER ) 
				+ sizeof( W32_BITMAPINFOHEADER) + iColorTableSize + cbBuffer);

	// fill w32 file header data
	PW32_BITMAPFILEHEADER pbfh = (PW32_BITMAPFILEHEADER) OOoDIBStream->getArray();
	memset( pbfh, 0, sizeof( W32_BITMAPFILEHEADER));
	pbfh->bfType = 'MB';
	pbfh->bfSize = sizeof( W32_BITMAPFILEHEADER ) 
				+ sizeof( W32_BITMAPINFOHEADER) + iColorTableSize + cbBuffer;
	pbfh->bfOffBits = sizeof( W32_BITMAPFILEHEADER) + sizeof( W32_BITMAPINFOHEADER) + iColorTableSize;

	// fill w32 info header
	PW32_BITMAPINFOHEADER pbih = (PW32_BITMAPINFOHEADER) (pbfh+1);
	// copy header fields (only win32 ones) and fix size
	memcpy( pbih, &bm.bmp2, sizeof(W32_BITMAPINFOHEADER));
	pbih->biSize = sizeof(W32_BITMAPINFOHEADER);

	// fill color palette (follows pbih)
	memcpy( (pbih+1), &bm.argb2Color[0], iColorTableSize);

	// fill bitmap data
	memcpy( (char*) pbfh + pbfh->bfOffBits, pbBuffer, cbBuffer);

	// done
	free( pbBuffer);
	return 1;
}

#ifdef TESTBMP

#include <io.h>
#include <fcntl.h>
#include <stdio.h>

int main( void)
{
	HAB hAB = WinQueryAnchorBlock( HWND_DESKTOP );
	
	// query clipboard data to get mimetype
	if( WinOpenClipbrd( hAB ) )
	{
		ULONG handle = WinQueryClipbrdData( hAB, CF_BITMAP);
		if (handle) {
			Sequence< sal_Int8 > winDIBStream;
			// convert to oustring and return it
			if (OS2HandleToOOoBmp( handle, &winDIBStream) == 1) {
				printf( "Conversion ok.\n");
				int fd = open( "test.bmp", O_BINARY | O_CREAT | O_TRUNC | O_RDWR);
				printf( "writing to fd %d\n", fd);
				write( fd, winDIBStream.getArray(), winDIBStream.getLength());
				close( fd);
			} else
				printf( "failed conversion.\n");
			
		}
		WinCloseClipbrd( hAB);
	}
	return 0;
}

#endif //TESTBMP

