#include "global.h"
#include "av.h"
#include "clipbrd.h"
#include "dd.h"
#include "edit.h"
#include "file.h"
#include "find.h"
#include "fontsel.h"
#include "icon.h"
#include "menu.h"
#include "makro.h"
#include "olga.h"
#include "options.h"
#include "projekt.h"
#include "rsc.h"
#include "se.h"
#include "set.h"
#include "string.h"
#include "text.h"
#include "window.h"
#include "event.h"

GLOBAL WORD	abortProg = FALSE;		/* Falls TRUE -> sofort Ende */

/****** DEFINES **************************************************************/

#ifndef WM_BOTTOMED
#define WM_BOTTOMED			33
#endif

#ifndef WM_ICONIFY
#define WM_ICONIFY			34
#define WM_UNICONIFY			35
#define WM_ALLICONIFY		36
#endif

#ifndef WM_SHADED
#define WM_SHADED				0x5758
#define WM_UNSHADED			0x5759
#endif

#ifndef SH_WDRAW
#define SH_WDRAW				72
#endif

#ifndef DHST_ACK 
#define DHST_ACK				0xDADE
#endif

#ifndef FONT_CHANGED
#define FONT_CHANGED 		0x7A18
#endif

#define MAX_EVENT 20

/****** TYPES ****************************************************************/

typedef struct
{
	WORD			event;
	MOUSEDATA	mouse;
	KEYDATA		key;
	WORD			msg[8];
}qedEVENT;

/****** VARIABLES ************************************************************/

LOCAL qedEVENT	msg_queue[MAX_EVENT];
LOCAL WORD		msg_head = 0,
					msg_tail = 0;
LOCAL WORD		old_mx = 0,
					old_my = 0; 	/* Immer die letzte Mausposition */


/****** FUNCTIONS ************************************************************/

LOCAL VOID hndl_keybd	(KEYDATA *key);
LOCAL VOID hndl_button 	(MOUSEDATA *mouse);
LOCAL VOID hndl_msg 		(WORD *msg, KEYDATA *key);

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

GLOBAL WORD qed_evnt_multi(WORD evtypes,
									WORD nclicks, WORD bmask, WORD bstate,
									WORD flg1_leave, WORD x1, WORD y1, WORD w1, WORD h1,
									WORD flg2_leave, WORD x2, WORD y2, WORD w2, WORD h2,
									WORD *msgbuf,
									ULONG ms,
									MOUSEDATA *mouse,
									KEYDATA *key)
{
	GRECT	r1, r2;
	WORD	ev;
			
	r1.g_x = x1; r1.g_y = y1; r1.g_w = w1; r1.g_h = h1;
	r2.g_x = x2; r2.g_y = y2; r2.g_w = w2; r2.g_h = h2;

	ev = evnt_multi(evtypes, nclicks, bmask, bstate,
							flg1_leave, &r1,
							flg2_leave, &r2,
							msgbuf,
							ms,
							(EVNTDATA*)mouse, &key->kreturn, &mouse->breturn);
	gem2key(mouse->kstate, key->kreturn, key);
	old_mx = mouse->x;
	old_my = mouse->y;
	return ev;
}


LOCAL BOOLEAN is_event(VOID)
{
	return (msg_head != msg_tail);
}

LOCAL BOOLEAN full_event(VOID)
{
	WORD next;

	next = msg_head+1;
	if (next == MAX_EVENT)
		next = 0;
	return(next == msg_tail);
}

LOCAL VOID add_event(qedEVENT *event)
{
	WORD next;

	if (event->event==MU_KEYBD && is_event())			/* Tastatur-Repeat? */
	{
		qedEVENT	*ptr = &msg_queue[msg_tail];

		if (ptr->event == MU_KEYBD && ptr->key.kreturn == event->key.kreturn &&
		    ptr->key.kstate == event->key.kstate)
			return;
	}

	next = msg_head + 1;
	if (next == MAX_EVENT)
		next = 0;
	if (next == msg_tail)
	{
		fnote(1,FATALERR,10);
		return;
	}
	msg_queue[msg_head] = *event;
	msg_head = next;
}

