/*************************************/
/* container_trans.cpp               */
/*                                   */
/* source for container transparency */
/*                                   */
/* loïc berthelot    janv 2004       */
/*************************************/

#include "container_trans.h"
#include "bitmap.h"
extern "C"
{
#include "osversion.h"
}
#include "container.h"
#include "colors.h"


//$LB (16/12/2003)
PtrObjBitmap LIB2DtransBkg=NULL; /* background bitmap for simulated window transparency */

PSLWA pSetLayeredWindowAttributes = NULL;
HMODULE user32DLL = NULL;
 

//$LB (16/12/2003)
int InitWindowTransparency(PtrObjWindow W)
{
	osinfo theOSinfo;
	RECT screen;


//***********************************
#if DEBUG_LIB2D
MMechostr (0, "LIB2D InitWindowTransparency\n");
#endif
//***********************************

   theOSinfo = GetOsInfo ();

	//
	// WIN2K, WINXP
	//
	if (theOSinfo >= kOsInfoWin2000)
	{
		if (user32DLL == NULL)
		{
			if ((user32DLL = LoadLibrary ("user32")) == NULL)
			{
				MMechostr(MSKDEBUG, "\nWindows Transparency Initialization failed!... user32.dll not found...\n");
				return 0;
			}

			pSetLayeredWindowAttributes = (PSLWA)GetProcAddress(user32DLL, "SetLayeredWindowAttributes");

			if (pSetLayeredWindowAttributes == NULL) 
			{
				MMechostr (MSKDEBUG, "\nWindows Transparency Initialization failed!... SetLayeredWindowAttributes function not found!\n");
				return 0;
			}
		}
	}


	//
	// OTHERS WINDOWS VERSION
	//
	if ((theOSinfo < kOsInfoWin2000 ) || ((W != NULL) && (W->tSimulated == 1)))
	{
		// LIB2D transparency bitmap background
		if (LIB2DtransBkg == NULL)
		{
		  GetWindowRect ( GetDesktopWindow () , &screen) ;

		  LIB2DtransBkg = (PtrObjBitmap) malloc (sizeof(struct ObjBitmap));
		  LIB2DtransBkg->TailleW = screen.right - screen.left;
		  LIB2DtransBkg->TailleH = screen.bottom - screen.top;
		  LIB2DtransBkg->BPP = 24;
		  LIB2DtransBkg->BytesPP = 3;

		  ObjBitmap_New (LIB2DtransBkg, NULL);
		}
		
		// update the screen capture
		ShowWindow (W->WHandler, SW_HIDE);
		Sleep (100);
		CaptureScreen (LIB2DtransBkg, 0, 0, LIB2DtransBkg->TailleW, LIB2DtransBkg->TailleH);
		Sleep (100);
		ShowWindow (W->WHandler, SW_SHOW);

		// window transparency buffer
		if (W != NULL)
		if (W->tBuf == NULL)
		{
			W->tBuf = (PtrObjBitmap) malloc (sizeof(struct ObjBitmap));
			W->tBuf->TailleW = W->TailleW;
			W->tBuf->TailleH = W->TailleH;
			W->tBuf->BPP = 24;
			W->tBuf->BytesPP = 3;
			W->tX = W->PosX;
			W->tY = W->PosY;

			ObjBitmap_NewDIBSection (W->tBuf, NULL);
		}
	}

	return 1;
}





