/*
 * Copyright 2010 Ole Loots <ole@monochrom.net>
 *
 * This file is part of NetSurf, http://www.netsurf-browser.org/
 *
 * NetSurf is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * NetSurf is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <limits.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <windom.h>

#include "desktop/gui.h"
#include "desktop/netsurf.h"
#include "desktop/browser.h"
#include "desktop/browser_private.h"
#include "desktop/mouse.h"
#include "desktop/textinput.h"
#include "desktop/hotlist.h"
#include "desktop/save_complete.h"
#include "desktop/options.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/url.h"

#include "atari/gui.h"
#include "atari/browser_win.h"
#include "atari/toolbar.h"
#include "atari/browser.h"
#include "atari/hotlist.h"
#include "atari/history.h"
#include "atari/misc.h"
#include "atari/global_evnt.h"
#include "atari/browser_win.h"
#include "atari/res/netsurf.rsh"
#include "atari/search.h"
#include "atari/findfile.h"
#include "atari/settings.h"
#include "cflib.h"

extern struct gui_window *input_window;
extern OBJECT * 	h_gem_menu;
extern int mouse_click_time[3];
extern int mouse_hold_start[3];
extern browser_mouse_state bmstate;
extern short last_drag_x;
extern short last_drag_y;
extern bool html_redraw_debug;

extern const char * option_homepage_url;
extern int option_window_width;
extern int option_window_height;
extern int option_window_x;
extern int option_window_y;
extern char options[PATH_MAX];

/* Zero based resource tree ids: */
#define T_ABOUT 0
#define T_FILE MAINMENU_T_FILE - MAINMENU_T_FILE + 1
#define T_EDIT MAINMENU_T_EDIT - MAINMENU_T_FILE + 1
#define T_VIEW MAINMENU_T_VIEW - MAINMENU_T_FILE + 1
#define T_NAV	MAINMENU_T_NAVIGATE - MAINMENU_T_FILE + 1
#define T_UTIL MAINMENU_T_UTIL - MAINMENU_T_FILE + 1
#define T_HELP MAINMENU_T_NAVIGATE - MAINMENU_T_FILE + 1
/* Count of the above defines: */
#define NUM_MENU_TITLES 7

/* Global event handlers: */
static void __CDECL global_evnt_apterm( WINDOW * win, short buff[8] );
static void __CDECL global_evnt_menu( WINDOW * win, short buff[8] );
static void __CDECL global_evnt_m1( WINDOW * win, short buff[8] );
static void __CDECL global_evnt_keybd( WINDOW * win, short buff[8],void * data);

/* Menu event handlers: */
static void __CDECL menu_about(WINDOW *win, int item, int title, void *data);


/* Menu event handlers: */
static void __CDECL menu_about(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	char buf[PATH_MAX];
	strcpy((char*)&buf, "file://");
	strncat((char*)&buf, (char*)"./doc/README.TXT", PATH_MAX - (strlen("file://")+1) );
	browser_window_create((char*)&buf, 0, 0, true, false);
}

static void __CDECL menu_new_win(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	browser_window_create(option_homepage_url, 0, 0, true, false);
}

static void __CDECL menu_open_url(WINDOW *win, int item, int title, void *data)
{
	struct gui_window * gw;
	struct browser_window * bw ;
	LOG(("%s", __FUNCTION__));

	gw = input_window;
	if( gw == NULL ) {
		bw = browser_window_create("", 0, 0, true, false);
		gw = bw->window;

	}
	/* Loose focus: */
	window_set_focus( gw, WIDGET_NONE, NULL );

	/* trigger on-focus event (select all text): */
	window_set_focus( gw, URL_WIDGET, &gw->root->toolbar->url );

	/* delete selection: */
	tb_url_input( gw, NK_DEL );
}

static void __CDECL menu_open_file(WINDOW *win, int item, int title, void *data)
{
	struct gui_window * gw;
	struct browser_window * bw ;

	LOG(("%s", __FUNCTION__));

	const char * filename = file_select( messages_get("OpenFile"), "" );
	if( filename != NULL ){
		char * url = local_file_to_url( filename );
		if( url ){
			bw = browser_window_create(url, NULL, NULL, true, false);
			free( url );
		}
	}
}

static void __CDECL menu_close_win(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if( input_window == NULL )
		return;
	gui_window_destroy( input_window );
}