LOCAL BOOLEAN get_event(qedEVENT *event)
{
	if (msg_head != msg_tail)
	{
		*event = msg_queue[msg_tail];
		msg_tail++;
		if (msg_tail == MAX_EVENT) 
			msg_tail = 0;
		return TRUE;
	}
	return FALSE;
}

LOCAL BOOLEAN idle(VOID)
{
	qedEVENT ev;
	WORD		events;

	if (full_event())
		return FALSE;

	events = MU_KEYBD | MU_BUTTON | MU_MESAG | MU_TIMER;
	if (mouse_sleeps())
		events |= MU_M1;
	ev.event = qed_evnt_multi(events,
										0x102, 3, 0,
										1, old_mx, old_my, 1, 1,
										0, 0, 0, 0, 0,
										ev.msg,
										0L,
										&ev.mouse,
										&ev.key);

	if (ev.event != MU_TIMER)
	{
		ev.event &= (~MU_TIMER);
		if (ev.event == MU_M1)
			wake_mouse();
		else
			add_event(&ev);
		return TRUE;
	}
	return FALSE;
}

GLOBAL BOOLEAN abbruch (VOID)
{
	qedEVENT ev;
	WORD		events;

	if (full_event())
		events = MU_KEYBD | MU_TIMER;
	else
	{
		events = MU_KEYBD | MU_BUTTON | MU_MESAG | MU_TIMER;
		if (mouse_sleeps())
			events |= MU_M1;
	}
	ev.event = qed_evnt_multi(events,
									  0x102, 3, 0,
									  1, old_mx, old_my, 1, 1,
									  0, 0, 0, 0, 0,
									  ev.msg,
									  0,
									  &ev.mouse,
									  &ev.key);

	if (ev.event != MU_TIMER)
	{
		ev.event &= (~MU_TIMER);
		if (ev.event == MU_M1)
		{
			wake_mouse();
			return FALSE;
		}
		if (ev.event & MU_KEYBD)					/* andere Tasten schlucken */
			return (ev.key.normkey & NK_ESC);
		add_event(&ev);
	}
	return FALSE;
}

LOCAL VOID next_action(qedEVENT *ev)
{
	WORD	events;

again:
	if (get_event(ev))									/* Event aus der Schlange */
	{
		old_mx = ev->mouse.x;
		old_my = ev->mouse.y;
		if (makro_play)
		{
			if (ev->event != MU_KEYBD)
			{
				end_play();
				return;
			}
			else if (ev->key.normkey | NK_ESC)
			{
				end_play();
				goto again;
			}
		}
		else
			return;
	}
	if (pre_makro(&ev->key)) 							/* Event vom Makro */
	{
		ev->event = MU_KEYBD;
		return;
	}
																/* auf Event warten */
	events = MU_KEYBD | MU_BUTTON | MU_MESAG | MU_TIMER;

	if (mouse_sleeps())
		events |= MU_M1;

	if (winlist_top() == NULL)
		events &= (~MU_TIMER);

	ev->event = qed_evnt_multi(events,
										0x102, 3, 0,
										1, old_mx, old_my, 1, 1,
										0, 0, 0, 0, 0,
										ev->msg,
										TIMER_INTERVALL,
										&ev->mouse,
										&ev->key);
}

/*****************************************************************************/
/* Ereignis-Verarbeitung																										 */
/*****************************************************************************/

LOCAL VOID hndl_keybd (KEYDATA *key)
{
	WINDOWP window;

	if (debug)
	{
/*
		TEXTP	t_ptr;
		
		window = winlist_top();
		if (window != NULL)
			t_ptr = get_text(window->handle);
*/
		
		if (key->normkey == (NKF_FUNC|NKF_CTRL|NK_F1))
		{
			Debug("Debug-Special: Ctrl-F1\n");
			wake_mouse();

			return;
		}
		if (key->normkey == (NKF_FUNC|NKF_CTRL|NK_F2))
		{
			Debug("Debug-Special: Ctrl-F2\n");
			wake_mouse();

			return;
		}
		if (key->normkey == (NKF_FUNC|NKF_CTRL|NK_F3))
		{
			Debug("Debug-Special: Ctrl-F3\n");
			wake_mouse();

			return;
		}
	}

	post_makro(key);
	if (!makro_play)
		sleep_mouse();

	window = winlist_top();
	if (window == NULL)
		key_global(key);
	else
	{
		if (!key_window(window, key))
			key_global(key);
	}
}