//$LB (16/12/2003)
/********************************************************/
/*                                                      */
/* int _SIMULATEcontainerTransparency                   */
/*                                                      */
/* allows to switch the transparency mode.              */
/*                                                      */
/* usefull to take a look at the graphic behaviour the  */
/* container should have if the transparency is simulated  */
/* (<=> when scol is running under Win95,98,ME)         */
/*                                                      */
/* parameters :                                         */
/*   - ObjWin container                                    */
/*   - I flag (1 : simulation ON; 0 : simulation OFF)   */
/*                                                      */
/********************************************************/
int _SIMULATEcontainerTransparency (mmachine m)
{
osinfo theOSinfo = GetOsInfo();
container* cont;
PtrObjWindow W;
HWND H;
int s, flag;


//***********************************
#if DEBUG_LIB2D
MMechostr (0, "_SIMULATEcontainerTransparency\n");
#endif
//***********************************


    flag  = MTOI (MMpull(m));
	s      = MMpull(m) ;

    if ( s == NIL ) 
	{
		MMechostr(MSKDEBUG, "\n_SIMULATEcontainerTransparency : container is NIL!...\n");
		return MMpush ( m , NIL ) ;
	}


    if ((cont=RetrievePtrContainer(m,MTOP(s)))==NULL)
	{
		MMechostr(MSKTRACE,"_SIMULATEcontainerTransparency : container already destroyed\n");
		return MMpush ( m , NIL ) ;
	}
	else if (cont->GetPtrWindow()->parent != NULL)
	{
		W = cont->GetPtrWindow()->parent;
		H = W->WHandler;


		//
		// WinXP, Win2000
		//
		if ( theOSinfo >= kOsInfoWin2000 )
		{

			if (flag == 0)
			{
				W->tSimulated = 0;

				if (!InitWindowTransparency (NULL))
				{
					MMechostr(0, "\nInitWindowTransparency failed\n");
					return MMpush(m,NIL);
				}

				pSetLayeredWindowAttributes (H,	_COLOR_BGR_TO_I (W->tColorB, W->tColorG, W->tColorR), W->tfactor, W->tflags);

			}
			else
			{
				W->tSimulated = 1;

				if (!InitWindowTransparency (W))
				{
					MMechostr(0, "\nInitWindowTransparency failed\n");
					return MMpush(m,NIL);
				}

				pSetLayeredWindowAttributes (H,	0xCCCCCC, 255, LWA_ALPHA);

				
			}


			//$LB (16/12/2003)
 			//$LBDEBUG
			//$LB TODO TO DO : event if PAINTcontainer is called after that, there is no transparent blit 'til the window is moved... so what's wrong?

		}
		
		//
		// OTHER VERSIONS : nothing to do
		//

	}
	else
	{
		MMechostr (MSKDEBUG, "\n_SIMULATEcontainerTransparency : failed!... window container is NULL\n");
		return MMpush (m, NIL);
	}

//***********************************
#if DEBUG_LIB2D
MMechostr (0, "_SIMULATEcontainerTransparency ok\n");
#endif
//***********************************

	return MMpush (m, s);
    
}





//$LB (16/12/2003)
/********************************************************/
/* internal body                                        */
/*                                                      */
/* int SetWindowTransparency                            */
/*                                                      */
/* allows to set the window transparency, which can be  */
/* done with a transparency color, or with an alpha     */
/* blending factor.                                     */
/*                                                      */
/* it's fully functional with WinXP and Win2000.        */
/* With other Windows versions, we have to simulate the */
/* process with a screen capture, within the window     */
/* painting procedure.                                  */
/*                                                      */
/********************************************************/
int SetWindowTransparency (PtrObjWindow Wnd, int color, int factor, int flags)
{
	HWND H = (HWND)Wnd->WHandler;

//***********************************
#if DEBUG_LIB2D
MMechostr (0, "LIB2D SetWindowTransparency\n");
#endif
//***********************************


		// <=> do we have to simulate the process ?
		if (Wnd->tSimulated == 0)
		//
		// WIN XP and WIN 2K : no
		//
		{
			if (flags <=0) {flags = WN_TRANS_ALPHA; factor = 255;}

			SetWindowLong(H, GWL_EXSTYLE, GetWindowLong(H, GWL_EXSTYLE) | WS_EX_LAYERED);
			
			if (!InitWindowTransparency(Wnd))
				return 0;

			pSetLayeredWindowAttributes(H, color, factor, flags);
		}
		//
		// OTHER VERSIONS : yes, transparency will be simulated in blit process
		//
		else
		{
			return 1;
		}

		return 1;
}






