#include <windows.h>
#include "floatwnd.h"
#include "root.h"

static LRESULT CALLBACK FloatWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );


static BOOL b_regist_wnd_class = FALSE;
static HWND hWndFloat = NULL;
static BOOL b_keep_floating = FALSE;
static HWND hWndChild = NULL;
extern BOOL b_presentation_mode;

static int cx_floatwnd = 160;
static int cy_floatwnd = 120;

enum{
	NO_SHOWN,
	FLOATING,
	STOPPING,
	SINKING
};

static int ANIME_TCK = 20; // a number which can divide 1000.
static int DISPLAY_MILSEC = 2000;

static int tck;
static int showing_state = NO_SHOWN;

static void GetDesktopSize( int* cx, int* cy );
static void GetWindowSize( int* cx, int* cy );
static void DestroyChildWindow( void );

static BOOL destroy_after_sink = FALSE;
static int** p_make_null_after_sink = NULL;

static HWND CreateFloatingWindow( void )
{
	int cx_screen, cy_screen;
	int cx, cy;
	int pos_x, pos_y;

	if( !b_regist_wnd_class ){
		WNDCLASSEX wndclass;
	
		wndclass.cbSize = sizeof(WNDCLASSEX);
		wndclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
		wndclass.lpfnWndProc = FloatWndProc;
		wndclass.cbClsExtra = 0;
		wndclass.cbWndExtra = 0;
		wndclass.hInstance = g_winData.hInstance;
		wndclass.hIcon = NULL;
		wndclass.hCursor = g_winData.pCursor[CS_NORMAL];
		wndclass.hbrBackground = GetStockObject(WHITE_BRUSH);
		wndclass.lpszMenuName = NULL;
		wndclass.lpszClassName = "DVIOUT_FLOATWND";
		wndclass.hIconSm = NULL;
	
		if( !RegisterClassEx(&wndclass) ) return NULL;
		b_regist_wnd_class = TRUE;
	}

	GetDesktopSize( &cx_screen, &cy_screen );

	cx = cx_floatwnd;
	cy = cy_floatwnd;
	pos_x = cx_screen - cx;
	pos_y = cy_screen;

	if( !hWndFloat ){
		hWndFloat = CreateWindowEx( WS_EX_NOACTIVATE | WS_EX_WINDOWEDGE,
			"DVIOUT_FLOATWND", "DVIOUT Floating Window",
			WS_POPUP | WS_VISIBLE | WS_THICKFRAME, pos_x, pos_y, cx, cy,
			g_winData.hMainWnd,
			NULL, g_winData.hInstance, NULL );
	}
	if( !hWndFloat ) return NULL;
	ShowWindow( hWndFloat, SW_SHOWNORMAL );
	UpdateWindow( hWndFloat );
	showing_state = NO_SHOWN;
	destroy_after_sink = FALSE;
	p_make_null_after_sink = NULL;
	return hWndFloat;
}

static void GetDesktopSize( int* cx, int* cy )
{
	RECT rect;
	if( b_presentation_mode ){
		*cx = GetSystemMetrics(SM_CXSCREEN);
		*cy = GetSystemMetrics(SM_CYSCREEN);
		return;
	} else{
		SystemParametersInfo( SPI_GETWORKAREA, 0, &rect, 0 );
		*cx = rect.right - rect.left;
		*cy = rect.bottom - rect.top;
	}
	return;
}

static void GetWindowSize( int* cx, int* cy )
{
	RECT rect;
	if( !hWndFloat ){
		cx = cy = 0;
		return;
	}
	GetWindowRect( hWndFloat, &rect );
	*cx = cx_floatwnd = rect.right - rect.left;
	*cy = cy_floatwnd = rect.bottom - rect.top;
	return;
}

void DisplayFloatingWindow( void )
{
	int cx_screen, cy_screen;
	int cx, cy;
	int pos_x, pos_y;
	if( !hWndFloat ) CreateFloatingWindow();

	GetDesktopSize( &cx_screen, &cy_screen );
	GetWindowSize( &cx, &cy );
	
	if( showing_state != NO_SHOWN ){
		KillTimer( hWndFloat, showing_state );
		showing_state = NO_SHOWN;
		tck = 0;
	}
	pos_x = cx_screen - cx;
	pos_y = cy_screen;
	SetWindowPos( hWndFloat, HWND_TOP, pos_x, pos_y, cx, cy, SWP_SHOWWINDOW | SWP_NOACTIVATE );
	if( !SetTimer( hWndFloat, FLOATING, 1000/ANIME_TCK, NULL ) ){
		pos_y -= cy;
		SetWindowPos( hWndFloat, NULL, pos_x, pos_y, 0, 0, SWP_NOSIZE | SWP_NOZORDER );
	}
	InvalidateRect( hWndFloat, NULL, TRUE );
}

void DestroyFloatingWindow( void )
{
	int cx_screen, cy_screen, cx, cy;
	int pos_x, pos_y;

	GetDesktopSize( &cx_screen, &cy_screen );
	GetWindowSize( &cx, &cy );

	if( showing_state != NO_SHOWN ){
		KillTimer( hWndFloat, showing_state );
		showing_state = NO_SHOWN;
		pos_x = cx_screen - cx;
		pos_y = cy_screen;
		SetWindowPos( hWndFloat, NULL, pos_x, pos_y, 0, 0, SWP_NOSIZE | SWP_NOZORDER );
		tck = 0;
	}

	if( hWndChild ) DestroyChildWindow();
	if( !hWndFloat ){
		if( b_regist_wnd_class ){
/*
			UnregisterClass( "DVIOUT_FLOATWND", g_winData.hInstance );
			b_regist_wnd_class = FALSE;
*/
		}
	} else{
		DestroyWindow( hWndFloat );
/*
		UnregisterClass( "DVIOUT_FLOATWND", g_winData.hInstance );
		b_regist_wnd_class = FALSE;
*/		
		hWndFloat = NULL;

	}
}