/*****************************************************************************/
LOCAL VOID hndl_button (MOUSEDATA *mouse)
{
	WORD		wh;
	WINDOWP	window;

	if (!all_iconified)
	{
		wake_mouse();
		wh = wind_find(mouse->x, mouse->y);
		window = get_window(wh);
		if (window != NULL)
			click_window(window, mouse);
	}
}

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

LOCAL VOID hndl_msg (WORD *msg, KEYDATA *key)
{
	WINDOWP	window;
	UBYTE		*str_p;

	wake_mouse();
	window= get_window(msg[3]);			/* Zugehriges Fenster */

	switch (msg[0])							/* Art der Nachricht */
	{
		case MN_SELECTED:
			if (makro_rec)						/* Makro wird Tastendruck vorgegaukelt */
			{
				KEYDATA	l_mk;

				str_p = (UBYTE *)get_obspec(menu, msg[4]);
				if (str_to_key(str_p ,&l_mk))
				{
					l_mk.kstate |= (key->kstate & 3);
					post_makro(&l_mk);
				}
			}
			hndl_menu (msg[3], msg[4], key->ctrl);
			break;
		case WM_REDRAW  :
			redraw_window (window, (GRECT*)(msg+4));
			break;
		case WM_CLOSED  :
			do_icon(window->handle, DO_DELETE);
			break;
		case WM_FULLED  :
			full_window (window);
			break;
		case WM_ARROWED :
			arrow_window (window, msg[4], 1);
			break;
		case WM_HSLID	 :
			h_slider (window, msg[4]);
			break;
		case WM_VSLID	 :
			v_slider (window, msg[4]);
			break;
		case WM_SIZED	 :
			size_window (window, (GRECT*)(msg+4), TRUE);
			break;
		case WM_MOVED	 :
			move_window (window, (GRECT*)(msg+4));
			break;
		case WM_NEWTOP	 : /* Fenster von qed ist irgendwie nach oben gekommen */
		case WM_ONTOP	 :
			ontop_window(window);
			break;
		case WM_TOPPED  :
			top_window (window);
			break;
		case WM_UNTOPPED: /* qed hat jetzt nicht mehr das Top-Fenster */
			untop_window (window);
			break;
		case WM_BOTTOMED: 	/* AES 4.00 MagiC 3 : Fenster nach hinten */
		case WM_M_BDROPPED :	/* Magic 2 */
			bottom_window (window, msg[0]);
			break;
		case WM_ICONIFY:
			iconify_window(window, (GRECT*)(msg+4));
			break;
		case WM_ALLICONIFY:
			all_iconify((GRECT*)(msg+4));
			break;
		case WM_UNICONIFY:
			if (all_iconified)
				all_uniconify((GRECT*)(msg+4));
			else
				uniconify_window(window, (GRECT*)(msg+4));
			break;
		case WM_SHADED :
		case WM_UNSHADED :
			shade_window(window, msg[0]);
			break;

		case AP_TERM:
			if (all_iconified)
				all_uniconify(NULL);
			quick_close = TRUE;
			if (prepare_quit())
				do_quit();
			break;
		case AP_DRAGDROP :
			if (all_iconified)
				all_uniconify(NULL);
			hndl_dd(msg);
			break;

		case SC_CHANGED :
			/*
			 * Ignorieren, da wir vor dem Paste sowieso neu laden.
			*/
			break;

		case VA_START :
		case VA_PROTOSTATUS :
		case VA_DRAG_COMPLETE :
		case VA_DRAGACCWIND :
			hndl_av(msg);
			break;

		case SE_INIT:
		case SE_OK:
		case SE_ACK:
		case SE_OPEN:
		case SE_ERROR:
		case SE_ERRFILE:
		case SE_PROJECT:
		case SE_QUIT:
		case SE_TERMINATE:
		case SE_CLOSE :
		case SE_MENU :
			if (all_iconified)
				all_uniconify(NULL);
		   hndl_se(msg);
		   break;

		case OLGA_INIT :
		case OLE_NEW :
		case OLGA_ACK :
		case OLE_EXIT :
			hndl_OLGA(msg);
			break;

		case SH_WDRAW:				/* schickt Freedom stndig */
		case DHST_ACK:				/* SMU antwortet */
			/* ignore */
			break;

		case FONT_CHANGED :
			if (msg[4] != 0)
				winFont.id = msg[4];
			if (msg[5] != 0)
				winFont.pts = msg[5];
			font_change();	
			break;
			
		default:
			if (debug)
			{
				UBYTE	str[12];
				WORD	d, i, id;
	
				if ((appl_xgetinfo(4, &d, &d, &i, &d)) && (i == 1))	/* gibts appl_search? */
				{
					i = appl_search(0, str, &d, &id);
					while (i != 0)
					{
						if (id == msg[1])
							break;
						i = appl_search( 1, str, &d, &id);
					}
				}
				else
				{
					strcpy(str, "");
					id = msg[1];
				}
				Debug("qed: Unbekannte Msg %d (0x%X) von %s (%d)\n", msg[0], msg[0], str, id);
			}
			break;
	}
}