//$LB (16/12/2003)
/********************************************************/
/*                                                      */
/* int _SETcontainerTransparency                        */
/*                                                      */
/* allows to set the window container transparency.     */
/*                                                      */
/* use the internal SetWindowTransprency function.      */
/*                                                      */
/* parameters :                                         */
/*   - ObjWin window                                    */
/*   - I transparencyColor   [0x000000 --> 0xFFFFFF]    */
/*   - I alphaBlendingFactor [0x00 --> 0xFF]            */
/*   - I flags (WNT_COLORKEY|WNT_ALPHA)                 */
/*                                                      */
/********************************************************/
//$BLG - Note - Original encountered problem:
//In Voyager 5.2.06, we wanted to include a 3D logo in a translucent windowed Container.
//We got an awful flickering phenomenom ...
//Explanation:
//When setting Container's transparency, you give its host window the WS_EX_LAYERED flag.
//This is not "compatible" with OpenGL SwapBuffers functionality, which is used for 
//blitting rendered scenes.
//Sources:
//- http://www.gamedev.net/community/forums/topic.asp?topic_id=458345
//- http://msdn2.microsoft.com/en-us/library/ms632680.aspx
//- http://groups.google.com/group/microsoft.public.win32.programmer.gdi/msg/9b5dc176dda33a57?hl=en&lr=&ie=UTF-8&oe=utf-8 
int _SETcontainerTransparency (mmachine m)
{
	int s, color, factor, flags;
	container* cont;
	PtrObjWindow parent;

//***********************************
#if DEBUG_LIB2D
MMechostr (0, "_SETcontainerTransparency\n");
#endif
//***********************************

  flags  = MTOI (MMpull(m));
	factor = MTOI (MMpull(m));
	color  = MTOI (MMpull(m));
	s      = MMpull(m) ;

  if (s == NIL) 
	{
		MMechostr(MSKDEBUG, "\n_SETcontainerTransparency : container is NIL!...\n");
		return MMpush(m, NIL);
	}

	if ((cont = RetrievePtrContainer(m, MTOP(s))) == NULL)
	{
		MMechostr(MSKTRACE, "_SETcontainerTransparency: container already destroyed\n");
		return MMpush(m, NIL);
	}
	else if (cont->GetPtrWindow()->parent != NULL)
	{
		parent = cont->GetPtrWindow()->parent;

		if (!SetWindowTransparency(parent, color, factor, flags))
		{
			//MMechostr(0, "!SetWindowTransparency\n");
			return MMpush(m, NIL);
		}
		else 
		{
			//MMechostr(0, "SetWindowTransparency\n");
			_COLOR_I_TO_BGR (color, &(parent->tColorB), &(parent->tColorG), &(parent->tColorR));
			
			parent->tfactor = factor;
			parent->tflags = flags;

//***********************************
#if DEBUG_LIB2D
MMechostr (0, "_SETcontainerTransparency ok\n");
#endif
//***********************************

			return MMpush (m, s);
		}
	}
	else
	{
		MMechostr(MSKDEBUG, "_SETcontainerTransparency : window container is NULL!...\n");
		return MMpush(m, NIL);
	}
}











