#include "dviout.h"

static int old_Lcol = -2;

BOOL LOUPE_OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg);

void InitLoupeWindow( void )
{
#	define	L_INITIALIZED	0x80000
	int color;
	WNDCLASS wc;

	if(old_Lcol == -2){
		g_winData.dviwin.loupe.diam     = RestoreInt( "Settings", "LoupeDiam", 250 );
		g_winData.dviwin.loupe.div      = RestoreInt( "Settings", "LoupeDiv", 1 );
	}
	wc.style         = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc   = LoupeWndProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = g_winData.hInstance;
	wc.hIcon         = NULL;
	wc.hCursor       = NULL;
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = "LOUPE";

	if(b_presentation_mode)
		goto col;
	switch( g_winData.dviwin.loupe.fShape & F_LCOLOR ){
		case F_LWHITE:
			color = 0xffffff;
			break;

		case F_LBLACK:
			color = 0;
			break;

		default:
col:		color = GetBackGround();
			break;

	}
	if(old_Lcol != -2){
		if(old_Lcol == color)
			return;
		DestroyWindow( g_winData.dviwin.loupe.hWnd );
		UnregisterClass( wc.lpszClassName, wc.hInstance);
	}
	old_Lcol = color;
	if(color == 0)
		wc.hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
	else if(color == 0xffffff)
		wc.hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
	else
		wc.hbrBackground = CreateSolidBrush(color);

	if( RegisterClass( &wc ) == 0 )
	{
		MessageBox( NULL, "RegisterClass for LOUPE failed.", NULL, MB_OK );
		goto quit;
	}
	g_winData.dviwin.loupe.hWnd = CreateWindowEx( WS_EX_TOPMOST,
					"LOUPE",
					NULL,
					WS_BORDER | WS_POPUP,
					0, 0,                   // position
					g_winData.dviwin.loupe.diam, g_winData.dviwin.loupe.diam,
					g_winData.hMainWnd,
					NULL, //(HMENU)ID_LOUPEWINDOW,
					g_winData.hInstance,
					NULL );
	if( g_winData.dviwin.loupe.hWnd == NULL )
	{
		MessageBox( g_winData.hMainWnd, 
			"Creation Loupe Window Failed.", NULL, MB_OK );
		exit( 1 );
	}
	// ShowWindow( g_winData.hLoupeWnd, SW_SHOWNORMAL );
	// UpdateWindow( g_winData.dviwin.hWnd );
quit:
	if( color != 0 && color != 0xffffff)
		DeleteObject(wc.hbrBackground);
	return;
}

void NotifyDviinfo(DVI_WINDOW_PROPERTY *w)
{
	/* Do nothing now because LOUPE is now a member of DVI_WINDOW_PROPERTY. */
/*	g_winData.dviwin.loupe.w = w; */
}