static void __CDECL menu_save_page(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	static bool init = true;
	bool is_folder=false;
	const char * path;

	if( !input_window )
		return;

	if( init ){
		init = false;
		save_complete_init();
	}

	do {
		// TODO: localize string
		path = file_select("Select folder", "");
		if (path)
			is_folder = is_dir(path);
	} while( !is_folder && path != NULL );

	if( path != NULL ){
		save_complete( input_window->browser->bw->current_content, path, NULL );
	}

}

static void __CDECL menu_quit(WINDOW *win, int item, int title, void *data)
{
	short buff[8];
	memset( &buff, 0, sizeof(short)*8 );
	LOG(("%s", __FUNCTION__));
	global_evnt_apterm( NULL, buff  );
}

static void __CDECL menu_cut(WINDOW *win, int item, int title, void *data)
{
	if( input_window != NULL )
		browser_window_key_press( input_window->browser->bw, KEY_CUT_SELECTION);
}

static void __CDECL menu_copy(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if( input_window != NULL )
		browser_window_key_press( input_window->browser->bw, KEY_COPY_SELECTION);
}

static void __CDECL menu_paste(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if( input_window != NULL )
		browser_window_key_press( input_window->browser->bw, KEY_PASTE);
}

static void __CDECL menu_find(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if( input_window != NULL )
		open_browser_search( input_window );
}

static void __CDECL menu_choices(WINDOW *win, int item, int title, void *data)
{
	static WINDOW * settings_dlg = NULL;
	LOG(("%s", __FUNCTION__));
	settings_dlg = open_settings();
}

static void __CDECL menu_stop(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if( input_window == NULL )
		return;
	tb_stop_click( input_window );

}

static void __CDECL menu_reload(WINDOW *win, int item, int title, void *data)
{
	if( input_window == NULL)
		return;
	tb_reload_click( input_window );
	LOG(("%s", __FUNCTION__));
}

static void __CDECL menu_toolbars(WINDOW *win, int item, int title, void *data)
{
	static int state = 0;
	LOG(("%s", __FUNCTION__));
	if( input_window != null && input_window->root->toolbar != null ){
		state = !state;
		tb_hide( input_window, state );
	}
}

static void __CDECL menu_savewin(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if (input_window && input_window->browser) {
		GRECT rect;
		wind_get_grect(input_window->root->handle->handle, WF_CURRXYWH, &rect);
		option_window_width = rect.g_w;
		option_window_height = rect.g_h;
		option_window_x = rect.g_x;
		option_window_y = rect.g_y;
		nsoption_set_int(window_width, rect.g_w);
		nsoption_set_int(window_height, rect.g_h);
		nsoption_set_int(window_x, rect.g_x);
		nsoption_set_int(window_y, rect.g_y);
		nsoption_write((const char*)&options);
	}

}

static void __CDECL menu_debug_render(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	html_redraw_debug = !html_redraw_debug;
	if( input_window != NULL ) {
		if ( input_window->browser != NULL && input_window->browser->bw != NULL) {
			LGRECT rect;
			browser_get_rect( input_window, BR_CONTENT, &rect );
			browser_window_reformat(input_window->browser->bw, false,
									rect.g_w, rect.g_h );
			MenuIcheck(NULL, MAINMENU_M_DEBUG_RENDER,
						(html_redraw_debug) ? 1 : 0 );
		}
	}
}

static void __CDECL menu_fg_images(WINDOW *win, int item, int title, void *data)
{
	nsoption_set_bool(foreground_images, !nsoption_bool(foreground_images));
	MenuIcheck( NULL, MAINMENU_M_FG_IMAGES, (nsoption_bool(foreground_images)) ? 1 : 0);
}

static void __CDECL menu_bg_images(WINDOW *win, int item, int title, void *data)
{
	nsoption_set_bool(background_images, !nsoption_bool(background_images));
	MenuIcheck( NULL, MAINMENU_M_BG_IMAGES, (nsoption_bool(background_images)) ? 1 : 0);
}

static void __CDECL menu_back(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if( input_window == NULL )
		return;
	tb_back_click( input_window );
}

static void __CDECL menu_forward(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if( input_window == NULL )
		return;
	tb_forward_click( input_window );
}

static void __CDECL menu_home(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if( input_window == NULL )
		return;
	tb_home_click( input_window );
}

static void __CDECL menu_lhistory(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if( input_window == NULL )
		return;
}

static void __CDECL menu_ghistory(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	global_history_open();
}