//$LB (16/12/2003)
/*****************************************/
/* TransparentBlitBuffer                 */
/*                                       */
/* now, scol windows can be transparent. */
/* if Win95/98/ME, we have to simulate   */
/* the transparency.                     */
/*                                       */
/*****************************************/
//                                                window :
//                                                ---------------------------
//                                                |     zone1               |
//$LB (16/12/2003)                                |      ----------         |
//                                                | zone2| bmp    |  zone3  |  
// here we have to simulate the transparency      |      |        |         |
//                                                |      ----------         |
//                                                |     zone4               |
//                                                ---------------------------
//
//
int WindowScolWin::TransparentBlitBuffer (CObjBufferWindows* CoBuffer, int buff, int PosX,int PosY,int dw,int dh,int sx,int sy)
{
	HDC Dcw,Dcbuf;
	HBITMAP OldBuf;
	WINDOWPLACEMENT pwi;
	OBJBITMAP_BUFFER bmp;
	OBJBITMAP_BUFFER bkg;
	OBJBITMAP_BUFFER buf;
	int j, i, restbmp, restbuf, restbkg; // windows bitmap are 32bits aligned
	register unsigned char db, dg, dr, sb, sg, sr;
	int BytesPP=3;
	//
	// for clipping
	//
	int bkgX, bkgY, bufX, bufY, bmpX, bmpY;
	int bkgW, bkgH, bufW, bufH, bmpW, bmpH, MAXW, MAXH; 
	int winW, winH;

  if (this->parent != NULL)
  {
			  // get actual window location
			  pwi.length = sizeof (WINDOWPLACEMENT);
			  GetWindowPlacement (this->parent->WHandler, &pwi);
			  this->parent->tX = pwi.rcNormalPosition.left;
			  this->parent->tY = pwi.rcNormalPosition.top;

			  MAXW = this->parent->tBkg->TailleW;  MAXH = this->parent->tBkg->TailleH;

			  winW = this->parent->TailleW;        winH = this->parent->TailleH;
 
			  bmpX = sx; bmpY = sy; bmpW = dw; bmpH = dh;
			  bufX = PosX; bufY = PosY; bufW = dw; bufH = dh;
			  bkgX = this->parent->tX; bkgY = this->parent->tY; bkgW = dw; bkgH = dh;



			  //$LB (16/12/2003)
			  //$LBDEBUG
			  //$LB TO DO TODO : clip coordinates. there is a bug about dw (dh?) when window is outside the screen
			  // the bug seems to come from the redrawArea in HdlPaint Callback.
			  // now we just get out of blit process if window is outside the screen, 
			  // to do : process blit when window is outside the screen, in clipping coordinates, and in hacking the bug
			  if ((this->parent->tX < 0) || (this->parent->tY < 0) || (this->parent->tX+winW-1 >= MAXW) || (this->parent->tY+winH-1 >= MAXH)
				   || (PosX+dw >this->parent->TailleW) || (PosY+dh >this->parent->TailleH))
				  return 0;

			  // 
			  // clipping coordinates
			  //
			  /*			  
			  if ((this->parent->tX+PosX) <0) 
			  { 
				bkgX    = 0;  bkgW = dw + this->parent->tX + PosX;
				bufX    = -this->parent->tX +PosX; bufW = dw + this->parent->tX + PosX;
				bmpX    = -this->parent->tX; bmpW = dw + this->parent->tX +sx;
				
			  }
			  else if (this->parent->tX+winW > MAXW)
			  {
				bkgX = this->parent->tX; bkgW = winW - (bkgX + winW - MAXW);
				bufX = 0; bufW = winW - (bkgX + winW - MAXW);
			  }
			  */




			  // 
			  // BLIT BITMAP
			  //
			  buf = (OBJBITMAP_BUFFER) (this->parent->tBuf->bits + bufY*this->parent->tBuf->BPL       + bufX*BytesPP);
			  bmp = (OBJBITMAP_BUFFER) (CoBuffer->WinBmp         + bmpY*CoBuffer->WinBitsBitmapLength + bmpX*BytesPP);
			  bkg = (OBJBITMAP_BUFFER) (this->parent->tBkg->bits + bkgY*this->parent->tBkg->BPL       + bkgX*BytesPP);
			  restbuf = this->parent->tBuf->BPL       - (bufW*BytesPP);      
			  restbmp = CoBuffer->WinBitsBitmapLength - (bmpW*BytesPP);
			  restbkg = this->parent->tBkg->BPL       - (bkgW*BytesPP);

			  for (j=0; j < bmpH; j++, bmp+=restbmp, buf+=restbuf, bkg+=restbkg)
			  {
			      
				  for (i=0; i < bmpW; i++, bmp+=BytesPP, buf+=BytesPP, bkg+=BytesPP)				  
				  {

						// if the color is the transparency color 
						if (  (this->parent->tflags & WN_TRANS_COLOR)  &&  ((*bmp) == this->parent->tColorB)  &&  ((*(bmp+1)) == this->parent->tColorG)  &&  ((*(bmp+2)) == this->parent->tColorR)  )

						// blit WindowBuffer <--- BackgroundBitmap
						{				
						    (*buf) = (*bkg); *(buf+1) = *(bkg+1); *(buf+2) = *(bkg+2);
						}

						// else if there an alpha blending factor
						else if (this->parent->tflags & WN_TRANS_ALPHA)

						// alphablit WindowBuffer <-- (background, Image)
						{
							db = *(bkg); dg = *(bkg+1); dr = *(bkg+2);
							sb = *(bmp); sg = *(bmp+1); sr = *(bmp+2);

							if (this->parent->tfactor == 0)
							{
								(*buf) = db; *(buf+1) = dg; *(buf+2) = dr;
							}
							if (this->parent->tfactor == 255)
							{
								(*buf) = sb; *(buf+1) = sg; *(buf+2) = sr;
							}
							else
							{
							    (*buf)   = db + this->parent->tfactor * (sb - db) / 255;
								*(buf+1) = dg + this->parent->tfactor * (sg - dg) / 255;
								*(buf+2) = dr + this->parent->tfactor * (sr - dr) / 255;
							}
						}

						// else simply blit WindowBuffer <-- Image
						else
						{ (*buf) = (*bmp); *(buf+1) = *(bmp+1); *(buf+2) = *(bmp+2); }

					
				  } // end i
			  }     // end j


			  //
			  // FINAL STAGE : blit Window <-- WindowBuffer
			  //
				Dcw = GetDC (Whwnd) ;
				Dcbuf = CreateCompatibleDC (Dcw) ;
				OldBuf    = (HBITMAP)SelectObject ( Dcbuf, this->parent->tBuf->DIBhandler);
				
				BitBlt ( Dcw, PosX, PosY, dw,  dh, Dcbuf, sx, sy, SRCCOPY);

				SelectObject ( Dcbuf , OldBuf ) ;
				DeleteDC ( Dcbuf ) ; 
				ReleaseDC (Whwnd, Dcw);
  }

  return 0;
}