LRESULT CALLBACK LoupeWndProc(
				HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
	switch( message )
	{
		PAINTSTRUCT ps;
		HDC hdc;

		POINT pt, pt2;
		static BYTE *pDib;
		static int f_busy;
		BYTE* pDibBits;
		int	l_size, x_off, y_off;
		short cxDib;    /* DIB の幅 */
		short cyDib;    /* DIB の高さ */
		int count;
		BYTE *pBMP, *pBMPBits;
		int x_pos, y_pos, x_size, y_size, x_org, y_org, type, f_and;
		int lx_pos, ly_pos;
		HBRUSH hbrBackground;
		RECT rc;
		int f_disp;
		HPEN aPen, oldPen;
#ifdef	DOUBLE_PAGE
		int xshift, yshift;
		int psub;
		int f_left;
#endif

		case WM_CREATE:
			g_winData.dviwin.loupe.hWnd = hwnd;
			break;
		
		case WM_PAINT:
			if(g_winData.dviwin.loupe.bDrawing || f_busy)
				break;
			f_busy = 1;
#ifdef	DOUBLE_PAGE
			f_disp = f_left = xshift = yshift = 0;
#endif
			hdc = BeginPaint( hwnd, &ps );
			/* Here please impliment the code which views the DVI output.
			   the coordinate of origin is (0, 0) and right-bottom edge
			   is (g_winData.dviwin.loupe.diam, g_winData.dviwin.loupe.diam). these value will never
			   be distinguished between two modes.
			   I remark here that mouse cursor always points the 
			   center of the window.
			   It is probably necessary that where mouse cursor is.
			   This value can be got GetCursorPos(&pt); where pt means
			   POINT structure. But I remark that pt means SCREEN coordinate.
			   so please change it by ScreenToClient( g_winData.dviwin.hWnd ); or
			   call IsMouseInDVIwindow function. It returns Boolean value
			   but set client coordinate into pt. */

			GetCursorPos(&pt);
			ScreenToClient( g_winData.dviwin.hWnd, &pt );
			pt = GetDVICoordFromClient( &g_winData.dviwin, pt.x+1, pt.y+1 );
			SetStretchBltMode( hdc, COLORONCOLOR ); /* 総て描画し直し */
			if(pDib != NULL)
				free_bmp(pDib - sizeof(BITMAPFILEHEADER));
			l_size = g_winData.dviwin.loupe.diam*g_winData.dviwin.loupe.div;

again:
#ifdef	DOUBLE_PAGE
			psub = (!f_2page || xshift || yshift)?0:1;
			if(IS_2DIB2)
				psub++;
			if(IS_2REV)
				psub++;
			psub &= 1;
			pt.x -= xshift;
			pt.y -= yshift;
			if(psub != 0 && p2Page == NULL)
				goto end_page;
			f_left++;
#endif
			x_off = l_size/2 - pt.x;
			y_off = l_size/2 - pt.y;
/*
 *	(x_off, y_off): left-upper corner of Loupe
 *  				coordinate is in PIXEL in Text
 */
			if(b_presentation_mode && g_winData.dviwin.cover_sheet.fOn == CS_ON && !f_2page){
				if(old_Lcol == 0)
					hbrBackground = (HBRUSH)GetStockObject( BLACK_BRUSH );
				else if(old_Lcol == 0xffffff)
					hbrBackground = (HBRUSH)GetStockObject( WHITE_BRUSH );
				else
					hbrBackground = CreateSolidBrush(old_Lcol);

				if(g_winData.dviwin.cover_sheet.fPlace & CS_RIGHT){
					rc.left = (x_off + GetCoverPos().x)/g_winData.dviwin.loupe.div;
					rc.right = (x_off + GetTextXSize())/g_winData.dviwin.loupe.div + 1;
				}else{
					rc.left = x_off/g_winData.dviwin.loupe.div;
					rc.right = 
						(GetTextXSize() - GetCoverPos().x + x_off)/g_winData.dviwin.loupe.div + 1;
				}
				rc.top = (y_off + GetCoverPos().y)/g_winData.dviwin.loupe.div;
				rc.bottom = (y_off + GetTextYSize())/g_winData.dviwin.loupe.div + 1;
				if(rc.top < 0)
					rc.top = 0;
				if(rc.left < 0)
					rc.left = 0;
				if(rc.right > g_winData.dviwin.loupe.diam)
					rc.right = g_winData.dviwin.loupe.diam;
				if(rc.bottom > g_winData.dviwin.loupe.diam)
					rc.bottom = g_winData.dviwin.loupe.diam;

				FillRect(hdc, &rc, hbrBackground);
				ExcludeClipRect(hdc, rc.left, rc.top, rc.right, rc.bottom);
				if(old_Lcol != 0 && old_Lcol != 0xffffff)
					DeleteObject(hbrBackground);
			}
			if(x_off < 0)
				x_off = 0;
			if(y_off < 0)
				y_off = 0;

			if(b_presentation_mode && f_background){
				if(f_background & 1)
					hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
				else
					hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
			}else
				hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE + 1);

			if((pDib = GetWindowBMP(
					pt.x - l_size/2 + x_off, pt.y - l_size/2 + y_off,
					l_size - x_off, l_size - y_off,
					g_winData.dviwin.loupe.div, g_winData.dviwin.loupe.div, 
#ifdef	DOUBLE_PAGE
					(psub)?p2Page:
#endif
					NULL)) == NULL){
				if(IS_2VERT)
					cyDib = 0;
				else
					cxDib = 0;
				goto end_page;
			}
#ifdef	DOUBLE_PAGE
			f_disp |= (xshift|yshift)?2:1;
#else
			f_disp = 1;
#endif
			pDib += sizeof(BITMAPFILEHEADER);
			pDibBits = GetDibBitsAddress( pDib );
			cxDib    = GetDibWidth( pDib );
			cyDib    = GetDibHeight( pDib );
			SetDIBitsToDevice( hdc,
				x_off /= g_winData.dviwin.loupe.div, y_off /= g_winData.dviwin.loupe.div, 
				cxDib, cyDib,
				0, 0, 
				0, cyDib,
				(LPSTR)pDibBits, (LPBITMAPINFO)pDib, DIB_RGB_COLORS );
#ifdef	DOUBLE_PAGE
			for(count = (psub||!f_2page||!p2Page)?0:last_bmp; ; )
#else
			for(count = 0; ; )
#endif
			{
				type = 0;
				pBMP = (BYTE *)GetBMPdata(count++, &x_pos, &y_pos, 
					&x_size, &y_size, &type);
				if(pBMP == NULL
#ifdef	DOUBLE_PAGE
				 || (f_2page && psub && count > last_bmp) 
#endif
				)
					break;
				lx_pos = l_size/2 - pt.x + x_pos;
				ly_pos = l_size/2 - pt.y + y_pos;
				if(  lx_pos + x_size <= 0 || lx_pos >= l_size
				  || ly_pos + y_size <= 0 || ly_pos >= l_size)
					continue;
#ifdef	WMF
				if(type == F_WMF || type == F_EMF){
					DisplayMetaFile(hdc, pBMP,
						lx_pos/g_winData.dviwin.loupe.div, ly_pos/g_winData.dviwin.loupe.div,
						x_size/g_winData.dviwin.loupe.div, y_size/g_winData.dviwin.loupe.div, type);
					continue;
				}
#endif
				pBMPBits = GetDibBitsAddress( pBMP );
				x_org   = GetDibWidth( pBMP );
				y_org   = GetDibHeight( pBMP );
				if(type == 1)
					f_and = SRCCOPY;
				else if(SetGamma(0)>=0)
					f_and = (type > -3)?SRCAND:SRCPAINT;
				else
					f_and = (type > -3)?SRCPAINT:SRCAND;
				if(type == -4){
					type--;
					if( !ChangeColor( pBMP, (SetGamma(0)>=0)?1:0 ) ){
						type--;
						goto skp;
					}
				}
rep:			StretchDIBits( hdc,
					lx_pos/g_winData.dviwin.loupe.div, ly_pos/g_winData.dviwin.loupe.div, 
					(x_size + g_winData.dviwin.loupe.div - 1)/g_winData.dviwin.loupe.div,
					(y_size + g_winData.dviwin.loupe.div - 1)/g_winData.dviwin.loupe.div,
					0, 0,
					x_org, y_org,
					(LPSTR)pBMPBits, (LPBITMAPINFO)pBMP, DIB_RGB_COLORS, f_and);  
				if(type-- == -5){
					ChangeColor( pBMP, -1 );
skp:    	        f_and = (f_and == SRCPAINT)?SRCAND:SRCPAINT;
					goto rep;
				}
			}
end_page:
#ifdef	DOUBLE_PAGE
			if(f_2page && !xshift && !yshift){
				if(IS_2VERT){
					yshift = g_winData.dviwin.loupe.div;
					if(f_left)
						yshift += GetTextYSize();
				}else{
					xshift = g_winData.dviwin.loupe.div;
					if(f_left)
						xshift += GetTextXSize();
				}
				goto again;
			}
#endif
			if(!f_disp){
				rc.left = rc.top = 0;
				rc.right = rc.bottom = g_winData.dviwin.loupe.diam;
				FillRect(hdc, &rc, hbrBackground);
				goto skip;
			}
#ifdef	DOUBLE_PAGE
			if(f_2page){
				if(IS_2VERT){
					y_off = (l_size/2 - pt.y - yshift)/g_winData.dviwin.loupe.div;
					cyDib = GetTextYSize();
					count = y_off + cyDib/g_winData.dviwin.loupe.div + 1;
					if(p2Page){
						MoveToEx( hdc, 0, count, &pt2 );
						LineTo( hdc, g_winData.dviwin.loupe.diam, count );
						cyDib += cyDib + 1;
					}
					cyDib /= g_winData.dviwin.loupe.div;
				}else{
					x_off = (l_size/2 - pt.x - xshift)/g_winData.dviwin.loupe.div;
					cxDib = GetTextXSize();
					count = x_off + cxDib/g_winData.dviwin.loupe.div + 1;
					if(p2Page){
						MoveToEx( hdc, count, 0, &pt2 );
						LineTo( hdc, count, g_winData.dviwin.loupe.diam );
						cxDib += cxDib + 1;
					}
					cxDib /= g_winData.dviwin.loupe.div;
				}
			}
#endif
			if(x_off > 0){
				rc.left = rc.top = 0;
				rc.right = x_off;
				rc.bottom = g_winData.dviwin.loupe.diam;
				FillRect(hdc, &rc, hbrBackground);
			}
			if(y_off > 0){
				rc.left = rc.top = 0;
				rc.right = g_winData.dviwin.loupe.diam;
				rc.bottom = y_off;
				FillRect(hdc, &rc, hbrBackground);
			}
			if(x_off + cxDib < g_winData.dviwin.loupe.diam){
				rc.left = x_off + cxDib;
				rc.top = 0;
				rc.right = rc.bottom = g_winData.dviwin.loupe.diam;
				FillRect(hdc, &rc, hbrBackground);	// bug for IS_2VERT
			}
			if(y_off + cyDib < g_winData.dviwin.loupe.diam){
				rc.left = 0;
				rc.top = y_off + cyDib;
				rc.right = rc.bottom = g_winData.dviwin.loupe.diam;
				FillRect(hdc, &rc, hbrBackground);
			}
			if( g_winData.dviwin.loupe.fShape & F_LCIRCLE )
			{
				if(!old_Lcol){
					aPen = CreatePen(PS_SOLID, 1, 0xffffff);
					oldPen = SelectObject(hdc, aPen);
				}
				/* if circle mode, we have to draw the border circle */
//				Ellipse( hdc, -1, -1, g_winData.dviwin.loupe.diam - 2, g_winData.dviwin.loupe.diam - 2 );
				Arc( hdc, -1, -1, g_winData.dviwin.loupe.diam - 2, g_winData.dviwin.loupe.diam - 2,
						-1, g_winData.dviwin.loupe.diam/2 - 1,
						-1, g_winData.dviwin.loupe.diam/2 - 1 );
				goto end_pen;
			}else if( (g_winData.dviwin.loupe.fShape & F_LMEASURE) || !old_Lcol){
				int i, j, k;

				if(!old_Lcol){
					aPen = CreatePen(PS_SOLID, 1, 0xffffff);
					oldPen = SelectObject(hdc, aPen);
					MoveToEx(hdc, 0, 0, &pt2);
					LineTo(hdc, 0, g_winData.dviwin.loupe.diam-3);
					LineTo(hdc, g_winData.dviwin.loupe.diam-3, g_winData.dviwin.loupe.diam-3);
					LineTo(hdc, g_winData.dviwin.loupe.diam-3, 0);
					LineTo(hdc, 0, 0);
				}
#define	INTERVAL	4
				if(g_winData.dviwin.loupe.fShape & F_LMEASURE){
					for( i = k = 0, count = INTERVAL; 
						count < g_winData.dviwin.loupe.diam; count += INTERVAL){
						if(++i == 5)
							j = 7;
						else if(i == 10){
							i = 0;
							if(++k == 5){
								k = 0;
								j = 15;
							}
							else j = 10;
						}else
							j = 4;
						MoveToEx( hdc, 0, count, &pt2 );
						LineTo( hdc, j, count);
						MoveToEx( hdc, g_winData.dviwin.loupe.diam-j-3, count, &pt2 );
						LineTo( hdc, g_winData.dviwin.loupe.diam, count);
						MoveToEx( hdc, count, 0, &pt2 );
						LineTo( hdc, count, j );
						MoveToEx( hdc, count, g_winData.dviwin.loupe.diam-j-3, &pt2 );
						LineTo( hdc, count, g_winData.dviwin.loupe.diam );
					}
				}
end_pen:		if(!old_Lcol){
					SelectObject(hdc, oldPen);
					DeleteObject(aPen);
				}
			}
skip:		EndPaint( hwnd, &ps );
			f_busy = 0;
			return FALSE;
		
		HANDLE_MSG( hwnd, WM_SETCURSOR, LOUPE_OnSetCursor );
		default:
			break;
	}
	return DefWindowProc( hwnd, message, wParam, lParam );
}

BOOL LOUPE_OnSetCursor(HWND hwnd, HWND hwndCursor, UINT codeHitTest, UINT msg)
{
	/* TRUE halts further processing. FALSE to continue. */
	if( g_winData.dviwin.loupe.bKillCursor ){
		SetCursor(g_winData.pCursor[CS_HIDE]);
		return TRUE; // stop processing by a child window.
	}
	return FALSE; /* continue processing (by a child window) */
}

void SetLoupeRect( void )
{
	g_winData.dviwin.loupe.fShape &= ~F_LCIRCLE;
	SetWindowRgn( g_winData.dviwin.loupe.hWnd, NULL, TRUE );
	return;
}

void SetLoupeCircle( void )
{
	HRGN hrgn;
	RECT rc;

	g_winData.dviwin.loupe.fShape |= F_LCIRCLE;
	GetClientRect( g_winData.dviwin.loupe.hWnd, &rc );
	hrgn = CreateEllipticRgn( 0, 0, g_winData.dviwin.loupe.diam, g_winData.dviwin.loupe.diam );
	SetWindowRgn( g_winData.dviwin.loupe.hWnd, hrgn, TRUE );
	return;
}