static void __CDECL menu_add_bookmark(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	if( input_window ) {
		if( input_window->browser->bw->current_content != NULL ){
			atari_hotlist_add_page(
				nsurl_access(hlcache_handle_get_url( input_window->browser->bw->current_content)),
				NULL
			);
		}
	}
}

static void __CDECL menu_bookmarks(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	hotlist_open();
}

static void __CDECL menu_vlog(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
	verbose_log = !verbose_log;
	MenuIcheck(NULL, MAINMENU_M_VLOG, (verbose_log) ? 1 : 0 );
}

static void __CDECL menu_help_content(WINDOW *win, int item, int title, void *data)
{
	LOG(("%s", __FUNCTION__));
}

static struct s_menu_item_evnt menu_evnt_tbl[] =
{
	{T_ABOUT,MAINMENU_M_ABOUT, menu_about, {0,0,0}, NULL },
	{T_FILE, MAINMENU_M_NEWWIN, menu_new_win, {0,0,0}, NULL},
	{T_FILE, MAINMENU_M_OPENURL, menu_open_url, {'G',0,K_CTRL}, NULL},
	{T_FILE, MAINMENU_M_OPENFILE, menu_open_file, {'O',0,K_CTRL}, NULL},
	{T_FILE, MAINMENU_M_CLOSEWIN, menu_close_win, {0,0,0}, NULL},
	{T_FILE, MAINMENU_M_SAVEPAGE, menu_save_page, {0,NK_F3,0}, NULL},
	{T_FILE, MAINMENU_M_QUIT, menu_quit, {'Q',0,K_CTRL}, NULL},
	{T_EDIT, MAINMENU_M_CUT, menu_cut, {'X',0,K_CTRL}, NULL},
	{T_EDIT, MAINMENU_M_COPY, menu_copy, {'C',0,K_CTRL}, NULL},
	{T_EDIT, MAINMENU_M_PASTE, menu_paste, {'V',0,K_CTRL}, NULL},
	{T_EDIT, MAINMENU_M_FIND, menu_find, {0,NK_F4,0}, NULL},
	{T_VIEW, MAINMENU_M_RELOAD, menu_reload, {0,NK_F5,0}, NULL},
	{T_VIEW, MAINMENU_M_TOOLBARS, menu_toolbars, {0,NK_F1,K_CTRL}, NULL},
	{T_VIEW, MAINMENU_M_SAVEWIN, menu_savewin, {0,0,0}, NULL},
	{T_VIEW, MAINMENU_M_DEBUG_RENDER, menu_debug_render, {0,0,0}, NULL},
	{T_VIEW, MAINMENU_M_FG_IMAGES, menu_fg_images, {0,0,0}, NULL},
	{T_VIEW, MAINMENU_M_BG_IMAGES, menu_bg_images, {0,0,0}, NULL},
	{T_VIEW, MAINMENU_M_STOP, menu_stop, {0,NK_ESC,K_ALT}, NULL},
	{T_NAV, MAINMENU_M_BACK, menu_back, {0,NK_LEFT,K_ALT}, NULL},
	{T_NAV, MAINMENU_M_FORWARD, menu_forward, {0,NK_RIGHT,K_ALT}, NULL},
	{T_NAV, MAINMENU_M_HOME, menu_home, {0,NK_CLRHOME,0}, NULL},
	{T_UTIL, MAINMENU_M_LHISTORY,menu_lhistory, {0,NK_F7,0}, NULL},
	{T_UTIL, MAINMENU_M_GHISTORY, menu_ghistory, {0,NK_F7,K_CTRL}, NULL},
	{T_UTIL, MAINMENU_M_ADD_BOOKMARK, menu_add_bookmark, {'D',0,K_CTRL}, NULL},
	{T_UTIL, MAINMENU_M_BOOKMARKS, menu_bookmarks, {0,NK_F6,0}, NULL},
	{T_UTIL, MAINMENU_M_CHOICES, menu_choices, {0,0,0}, NULL},
	{T_UTIL, MAINMENU_M_VLOG, menu_vlog, {'V',0,K_ALT}, NULL},
	{T_HELP, MAINMENU_M_HELP_CONTENT, menu_help_content, {0,NK_F1,0}, NULL},
	{T_HELP, -1, NULL,{0,0,0}, NULL }
};

void __CDECL global_evnt_apterm( WINDOW * win, short buff[8] )
{
	int i = 0;
	LOG((""));
	netsurf_quit = true;
}