BOOL KeepFloatingWindow( BOOL fKeep )
{
	int cx_screen, cy_screen, cx, cy;

	GetDesktopSize( &cx_screen, &cy_screen );
	GetWindowSize( &cx, &cy );

	b_keep_floating = fKeep;
	if( fKeep ){
		if( showing_state == SINKING ){
			KillTimer( hWndFloat, SINKING );
			MoveWindow(hWndFloat, cx_screen - cx, cy_screen - cy, cx, cy, TRUE );
			SetTimer( hWndFloat, STOPPING, DISPLAY_MILSEC, NULL );
			showing_state = STOPPING;
		}
	}
	return TRUE;
}



static LRESULT CALLBACK FloatWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;
	int cx_screen, cy_screen, cx, cy;

	switch( uMsg ){
		case WM_CREATE:
		{
			CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
			SetWindowPos( hwnd, HWND_TOP, pcs->x, pcs->y, pcs->cx, pcs->cy, SWP_NOACTIVATE | SWP_SHOWWINDOW );
			tck = 0;
			break;
		}
		case WM_PAINT:
			hdc = BeginPaint( hwnd, &ps );
			GetClientRect( hwnd, &rect );
//			DrawText( hdc, "HELLO!!!", -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER );
			EndPaint( hwnd, &ps );
			if( hWndChild ) UpdateWindow( hWndChild );
			return 0;
		case WM_TIMER:
			if( wParam == FLOATING ){
				ShowWindow( hWndFloat, SW_SHOWNORMAL );
				showing_state = FLOATING;
				tck++;
				GetDesktopSize( &cx_screen, &cy_screen );
				GetWindowSize( &cx, &cy );
				
				MoveWindow( hwnd, cx_screen - cx, cy_screen - tck*cy/ANIME_TCK, cx, cy, TRUE );
				if( tck == ANIME_TCK ){
					KillTimer( hwnd, FLOATING );
					MessageBeep(-1);
					if( DISPLAY_MILSEC <= 0 ){
						SetTimer( hwnd, SINKING, 1000/ANIME_TCK/2, NULL );
						showing_state = SINKING;
					} else{
						SetTimer( hwnd, STOPPING, DISPLAY_MILSEC, NULL );
						showing_state = STOPPING;
					}
				}
			} else if( wParam == STOPPING ){
				if( b_keep_floating ) break;
				KillTimer( hwnd, STOPPING );
				SetTimer( hwnd, SINKING, 1000/ANIME_TCK/2, NULL );
				showing_state = SINKING;
				tck = ANIME_TCK;
			} else if( wParam == SINKING ){
				showing_state = SINKING;
				tck--;
				GetDesktopSize( &cx_screen, &cy_screen );
				GetWindowSize( &cx, &cy );
				MoveWindow( hwnd, cx_screen - cx, cy_screen - tck*cy/ANIME_TCK, cx, cy, TRUE );
				if( tck == 0 ){
					KillTimer( hwnd, SINKING );
					ShowWindow( hWndFloat, SW_HIDE );
					showing_state = NO_SHOWN;
					if( destroy_after_sink ){
						DestroyFloatingWindow();
						*p_make_null_after_sink = NULL;
					}
				}
			}
			return 0;
		case WM_SIZE:
			if( hWndChild ){
				MoveWindow( hWndChild, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE );
			}
			return 0;
		default:
			break;
	}
	return DefWindowProc( hwnd, uMsg, wParam, lParam );
}

static void DestroyChildWindow( void )
{
	DestroyWindow( hWndChild );
	hWndChild = NULL;
}

HWND CreateWindowInFloatWindow( DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName, DWORD dwStyle )
{
	int cx, cy;
	if( hWndChild ) DestroyChildWindow();
	if( !hWndFloat ) CreateFloatingWindow();
	GetWindowSize( &cx, &cy );
	hWndChild = CreateWindowEx( dwExStyle, lpClassName, lpWindowName, dwStyle,
		0, 0, cx, cy, hWndFloat, NULL, g_winData.hInstance, NULL );
	return hWndChild;
}

HWND CreateDialogInFloatWindow( LPCTSTR lpTemplate, DLGPROC lpDialogFunc)
{
	int cx, cy;
	if( hWndChild ) DestroyChildWindow();
	if( !hWndFloat ) CreateFloatingWindow();
	GetWindowSize( &cx, &cy );
	hWndChild = CreateDialog( g_winData.hInstance, lpTemplate, hWndFloat, lpDialogFunc );
	/* Make sure to have it a child window even if it has the "popup" style. */
	SetParent( hWndChild, hWndFloat );
	MoveWindow( hWndChild, 0, 0, cx, cy, TRUE );
	return hWndChild;
}

void DelayedDestroyFloatingWindow( void* p_make_null )
{
	destroy_after_sink = TRUE;
	p_make_null_after_sink = p_make_null;
}

void SetFloatingWindowSize( int cx, int cy )
{
	cx_floatwnd = cx;
	cy_floatwnd = cy;
	if( hWndFloat != NULL ){
		SetWindowPos( hWndFloat, NULL, 0, 0, cx, cy, SWP_NOMOVE | SWP_NOZORDER ) ;
	}
}

void SetFloatingKeepTime( int millsec )
{
	DISPLAY_MILSEC = (millsec < 0)?2000:millsec;
}