/*****************************************************************************/
GLOBAL VOID hndl_events (VOID)
{
	qedEVENT	ev;
	BOOLEAN	menu_chg;
	LONG		as_timer = 0, t;
	
	updt_menu();
	menu_chg = FALSE;
	onblink_edit();
	do
	{
		quick_close = FALSE; 						/* Sichern der Texte ohne Nachfrage */
		next_action (&ev);
		wind_update(BEG_UPDATE);
		get_realtop();
		global_shift = ev.key.shift;

		if (ev.event == MU_TIMER) 					/* Zeit (nur wenn nichts anderes anliegt) */
		{
			timer_se();
			if (blinking_cursor)
				blink_edit();
			if (clip_on_disk)
				save_clip();

			if (as_text || as_prj)
			{
				/*
				 * Weil der do_all_icon() recht viel Rechenzeit verheizt, und
				 * AutoSave min. 1 min wartet, wird er nur jede Minute aufgerufen.
				*/
				t = Tgettime();
				if ((t - as_timer) >= 32)
				{
					do_all_icon(ALL_TYPES, DO_AUTOSAVE);
					as_timer = t;
				}
			}
		}
		if (ev.event & MU_KEYBD)					/* Taste */
		{
			offblink_edit();
			hndl_keybd(&ev.key);
			menu_chg = TRUE;
			onblink_edit();
			while (idle()) 							/*	Auch fr Makro-Play-Abbruch */
				;
		}
		if (ev.event & MU_BUTTON)					/* Mausknopf */
		{
			offblink_edit();
			hndl_button(&ev.mouse);
			menu_chg = TRUE;
			onblink_edit();
		}
		if (ev.event & MU_MESAG)					/* Meldung */
		{
			offblink_edit();
			hndl_msg(ev.msg, &ev.key);	
			menu_chg = TRUE;
			onblink_edit();
		}
		if (ev.event & MU_M1)						/* Maus bewegt */
			wake_mouse();

		if (menu_chg && !is_event())				/* Wenn Zeit */
		{
			updt_menu();								/* Eine Aktion kann Mens verndern */
			menu_chg = FALSE;
		}
		wind_update(END_UPDATE);
		end_undo_seq();
		if (abortProg)
		{
			WORD	msg[] = {0,0,0,0,0,0,0,0};

			msg[0] = AP_TERM;
			msg[1] = gl_apid;
			appl_write(gl_apid, 16, msg);
		}
	} while (! done);
}