static void __CDECL global_evnt_m1( WINDOW * win, short buff[8] )
{
	struct gui_window * gw = input_window;
	static bool prev_url = false;
	static short prev_x=0;
	static short prev_y=0;
	bool within = false;
	LGRECT urlbox, bwbox, sbbox;
	int nx, ny;

	if( gw == NULL)
		return;

	if( prev_x == evnt.mx && prev_y == evnt.my ){
		return;
	}

	short ghandle = wind_find( evnt.mx, evnt.my );
	if( input_window->root->handle->handle == ghandle ){

		// The window found at x,y is an gui_window
		// and it's the input window.

		browser_get_rect( gw, BR_CONTENT, &bwbox );

		if( evnt.mx > bwbox.g_x && evnt.mx < bwbox.g_x + bwbox.g_w &&
			evnt.my > bwbox.g_y &&  evnt.my < bwbox.g_y + bwbox.g_h ){
			within = true;
			browser_window_mouse_track(
							input_window->browser->bw,
							0,
							evnt.mx - bwbox.g_x + gw->browser->scroll.current.x,
							evnt.my - bwbox.g_y + gw->browser->scroll.current.y
						);
		}

		if( gw->root->toolbar && within == false ) {
			mt_CompGetLGrect(&app, gw->root->toolbar->url.comp, WF_WORKXYWH, &urlbox);
			if( (evnt.mx > urlbox.g_x && evnt.mx < urlbox.g_x + urlbox.g_w ) &&
				(evnt.my > urlbox.g_y && evnt.my < + urlbox.g_y + urlbox.g_h )) {
				gem_set_cursor( &gem_cursors.ibeam );
				prev_url = true;
			} else {
				if( prev_url ) {
					gem_set_cursor( &gem_cursors.arrow );
					prev_url = false;
				}
			}
		}
	} else {
		gem_set_cursor( &gem_cursors.arrow );
		prev_url = false;
	}

	prev_x = evnt.mx;
	prev_y = evnt.my;
}

void __CDECL global_evnt_keybd( WINDOW * win, short buff[8], void * data)
{
	char sascii;
	long kstate = 0;
	long kcode = 0;
	unsigned short nkc = 0;
	unsigned short nks = 0;

	int i=0;
	bool done = false;
	struct gui_window * gw = input_window;
	struct gui_window * gw_tmp;
	if( gw == NULL )
		return;
	kstate = evnt.mkstate;
	kcode = evnt.keybd;
	nkc= gem_to_norm( (short)kstate, (short)kcode );
	nks = (nkc & 0xFF00);
	if( kstate & (K_LSHIFT|K_RSHIFT))
		kstate |= K_LSHIFT|K_RSHIFT;
	if( window_url_widget_has_focus( gw ) ) {
		/* make sure we report for the root window and report...: */
		done = tb_url_input( gw, nkc );
	}  else  {
		gw_tmp = window_list;
		/* search for active browser component: */
		while( gw_tmp != NULL && done == false ) {
			/* todo: only handle when input_window == ontop */
			if( window_widget_has_focus( (struct gui_window *)input_window,
										 BROWSER,(void*)gw_tmp->browser)) {
				done = browser_input( gw_tmp, nkc );
				break;
			} else {
				gw_tmp = gw_tmp->next;
			}
		}
	}
	sascii = keybd2ascii( evnt.keybd, K_LSHIFT);
	while( menu_evnt_tbl[i].rid != -1 && done == false) {
		if( kstate == menu_evnt_tbl[i].accel.mod && menu_evnt_tbl[i].accel.ascii != 0) {
			if( menu_evnt_tbl[i].accel.ascii == sascii) {
				menu_evnt_tbl[i].menu_func( NULL, menu_evnt_tbl[i].rid, MAINMENU, buff);
				done = true;
				break;
			}
		} else {
			/* the accel code hides in the keycode: */
			if( menu_evnt_tbl[i].accel.keycode != 0) {
				if( menu_evnt_tbl[i].accel.keycode == (nkc & 0xFF) &&
					kstate == menu_evnt_tbl[i].accel.mod &&
					menu_evnt_tbl[i].menu_func != NULL) {
						menu_evnt_tbl[i].menu_func( NULL,
							menu_evnt_tbl[i].rid,
							MAINMENU, buff
						);
						done = true;
						break;
				}
			}
		}
		i++;
	}
}

/*
	Parse encoded menu key shortcut

	The format is:

	"[" 		-   marks start of the shortcut
	"@,^,<" 	-   If the keyshortcut is only valid
					with modifier keys, one of these characters must directly
					follow the start mark.
					Meaning:
					@ -> Alternate
					^ -> Control
	"#"			-   keycode or ascii character.
					The value is handled as keycode if the character value
					is <= 28 ( Atari chracter table )
					or if it is interpreted as function key string.
					(strings: F1 - F10)

*/
static void register_menu_str( struct s_menu_item_evnt * mi )
{
	char * str = ObjcString( h_gem_menu, mi->rid, NULL );
	int l = strlen(str);
	int i = l;
	int x = -1;
	struct s_accelerator * accel = &mi->accel;



	while( i>2 ){
		if( str[i] == '['){
			x = i;
			break;
		}
		i--;
	}
	if( x > -1 ){
		mi->menustr = malloc( l+1 );
		strcpy(mi->menustr, str );
		mi->menustr[x]=' ';
		x++;
		if( str[x] == '@' ){
			accel->mod = K_ALT;
			mi->menustr[x] = 0x07;
			x++;
		}
		else if( str[x] == '^' ) {
			accel->mod = K_CTRL;
			x++;
		}
		if( str[x] <= 28 ){
			// parse symbol
			unsigned short keycode=0;
			switch( str[x] ){
					case 0x03:
					accel->keycode = NK_RIGHT;
				break;
					case 0x04:
					accel->keycode = NK_LEFT;
				break;
					case 0x1B:
					accel->keycode = NK_ESC;
				break;
					default:
				break;
			}
		} else {
			if(str[x] == 'F' && ( str[x+1] >= '1' && str[x+1] <= '9') ){
				// parse function key
				short fkey = atoi(  &str[x+1] );
				if( (fkey >= 0) && (fkey <= 10) ){
					accel->keycode = NK_F1 - 1 + fkey;
				}
			} else {
				accel->ascii = str[x];
			}
		}
	}
}


void __CDECL global_evnt_menu( WINDOW * win, short buff[8] )
{
	int title = buff[ 3];
	INT16 x,y;
	char *str;
	struct gui_window * gw = window_list;
	int i=0;
	MenuTnormal( NULL, title, 1);
	while( gw ) {
		window_set_focus( gw, WIDGET_NONE, NULL  );
		gw = gw->next;
	}
	while( menu_evnt_tbl[i].rid != -1) {
		if( menu_evnt_tbl[i].rid ==  buff[4] ) {
			menu_evnt_tbl[i].menu_func(win, (int)buff[4], (int)buff[3], NULL );
			break;
		}
		i++;
	}
}

void main_menu_update( void )
{
	MenuIcheck( NULL, MAINMENU_M_DEBUG_RENDER, (html_redraw_debug) ? 1 : 0);
	MenuIcheck( NULL, MAINMENU_M_FG_IMAGES, (nsoption_bool(foreground_images)) ? 1 : 0);
	MenuIcheck( NULL, MAINMENU_M_BG_IMAGES, (nsoption_bool(background_images)) ? 1 : 0);
}


/* Bind global and menu events to event handler functions, create accelerators */
void bind_global_events( void )
{
	int i, len;
	int maxlen[NUM_MENU_TITLES]={0};
	char * m, *u, *t;
	char spare[128];
	memset( (void*)&evnt_data, 0, sizeof(struct s_evnt_data) );
	EvntDataAttach( NULL, WM_XKEYBD, global_evnt_keybd, (void*)&evnt_data );
	EvntAttach( NULL, AP_TERM, global_evnt_apterm );
	EvntAttach( NULL, MN_SELECTED,  global_evnt_menu );
	EvntAttach( NULL, WM_XM1, global_evnt_m1 );

	/* parse and update menu items:  */
	i = 0;
	while( menu_evnt_tbl[i].rid != -1 ) {
		char * str = ObjcString( h_gem_menu, menu_evnt_tbl[i].rid, NULL );
		register_menu_str( &menu_evnt_tbl[i] );
		if( menu_evnt_tbl[i].menustr != NULL ){
			MenuText( NULL, menu_evnt_tbl[i].rid, menu_evnt_tbl[i].menustr );
		}
		i++;
	}
	main_menu_update();
}

void unbind_global_events( void )
{
	int i;
	i=0;
	while(menu_evnt_tbl[i].rid != -1) {
		if( menu_evnt_tbl[i].menustr != NULL )
			free(menu_evnt_tbl[i].menustr);
		i++;
	}
}