/*
 * Copyright 2008 Vincent Sanders <vince@simtec.co.uk>
 * Copyright 2009 Mark Benjamin <netsurf-browser.org.MarkBenjamin@dfgh.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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <limits.h>
#include <unistd.h>
#include <string.h>
#include <stdbool.h>

#include <windows.h>
#include <windowsx.h>
#define _WIN32_IE (0x0501)
#include <commctrl.h>

#include <hubbub/hubbub.h>

#include "content/urldb.h"
#include "content/fetch.h"
#include "css/utils.h"
#include "desktop/gui.h"
#include "desktop/history_core.h"
#include "desktop/mouse.h"
#include "desktop/netsurf.h"
#include "desktop/options.h"
#include "desktop/plotters.h"
#include "desktop/save_complete.h"
#include "desktop/selection.h"
#include "desktop/textinput.h"
#include "render/html.h"
#include "utils/url.h"
#include "utils/log.h"
#include "utils/messages.h"
#include "utils/utils.h"

#include "windows/about.h"
#include "windows/gui.h"
#include "windows/findfile.h"
#include "windows/font.h"
#include "windows/localhistory.h"
#include "windows/plot.h"
#include "windows/prefs.h"
#include "windows/resourceid.h"

char *default_stylesheet_url;
char *adblock_stylesheet_url;
char *quirks_stylesheet_url;
char *options_file_location;

struct gui_window *input_window = NULL;
struct gui_window *search_current_window;
struct gui_window *window_list = NULL;

FARPROC urlproc;
WNDPROC	toolproc;

static char default_page[] = "http://www.netsurf-browser.org/welcome/";
static HICON hIcon, hIconS;
static int open_windows = 0;

static const char windowclassname_main[] = "nswsmainwindow";
static const char windowclassname_drawable[] = "nswsdrawablewindow";

#define NSWS_THROBBER_WIDTH 24
#define NSWS_URL_ENTER (WM_USER)

struct gui_window {
	/* The front's private data connected to a browser window */
	/* currently 1<->1 gui_window<->windows window [non-tabbed] */
	struct browser_window *bw; /** the browser_window */
	HWND main; /**< handle to the actual window */
	HWND toolbar; /**< toolbar handle */
	HWND urlbar; /**< url bar handle */
	HWND throbber; /** throbber handle */
	HWND drawingarea; /**< drawing area handle */
	HWND statusbar; /**< status bar handle */
	HWND vscroll; /**< vertical scrollbar handle */
	HWND hscroll; /**< horizontal scrollbar handle */
	HMENU mainmenu; /**< the main menu */
	HMENU rclick; /**< the right-click menu */
	struct nsws_localhistory *localhistory;	/**< handle to local history window */
	int width; /**< width of window */
	int height; /**< height of drawing area */

	int toolbuttonc; /**< number of toolbar buttons */
	int toolbuttonsize; /**< width, height of buttons */
	bool throbbing; /**< whether currently throbbing */

	struct browser_mouse *mouse; /**< mouse state */

	HACCEL acceltable; /**< accelerators */

	float scale; /**< scale of content */

	int scrollx; /**< current scroll location */
	int scrolly; /**< current scroll location */

	RECT *fullscreen; /**< memorize non-fullscreen area */
	RECT redraw; /**< Area needing redraw. */
	RECT clip; /**< current clip rectangle */
	int requestscrollx, requestscrolly; /**< scolling requested. */
	struct gui_window *next, *prev; /**< global linked list */
};

static struct nsws_pointers nsws_pointer;

#ifndef MIN
#define MIN(a,b) (((a) < (b)) ? (a) : (b))
#endif

#ifndef MAX
#define MAX(a,b) (((a) > (b)) ? (a) : (b))
#endif

typedef enum {
	NSWS_ID_TOOLBAR = 1111,
	NSWS_ID_URLBAR,
	NSWS_ID_THROBBER,
	NSWS_ID_DRAWINGAREA,
	NSWS_ID_STATUSBAR,
	NSWS_ID_LAUNCH_URL,
} nsws_constants ;

LRESULT CALLBACK nsws_window_url_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
LRESULT CALLBACK nsws_window_toolbar_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
LRESULT CALLBACK nsws_window_event_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
LRESULT CALLBACK nsws_window_drawable_event_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);

HINSTANCE hinstance;

void gui_multitask(void)
{
/*	LOG(("gui_multitask")); */
}

void gui_poll(bool active)
{
	MSG Msg;
	if (PeekMessage(&Msg, NULL, 0, 0, PM_REMOVE) != 0) {
		if (!((current_gui == NULL) ||
		      (TranslateAccelerator(current_gui->main,
					    current_gui->acceltable, &Msg))))
			TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}

	schedule_run();

}



/**
 * callback for url bar events
 */
LRESULT CALLBACK 
nsws_window_url_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	DWORD i, ii;
	SendMessage(hwnd, EM_GETSEL, (WPARAM)&i, (LPARAM)&ii);
	int x,y;
	x = GET_X_LPARAM(lparam);
	y = GET_Y_LPARAM(lparam);

	if (msg == WM_PAINT) {
		SendMessage(hwnd, EM_SETSEL, (WPARAM)0, (LPARAM)-1);
		SendMessage(hwnd, EM_SETSEL, (WPARAM)i, (LPARAM)ii);
	}
	return CallWindowProc((WNDPROC) urlproc, hwnd, msg, wparam, lparam);
}

/* calculate the dimensions of the url bar relative to the parent toolbar */
static void
urlbar_dimensions(HWND hWndParent, int toolbuttonsize, int buttonc, int *x, int *y, int *width, int *height)
{
	RECT rc;
	const int cy_edit = 24;

	GetClientRect(hWndParent, &rc);
	*x = (toolbuttonsize + 2) * (buttonc + 1) + (NSWS_THROBBER_WIDTH>>1);
	*y = (((rc.bottom - rc.top) + 1) - cy_edit) >> 1;
	*width = ((rc.right - rc.left) + 1) - *x - (NSWS_THROBBER_WIDTH>>1) - NSWS_THROBBER_WIDTH;
	*height = cy_edit;
}

/* obtain gui window structure from windows window handle */
static struct gui_window *
nsws_get_gui_window(HWND hwnd)
{
	struct gui_window *gw;
	HWND phwnd;

	gw = GetProp(hwnd, TEXT("GuiWnd"));

	if (gw == NULL) {
		/* try the parent window instead */
		phwnd = GetParent(hwnd);
		gw = GetProp(phwnd, TEXT("GuiWnd"));
	}

	if (gw == NULL) {
		/* unable to fetch from property, try seraching the
		 * gui window list
		 */
		gw = window_list;
		while (gw != NULL) {
			if ((gw->main == hwnd) || (gw->toolbar == hwnd)) {
				break;
			}
			gw = gw->next;
		}
	}

	return gw;
}

/**
 * callback for toolbar events
 */
LRESULT CALLBACK
nsws_window_toolbar_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	struct gui_window *gw;
	int urlx, urly, urlwidth, urlheight;

	switch (msg) {
	case WM_SIZE:
		gw = nsws_get_gui_window(hwnd);
		urlbar_dimensions(hwnd, gw->toolbuttonsize, gw->toolbuttonc, &urlx, &urly, &urlwidth, &urlheight);
		/* resize url */
		if (gw->urlbar != NULL) {
			MoveWindow(gw->urlbar, urlx, urly, urlwidth, urlheight, true);
		}
		/* move throbber */
		if (gw->throbber != NULL) {
			MoveWindow(gw->throbber,
				   LOWORD(lparam) - NSWS_THROBBER_WIDTH - 4, 8,
				   NSWS_THROBBER_WIDTH, NSWS_THROBBER_WIDTH,
				   true);
		}

		break;
	}

	/* chain to the next handler */
	return CallWindowProc(toolproc, hwnd, msg, wparam, lparam);
}

/**
 * update state of forward/back buttons/menu items when page changes
 */
static void nsws_window_update_forward_back(struct gui_window *w)
{
	if (w->bw == NULL)
		return;
	bool forward = history_forward_available(w->bw->history);
	bool back = history_back_available(w->bw->history);
	if (w->mainmenu != NULL) {
		EnableMenuItem(w->mainmenu, NSWS_ID_NAV_FORWARD,
			       (forward ? MF_ENABLED : MF_GRAYED));
		EnableMenuItem(w->mainmenu, NSWS_ID_NAV_BACK,
			       (back ? MF_ENABLED : MF_GRAYED));
		EnableMenuItem(w->rclick, NSWS_ID_NAV_FORWARD,
			       (forward ? MF_ENABLED : MF_GRAYED));
		EnableMenuItem(w->rclick, NSWS_ID_NAV_BACK,
			       (back ? MF_ENABLED : MF_GRAYED));
	}
	if (w->toolbar != NULL) {
		SendMessage(w->toolbar, TB_SETSTATE,
			    (WPARAM) NSWS_ID_NAV_FORWARD,
			    MAKELONG((forward ? TBSTATE_ENABLED :
				      TBSTATE_INDETERMINATE), 0));
		SendMessage(w->toolbar, TB_SETSTATE,
			    (WPARAM) NSWS_ID_NAV_BACK,
			    MAKELONG((back ? TBSTATE_ENABLED :
				      TBSTATE_INDETERMINATE), 0));
	}
}

static void nsws_update_edit(struct gui_window *w)
{
	bool paste, copy, del;
	if (GetFocus() == w->urlbar) {
		DWORD i, ii;
		SendMessage(w->urlbar, EM_GETSEL, (WPARAM)&i,
			    (LPARAM)&ii);
		paste = true;
		copy = (i != ii);
		del = (i != ii);

	} else if ((w->bw != NULL) && (w->bw->sel != NULL)){
		paste = (w->bw->paste_callback != NULL);
		copy = w->bw->sel->defined;
		del = ((w->bw->sel->defined) &&
		       (w->bw->caret_callback != NULL));
	} else {
		paste = false;
		copy = false;
		del = false;
	}
	EnableMenuItem(w->mainmenu,
		       NSWS_ID_EDIT_PASTE,
		       (paste ? MF_ENABLED : MF_GRAYED));

	EnableMenuItem(w->rclick,
		       NSWS_ID_EDIT_PASTE,
		       (paste ? MF_ENABLED : MF_GRAYED));


	EnableMenuItem(w->mainmenu,
		       NSWS_ID_EDIT_COPY,
		       (copy ? MF_ENABLED : MF_GRAYED));


	EnableMenuItem(w->rclick,
		       NSWS_ID_EDIT_COPY,
		       (copy ? MF_ENABLED : MF_GRAYED));


	if (del == true) {
		EnableMenuItem(w->mainmenu, NSWS_ID_EDIT_CUT, MF_ENABLED);
		EnableMenuItem(w->mainmenu, NSWS_ID_EDIT_DELETE, MF_ENABLED);
		EnableMenuItem(w->rclick, NSWS_ID_EDIT_CUT, MF_ENABLED);
		EnableMenuItem(w->rclick, NSWS_ID_EDIT_DELETE, MF_ENABLED);
	} else {
		EnableMenuItem(w->mainmenu, NSWS_ID_EDIT_CUT, MF_GRAYED);
		EnableMenuItem(w->mainmenu, NSWS_ID_EDIT_DELETE, MF_GRAYED);
		EnableMenuItem(w->rclick, NSWS_ID_EDIT_CUT, MF_GRAYED);
		EnableMenuItem(w->rclick, NSWS_ID_EDIT_DELETE, MF_GRAYED);
	}
}

static bool
nsws_ctx_menu(struct gui_window *w, HWND hwnd, int x, int y)
{
	RECT rc; /* client area of window */
	POINT pt = { x, y }; /* location of mouse click */

	/* Get the bounding rectangle of the client area. */
	GetClientRect(hwnd, &rc);

	/* Convert the mouse position to client coordinates. */
	ScreenToClient(hwnd, &pt);

	/* If the position is in the client area, display a shortcut menu. */
	if (PtInRect(&rc, pt)) {
		ClientToScreen(hwnd, &pt);
		nsws_update_edit(w);
		TrackPopupMenu(GetSubMenu(w->rclick, 0),
			       TPM_CENTERALIGN | TPM_TOPALIGN,
			       x,
			       y,
			       0,
			       hwnd,
			       NULL);

		return true;
	}

	/* Return false if no menu is displayed. */
	return false;
}

/**
 * set accelerators
 */
static void nsws_window_set_accels(struct gui_window *w)
{
	int i, nitems = 13;
	ACCEL accels[nitems];
	for (i = 0; i < nitems; i++)
		accels[i].fVirt = FCONTROL | FVIRTKEY;
	accels[0].key = 0x51; /* Q */
	accels[0].cmd = NSWS_ID_FILE_QUIT;
	accels[1].key = 0x4E; /* N */
	accels[1].cmd = NSWS_ID_FILE_OPEN_WINDOW;
	accels[2].key = VK_LEFT;
	accels[2].cmd = NSWS_ID_NAV_BACK;
	accels[3].key = VK_RIGHT;
	accels[3].cmd = NSWS_ID_NAV_FORWARD;
	accels[4].key = VK_UP;
	accels[4].cmd = NSWS_ID_NAV_HOME;
	accels[5].key = VK_BACK;
	accels[5].cmd = NSWS_ID_NAV_STOP;
	accels[6].key = VK_SPACE;
	accels[6].cmd = NSWS_ID_NAV_RELOAD;
	accels[7].key = 0x4C; /* L */
	accels[7].cmd = NSWS_ID_FILE_OPEN_LOCATION;
	accels[8].key = 0x57; /* w */
	accels[8].cmd = NSWS_ID_FILE_CLOSE_WINDOW;
	accels[9].key = 0x41; /* A */
	accels[9].cmd = NSWS_ID_EDIT_SELECT_ALL;
	accels[10].key = VK_F8;
	accels[10].cmd = NSWS_ID_VIEW_SOURCE;
	accels[11].key = VK_RETURN;
	accels[11].fVirt = FVIRTKEY;
	accels[11].cmd = NSWS_ID_LAUNCH_URL;
	accels[12].key = VK_F11;
	accels[12].fVirt = FVIRTKEY;
	accels[12].cmd = NSWS_ID_VIEW_FULLSCREEN;

	w->acceltable = CreateAcceleratorTable(accels, nitems);
}

/**
 * set window icons
 */
static void nsws_window_set_ico(struct gui_window *w)
{
	char ico[PATH_MAX];
	nsws_find_resource(ico, "NetSurf32.ico", "windows/res/NetSurf32.ico");
	LOG(("setting ico as %s", ico));
	hIcon = LoadImage(NULL, ico, IMAGE_ICON, 32, 32, LR_LOADFROMFILE);
	if (hIcon != NULL)
		SendMessage(w->main, WM_SETICON, ICON_BIG, (LPARAM) hIcon);
	nsws_find_resource(ico, "NetSurf16.ico", "windows/res/NetSurf16.ico");
	LOG(("setting ico as %s", ico));
	hIconS = LoadImage(NULL, ico, IMAGE_ICON, 16, 16, LR_LOADFROMFILE);
	if (hIconS != NULL)
		SendMessage(w->main, WM_SETICON, ICON_SMALL, (LPARAM)hIconS);
}


/**
 * creation of throbber
 */
static void nsws_window_throbber_create(struct gui_window *w)
{
	HWND hwnd;
	char avi[PATH_MAX];

	hwnd = CreateWindow(ANIMATE_CLASS,
			    "",
			    WS_CHILD | WS_VISIBLE | ACS_TRANSPARENT,
			    w->width - NSWS_THROBBER_WIDTH - 4,
			    8,
			    NSWS_THROBBER_WIDTH,
			    NSWS_THROBBER_WIDTH,
			    w->main,
			    (HMENU) NSWS_ID_THROBBER,
			    hinstance,
			    NULL);

	nsws_find_resource(avi, "throbber.avi", "windows/res/throbber.avi");
	LOG(("setting throbber avi as %s", avi));
	Animate_Open(hwnd, avi);
	if (w->throbbing)
		Animate_Play(hwnd, 0, -1, -1);
	else
		Animate_Seek(hwnd, 0);
	ShowWindow(hwnd, SW_SHOWNORMAL);
	w->throbber = hwnd;
}

static HIMAGELIST 
nsws_set_imagelist(HWND hwnd, UINT msg, int resid, int bsize, int bcnt)
{
	HIMAGELIST hImageList;
	HBITMAP hScrBM;

	hImageList = ImageList_Create(bsize, bsize, ILC_COLOR24 |ILC_MASK, 0, bcnt);
	hScrBM = LoadImage(hinstance, MAKEINTRESOURCE(resid),
			   IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR);
	ImageList_AddMasked(hImageList, hScrBM, 0xcccccc);
	DeleteObject(hScrBM);

	SendMessage(hwnd, msg, (WPARAM)0, (LPARAM)hImageList);
	return hImageList;
}

static HWND
nsws_window_toolbar_create(struct gui_window *gw, HWND hWndParent)
{
	HWND hWndToolbar;
	/* Toolbar buttons */
	TBBUTTON tbButtons[] = {
		{0, NSWS_ID_NAV_BACK, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
		{1, NSWS_ID_NAV_FORWARD, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
		{2, NSWS_ID_NAV_HOME, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
		{3, NSWS_ID_NAV_RELOAD, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
		{4, NSWS_ID_NAV_STOP, TBSTATE_ENABLED, BTNS_BUTTON, {0}, 0, 0},
	};

	/* Create the toolbar child window. */
	hWndToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, "Toolbar",
				     WS_CHILD | WS_VISIBLE | TBSTYLE_FLAT,
				     0, 0, 0, 0,
				     hWndParent, NULL, HINST_COMMCTRL, NULL);

	if (!hWndToolbar) {
		return NULL;
	}

	/* remember how many buttons are being created */
	gw->toolbuttonc = sizeof(tbButtons) / sizeof(TBBUTTON);

	/* Create the standard image list and assign to toolbar. */
	nsws_set_imagelist(hWndToolbar, TB_SETIMAGELIST, NSWS_ID_TOOLBAR_BITMAP, gw->toolbuttonsize, gw->toolbuttonc);

	/* Create the disabled image list and assign to toolbar. */
	nsws_set_imagelist(hWndToolbar, TB_SETDISABLEDIMAGELIST, NSWS_ID_TOOLBAR_GREY_BITMAP, gw->toolbuttonsize, gw->toolbuttonc);

	/* Create the hot image list and assign to toolbar. */
	nsws_set_imagelist(hWndToolbar, TB_SETHOTIMAGELIST, NSWS_ID_TOOLBAR_HOT_BITMAP, gw->toolbuttonsize, gw->toolbuttonc);

	/* Add buttons. */
	SendMessage(hWndToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
	SendMessage(hWndToolbar, TB_ADDBUTTONS, (WPARAM)gw->toolbuttonc, (LPARAM)&tbButtons);


	int urlx, urly, urlwidth, urlheight;
	urlbar_dimensions(hWndToolbar, gw->toolbuttonsize, gw->toolbuttonc, &urlx, &urly, &urlwidth, &urlheight);

	// Create the edit control child window.
	gw->urlbar = CreateWindowEx(0L, "Edit", NULL,
				    WS_CHILD | WS_BORDER | WS_VISIBLE | ES_LEFT
				    | ES_AUTOVSCROLL | ES_MULTILINE,
				    urlx,
				    urly,
				    urlwidth,
				    urlheight,
				    hWndToolbar,
				    (HMENU)NSWS_ID_URLBAR,
				    hinstance, 0 );

	if (!gw->urlbar) {
		DestroyWindow(hWndToolbar);
		return NULL;
	}

	nsws_window_throbber_create(gw);

	/* set the gui window associated with this toolbar */
	SetProp(hWndToolbar, TEXT("GuiWnd"), (HANDLE)gw);

	/* subclass the message handler */
	toolproc = (WNDPROC)SetWindowLongPtr(hWndToolbar, GWLP_WNDPROC, (LONG_PTR)nsws_window_toolbar_callback);

	/* Return the completed toolbar */
	return hWndToolbar;
}




static LRESULT nsws_drawable_mousemove(struct gui_window *gw, int x, int y)
{
	bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
	bool ctrl = ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000);
	bool alt = ((GetKeyState(VK_MENU) & 0x8000) == 0x8000);

	if ((gw == NULL) ||
	    (gw->mouse == NULL) ||
	    (gw->bw == NULL))
		return 0;

	if ((gw->mouse->state & BROWSER_MOUSE_PRESS_1) != 0) {
		browser_window_mouse_click(gw->bw, BROWSER_MOUSE_DRAG_1,
					   gw->mouse->pressed_x,
					   gw->mouse->pressed_y);
		gw->mouse->state &= ~BROWSER_MOUSE_PRESS_1;
		gw->mouse->state |= BROWSER_MOUSE_HOLDING_1 |
			BROWSER_MOUSE_DRAG_ON;
	}
	else if ((gw->mouse->state & BROWSER_MOUSE_PRESS_2) != 0) {
		browser_window_mouse_click(gw->bw, BROWSER_MOUSE_DRAG_2,
					   gw->mouse->pressed_x,
					   gw->mouse->pressed_y);
		gw->mouse->state &= ~BROWSER_MOUSE_PRESS_2;
		gw->mouse->state |= BROWSER_MOUSE_HOLDING_2 |
			BROWSER_MOUSE_DRAG_ON;
	}
	if (((gw->mouse->state & BROWSER_MOUSE_MOD_1) != 0) && !shift)
		gw->mouse->state &= ~BROWSER_MOUSE_MOD_1;
	if (((gw->mouse->state & BROWSER_MOUSE_MOD_2) != 0) && !ctrl)
		gw->mouse->state &= ~BROWSER_MOUSE_MOD_2;
	if (((gw->mouse->state & BROWSER_MOUSE_MOD_3) != 0) && !alt)
		gw->mouse->state &= ~BROWSER_MOUSE_MOD_3;

	browser_window_mouse_track(gw->bw, gw->mouse->state,
				   (x + gw->scrollx) / gw->bw->scale,
				   (y + gw->scrolly) / gw->bw->scale);

	return 0;
}

static LRESULT nsws_drawable_mousedown(struct gui_window *gw, int x, int y, browser_mouse_state button)
{
	if ((gw == NULL) ||
	    (gw->mouse == NULL) ||
	    (gw->bw == NULL)) {
		nsws_localhistory_close(gw);
		return 0;
	}

	gw->mouse->state = button;
	if ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000)
		gw->mouse->state |= BROWSER_MOUSE_MOD_1;
	if ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000)
		gw->mouse->state |= BROWSER_MOUSE_MOD_2;
	if ((GetKeyState(VK_MENU) & 0x8000) == 0x8000)
		gw->mouse->state |= BROWSER_MOUSE_MOD_3;

	gw->mouse->pressed_x = (x + gw->scrollx) / gw->bw->scale;
	gw->mouse->pressed_y = (y + gw->scrolly) / gw->bw->scale;

	browser_window_mouse_click(gw->bw, gw->mouse->state,
				   (x + gw->scrollx) / gw->bw->scale ,
				   (y + gw->scrolly) / gw->bw->scale);

	return 0;
}

static LRESULT
nsws_drawable_mouseup(struct gui_window *gw,
		      int x,
		      int y,
		      browser_mouse_state press,
		      browser_mouse_state click)
{
	bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
	bool ctrl = ((GetKeyState(VK_CONTROL) & 0x8000) == 0x8000);
	bool alt = ((GetKeyState(VK_MENU) & 0x8000) == 0x8000);

	if ((gw == NULL) ||
	    (gw->mouse == NULL) ||
	    (gw->bw == NULL))
		return 0;


	if ((gw->mouse->state & press) != 0) {
		gw->mouse->state &= ~press;
		gw->mouse->state |= click;
	}

	if (((gw->mouse->state & BROWSER_MOUSE_MOD_1) != 0) && !shift)
		gw->mouse->state &= ~BROWSER_MOUSE_MOD_1;
	if (((gw->mouse->state & BROWSER_MOUSE_MOD_2) != 0) && !ctrl)
		gw->mouse->state &= ~BROWSER_MOUSE_MOD_2;
	if (((gw->mouse->state & BROWSER_MOUSE_MOD_3) != 0) && !alt)
		gw->mouse->state &= ~BROWSER_MOUSE_MOD_3;

	if ((gw->mouse->state & click) != 0)
		browser_window_mouse_click(gw->bw,
					   gw->mouse->state,
					   (x + gw->scrollx) / gw->bw->scale,
					   (y + gw->scrolly) / gw->bw->scale);
	else
		browser_window_mouse_drag_end(gw->bw,
					      0,
					      (x + gw->scrollx) / gw->bw->scale,
					      (y + gw->scrolly) / gw->bw->scale);

	gw->mouse->state = 0;
	return 0;
}

static void nsws_drawable_paint(struct gui_window *gw, HWND hwnd)
{
	PAINTSTRUCT ps;

	BeginPaint(hwnd, &ps);

	if ((gw->bw != NULL) && (gw->bw->current_content != NULL)) {
		/* set globals for the plotters */
		current_hwnd = gw->drawingarea;
		current_gui = gw;

		content_redraw(gw->bw->current_content, 
			       -gw->scrollx / gw->bw->scale,
			       -gw->scrolly / gw->bw->scale,
			       gw->width, 
			       gw->height,
			       ps.rcPaint.left,
			       ps.rcPaint.top,
			       ps.rcPaint.right,
			       ps.rcPaint.bottom,
			       gw->bw->scale, 
			       0xFFFFFF);
	}

	EndPaint(hwnd, &ps);
}

static void 
nsws_drawable_hscroll(struct gui_window *gw, HWND hwnd, WPARAM wparam)
{
	LOG(("HSCROLL!"));
	if (gw->requestscrollx != 0)
		return;
	SCROLLINFO si;
	int mem;
	si.cbSize = sizeof(si);
	si.fMask = SIF_ALL;
	GetScrollInfo(hwnd, SB_HORZ, &si);
	mem = si.nPos;
	switch (LOWORD(wparam))	{
	case SB_LINELEFT:
		si.nPos -= 30;
		break;

	case SB_LINERIGHT:
		si.nPos += 30;
		break;

	case SB_PAGELEFT:
		si.nPos -= gw->width;
		break;

	case SB_PAGERIGHT:
		si.nPos += gw->width;
		break;

	case SB_THUMBTRACK:
		si.nPos = si.nTrackPos;
		break;

	default:
		break;
	}
	si.fMask = SIF_POS;
	if ((gw->bw != NULL) && (gw->bw->current_content != NULL))
		si.nPos = MIN(si.nPos,
			      content_get_width(gw->bw->current_content) *
			      gw->bw->scale - gw->width);
	si.nPos = MAX(si.nPos, 0);
	SetScrollInfo(hwnd, SB_HORZ, &si, TRUE);
	GetScrollInfo(hwnd, SB_HORZ, &si);
	if (si.nPos != mem) {
		gui_window_set_scroll(gw, gw->scrollx + gw->requestscrollx + si.nPos - mem, gw->scrolly);
	}
}

static void
nsws_drawable_vscroll(struct gui_window *gw, HWND hwnd, WPARAM wparam)
{
	LOG(("VSCROLL!"));
	if (gw->requestscrolly != 0)
		return;
	SCROLLINFO si;
	int mem;
	si.cbSize = sizeof(si);
	si.fMask = SIF_ALL;
	GetScrollInfo(hwnd, SB_VERT, &si);
	mem = si.nPos;
	switch (LOWORD(wparam))	{
	case SB_TOP:
		si.nPos = si.nMin;
		break;

	case SB_BOTTOM:
		si.nPos = si.nMax;
		break;

	case SB_LINEUP:
		si.nPos -= 30;
		break;

	case SB_LINEDOWN:
		si.nPos += 30;
		break;

	case SB_PAGEUP:
		si.nPos -= gw->height;
		break;

	case SB_PAGEDOWN:
		si.nPos += gw->height;
		break;

	case SB_THUMBTRACK:
		si.nPos = si.nTrackPos;
		break;

	default:
		break;
	}
	si.fMask = SIF_POS;
	if ((gw->bw != NULL) && 
	    (gw->bw->current_content != NULL)) {
		si.nPos = MIN(si.nPos,
			      content_get_height(gw->bw->current_content) *
			      gw->bw->scale - gw->height);
	}
	si.nPos = MAX(si.nPos, 0);
	SetScrollInfo(hwnd, SB_VERT, &si, TRUE);
	GetScrollInfo(hwnd, SB_VERT, &si);
	if (si.nPos != mem) {
		gui_window_set_scroll(gw, gw->scrollx, gw->scrolly +
				      gw->requestscrolly + si.nPos - mem);
	}
}

static void nsws_drawable_key(struct gui_window *gw, HWND hwnd, WPARAM wparam)
{
	if (GetFocus() != hwnd)
		return;

	uint32_t i;
	bool shift = ((GetKeyState(VK_SHIFT) & 0x8000) == 0x8000);
	bool capslock = ((GetKeyState(VK_CAPITAL) & 1) == 1);

	switch(wparam) {
	case VK_LEFT:
		i = KEY_LEFT;
		if (shift)
			SendMessage(hwnd, WM_HSCROLL,
				    MAKELONG(SB_LINELEFT, 0), 0);
		break;

	case VK_RIGHT:
		i = KEY_RIGHT;
		if (shift)
			SendMessage(hwnd, WM_HSCROLL,
				    MAKELONG(SB_LINERIGHT, 0), 0);
		break;

	case VK_UP:
		i = KEY_UP;
		if (shift)
			SendMessage(hwnd, WM_VSCROLL,
				    MAKELONG(SB_LINEUP, 0), 0);
		break;

	case VK_DOWN:
		i = KEY_DOWN;
		if (shift)
			SendMessage(hwnd, WM_VSCROLL,
				    MAKELONG(SB_LINEDOWN, 0), 0);
		break;

	case VK_HOME:
		i = KEY_LINE_START;
		if (shift)
			SendMessage(hwnd, WM_HSCROLL,
				    MAKELONG(SB_PAGELEFT, 0), 0);
		break;

	case VK_END:
		i = KEY_LINE_END;
		if (shift)
			SendMessage(hwnd, WM_HSCROLL,
				    MAKELONG(SB_PAGERIGHT, 0), 0);
		break;

	case VK_DELETE:
		i = KEY_DELETE_RIGHT;
		break;

	case VK_NEXT:
		i = wparam;
		SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_PAGEDOWN, 0),
			    0);
		break;

	case VK_PRIOR:
		i = wparam;
		SendMessage(hwnd, WM_VSCROLL, MAKELONG(SB_PAGEUP, 0),
			    0);
		break;

	default:
		i = wparam;
		break;
	}

	if ((i >= 'A') && (i <= 'Z') &&
	    (((!capslock) && (!shift)) ||
	     ((capslock) && (shift))))
		i += 'a' - 'A';

	if (gw != NULL)
		browser_window_key_press(gw->bw, i);

}

static DWORD 
nsws_drawable_wheel(struct gui_window *gw, HWND hwnd, WPARAM wparam)
{
	int i, z = GET_WHEEL_DELTA_WPARAM(wparam) / WHEEL_DELTA;
	int key = LOWORD(wparam);
	DWORD command;
	unsigned int newmessage = WM_VSCROLL;
	if (key == MK_SHIFT) {
		command = (z > 0) ? SB_LINERIGHT : SB_LINELEFT;
		newmessage = WM_HSCROLL;
	} else
		/* add MK_CONTROL -> zoom */
		command = (z > 0) ? SB_LINEUP : SB_LINEDOWN;
	z = (z < 0) ? -1 * z : z;
	for (i = 0; i < z; i++)
		SendMessage(hwnd, newmessage, MAKELONG(command, 0), 0);
	return 0;
}

static DWORD
nsws_drawable_resize(struct gui_window *gw)
{
	browser_window_reformat(gw->bw, gw->width, gw->height);
	return 0;
}

/* Called when activity occours within the drawable window. */
LRESULT CALLBACK nsws_window_drawable_event_callback(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
	struct gui_window *gw = window_list;

	while (gw != NULL) {
		if (gw->drawingarea == hwnd) {
			break;
		}
		gw = gw->next;
	}

	if (gw == NULL)
		return DefWindowProc(hwnd, msg, wparam, lparam);

	switch(msg) {

	case WM_MOUSEMOVE:
		nsws_drawable_mousemove(gw,
					GET_X_LPARAM(lparam),
					GET_Y_LPARAM(lparam));
		break;

	case WM_LBUTTONDOWN:
		nsws_drawable_mousedown(gw,
					GET_X_LPARAM(lparam),
					GET_Y_LPARAM(lparam),
					BROWSER_MOUSE_PRESS_1);
		SetFocus(hwnd);
		nsws_localhistory_close(gw);
		break;

	case WM_RBUTTONDOWN:
		nsws_drawable_mousedown(gw,
					GET_X_LPARAM(lparam),
					GET_Y_LPARAM(lparam),
					BROWSER_MOUSE_PRESS_2);
		SetFocus(hwnd);
		break;

	case WM_LBUTTONUP:
		nsws_drawable_mouseup(gw,
				      GET_X_LPARAM(lparam),
				      GET_Y_LPARAM(lparam),
				      BROWSER_MOUSE_PRESS_1,
				      BROWSER_MOUSE_CLICK_1);
		break;

	case WM_RBUTTONUP:
		nsws_drawable_mouseup(gw,
				      GET_X_LPARAM(lparam),
				      GET_Y_LPARAM(lparam),
				      BROWSER_MOUSE_PRESS_2,
				      BROWSER_MOUSE_CLICK_2);
		break;

	case WM_ERASEBKGND:
		return 0;

	case WM_PAINT:
		nsws_drawable_paint(gw, hwnd);
		break;

	case WM_KEYDOWN:
		nsws_drawable_key(gw, hwnd, wparam);
		break;

	case WM_SIZE:
		nsws_drawable_resize(gw);
		break;

	case WM_HSCROLL:
		nsws_drawable_hscroll(gw, hwnd, wparam);
		break;

	case WM_VSCROLL:
		nsws_drawable_vscroll(gw, hwnd, wparam);
		break;

	case WM_MOUSEWHEEL:
		return nsws_drawable_wheel(gw, hwnd, wparam);

	default:
		break;
	}

	return DefWindowProc(hwnd, msg, wparam, lparam);
}

static void
nsws_window_resize(struct gui_window *w,
		   HWND hwnd,
		   WPARAM wparam,
		   LPARAM lparam)
{
	if ((w->toolbar == NULL) ||
	    (w->urlbar == NULL) ||
	    (w->statusbar == NULL))
		return;

	int x, y;
	RECT rmain, rstatus, rtool;
	GetClientRect(hwnd, &rmain);
	GetClientRect(w->toolbar, &rtool);
	GetWindowRect(w->statusbar, &rstatus);
	gui_window_get_scroll(w, &x, &y);
	w->height = HIWORD(lparam) - (rtool.bottom - rtool.top) - (rstatus.bottom - rstatus.top);
	w->width = LOWORD(lparam);

	if (w->drawingarea != NULL) {
		MoveWindow(w->drawingarea,
			   0,
			   rtool.bottom,
			   w->width,
			   w->height,
			   true);
	}
	nsws_window_update_forward_back(w);

	gui_window_set_scroll(w, x, y);

	if (w->toolbar != NULL)
		SendMessage(w->toolbar, TB_SETSTATE,
			    (WPARAM) NSWS_ID_NAV_STOP,
			    MAKELONG(TBSTATE_INDETERMINATE, 0));

}

/**
 * callback for window events generally
 */
LRESULT CALLBACK nsws_window_event_callback(HWND hwnd, UINT msg, WPARAM wparam,
					    LPARAM lparam)
{
	bool match = false;
	bool historyactive = false;
	struct gui_window *w = window_list;
	while (w != NULL) {
		if (w->main == hwnd) {
			match = true;
			break;
		}
		w = w->next;
	}
	if (!match) { /* during initial window creation */
		w = window_list;
		while (w != NULL) {
			if (w->main == NULL) {
				w->main = hwnd;
				break;
			}
			w = w->next;
		}
	}
	if ((match) && (current_gui == NULL)) {
		/* local history window is active */
		if ((msg == WM_LBUTTONDOWN) || (msg == WM_PAINT))
			historyactive = true;
		else if ((msg == WM_NCHITTEST) || (msg == WM_SETCURSOR))
			return DefWindowProc(hwnd, msg, wparam, lparam);
		else
			return 0;
	}
	current_gui = w;
	switch(msg) {

	case WM_LBUTTONDBLCLK: {
		int x,y;
		x = GET_X_LPARAM(lparam);
		y = GET_Y_LPARAM(lparam);
		if ((w != NULL) && (w->bw != NULL) )
			browser_window_mouse_click(w->bw,
						   BROWSER_MOUSE_DOUBLE_CLICK,
						   (x + w->scrollx) / w->bw->scale,
						   (y + w->scrolly) / w->bw->scale);
		return DefWindowProc(hwnd, msg, wparam, lparam);
		break;
	}
	case WM_NCLBUTTONDOWN: {
		int x,y;
		x = GET_X_LPARAM(lparam);
		y = GET_Y_LPARAM(lparam);
		return DefWindowProc(hwnd, msg, wparam, lparam);
		break;
	}
	case WM_ENTERMENULOOP:
		nsws_update_edit(w);
		return DefWindowProc(hwnd, msg, wparam, lparam);

	case WM_CONTEXTMENU:
		if (!nsws_ctx_menu(w, hwnd, GET_X_LPARAM(lparam), GET_Y_LPARAM(lparam)))
			return DefWindowProc(hwnd, msg, wparam, lparam);


		break;

	case WM_COMMAND:
	{
		switch(LOWORD(wparam)) {
		case NSWS_ID_FILE_QUIT:
			w = window_list;
			while (w != NULL) {
				browser_window_destroy(w->bw);
				w = w->next;
			}
			netsurf_quit = true;
			break;

		case NSWS_ID_FILE_OPEN_LOCATION:
			SetFocus(w->urlbar);
			break;

		case NSWS_ID_FILE_OPEN_WINDOW:
			browser_window_create(NULL, w->bw, NULL, false, false);
			break;

		case NSWS_ID_FILE_CLOSE_WINDOW:
			PostMessage(hwnd, WM_CLOSE, 0, 0);
			break;
		case NSWS_ID_FILE_SAVE_PAGE:
			break;
		case NSWS_ID_FILE_SAVEAS_TEXT:
			break;
		case NSWS_ID_FILE_SAVEAS_PDF:
			break;
		case NSWS_ID_FILE_SAVEAS_DRAWFILE:
			break;
		case NSWS_ID_FILE_SAVEAS_POSTSCRIPT:
			break;
		case NSWS_ID_FILE_PRINT_PREVIEW:
			break;
		case NSWS_ID_FILE_PRINT:
			break;
		case NSWS_ID_EDIT_CUT:
			OpenClipboard(hwnd);
			EmptyClipboard();
			CloseClipboard();
			if (GetFocus() == w->urlbar)
				SendMessage(w->urlbar, WM_CUT, 0, 0);
			else if (w->bw != NULL)
				browser_window_key_press(w->bw,
							 KEY_CUT_SELECTION);
			break;
		case NSWS_ID_EDIT_COPY:
			OpenClipboard(hwnd);
			EmptyClipboard();
			CloseClipboard();
			if (GetFocus() == w->urlbar)
				SendMessage(w->urlbar, WM_COPY, 0, 0);
			else if (w->bw != NULL)
				gui_copy_to_clipboard(w->bw->sel);
			break;
		case NSWS_ID_EDIT_PASTE: {
			OpenClipboard(hwnd);
			HANDLE h = GetClipboardData(CF_TEXT);
			if (h != NULL) {
				char *content = GlobalLock(h);
				LOG(("pasting %s\n", content));
				GlobalUnlock(h);
			}
			CloseClipboard();
			if (GetFocus() == w->urlbar)
				SendMessage(w->urlbar, WM_PASTE, 0, 0);
			else
				gui_paste_from_clipboard(w, 0, 0);
			break;
		}
		case NSWS_ID_EDIT_DELETE:
			if (GetFocus() == w->urlbar)
				SendMessage(w->urlbar, WM_CUT, 0, 0);
			else
				browser_window_key_press(w->bw,
							 KEY_DELETE_RIGHT);
			break;
		case NSWS_ID_EDIT_SELECT_ALL:
			if (GetFocus() == w->urlbar)
				SendMessage(w->urlbar, EM_SETSEL, 0, -1);
			else
				selection_select_all(w->bw->sel);
			break;
		case NSWS_ID_EDIT_SEARCH:
			break;
		case NSWS_ID_EDIT_PREFERENCES:
			nsws_prefs_dialog_init(w->main);
			break;
		case NSWS_ID_NAV_BACK:
			if ((w->bw != NULL) && (history_back_available(
							w->bw->history))) {
				history_back(w->bw, w->bw->history);
			}
			nsws_window_update_forward_back(w);
			break;
		case NSWS_ID_NAV_FORWARD:
			if ((w->bw != NULL) && (history_forward_available(
							w->bw->history))) {
				history_forward(w->bw, w->bw->history);
			}
			nsws_window_update_forward_back(w);
			break;
		case NSWS_ID_NAV_HOME:
			browser_window_go(w->bw, default_page, 0, true);
			break;
		case NSWS_ID_NAV_STOP:
			browser_window_stop(w->bw);
			break;
		case NSWS_ID_NAV_RELOAD:
			browser_window_reload(w->bw, true);
			break;
		case NSWS_ID_NAV_LOCALHISTORY:
			nsws_localhistory_init(w);
			break;
		case NSWS_ID_NAV_GLOBALHISTORY:
			break;
		case NSWS_ID_VIEW_ZOOMPLUS: {
			int x, y;
			gui_window_get_scroll(w, &x, &y);
			if (w->bw != NULL) {
				browser_window_set_scale(w->bw,
							 w->bw->scale * 1.1, true);
				browser_window_reformat(w->bw, w->width,
							w->height);
			}
			gui_window_redraw_window(w);
			gui_window_set_scroll(w, x, y);
			break;
		}
		case NSWS_ID_VIEW_ZOOMMINUS: {
			int x, y;
			gui_window_get_scroll(w, &x, &y);
			if (w->bw != NULL) {
				browser_window_set_scale(w->bw,
							 w->bw->scale * 0.9, true);
				browser_window_reformat(w->bw, w->width,
							w->height);
			}
			gui_window_redraw_window(w);
			gui_window_set_scroll(w, x, y);
			break;
		}
		case NSWS_ID_VIEW_ZOOMNORMAL: {
			int x, y;
			gui_window_get_scroll(w, &x, &y);
			if (w->bw != NULL) {
				browser_window_set_scale(w->bw,
							 1.0, true);
				browser_window_reformat(w->bw, w->width,
							w->height);
			}
			gui_window_redraw_window(w);
			gui_window_set_scroll(w, x, y);
			break;
		}
		case NSWS_ID_VIEW_SOURCE:
			break;
		case NSWS_ID_VIEW_SAVE_WIN_METRICS: {
			RECT r;
			GetWindowRect(hwnd, &r);
			option_window_x = r.left;
			option_window_y = r.top;
			option_window_width = r.right - r.left;
			option_window_height = r.bottom - r.top;
			options_write(options_file_location);
			break;
		}
/*		case NSWS_ID_VIEW_FULLSCREEN: {
			RECT rdesk;
			if (w->fullscreen == NULL) {
				HWND desktop = GetDesktopWindow();
				w->fullscreen = malloc(sizeof(RECT));
				if ((desktop == NULL) ||
				    (w->fullscreen == NULL)) {
					warn_user("NoMemory", 0);
					break;
				}
				GetWindowRect(desktop, &rdesk);
				GetWindowRect(hwnd, w->fullscreen);
				DeleteObject(desktop);
				SetWindowLong(hwnd, GWL_STYLE, 0);
				SetWindowPos(hwnd, HWND_TOPMOST, 0, 0,
					     rdesk.right - rdesk.left,
					     rdesk.bottom - rdesk.top,
					     SWP_SHOWWINDOW);
			} else {
				SetWindowLong(hwnd, GWL_STYLE,
					      WS_OVERLAPPEDWINDOW |
					      WS_HSCROLL | WS_VSCROLL |
					      WS_CLIPCHILDREN |
					      WS_CLIPSIBLINGS | CS_DBLCLKS);
				SetWindowPos(hwnd, HWND_TOPMOST,
					     w->fullscreen->left,
					     w->fullscreen->top,
					     w->fullscreen->right -
					     w->fullscreen->left,
					     w->fullscreen->bottom -
					     w->fullscreen->top,
					     SWP_SHOWWINDOW |
					     SWP_FRAMECHANGED);
				free(w->fullscreen);
				w->fullscreen = NULL;
			}
			break;
			}*/
		case NSWS_ID_VIEW_DOWNLOADS:
			break;
		case NSWS_ID_VIEW_TOGGLE_DEBUG_RENDERING:
			html_redraw_debug = !html_redraw_debug;
			if (w->bw != NULL) {
				browser_window_reformat(w->bw, w->width, w->height);
			}
			break;
		case NSWS_ID_VIEW_DEBUGGING_SAVE_BOXTREE:
			break;
		case NSWS_ID_VIEW_DEBUGGING_SAVE_DOMTREE:
			break;
		case NSWS_ID_HELP_CONTENTS:
			break;
		case NSWS_ID_HELP_GUIDE:
			break;
		case NSWS_ID_HELP_INFO:
			break;
		case NSWS_ID_HELP_ABOUT:
			nsws_about_dialog_init(hinstance, hwnd);
			break;
		case NSWS_ID_LAUNCH_URL:
		{
			if (GetFocus() != w->urlbar)
				break;
			int len = SendMessage(w->urlbar, WM_GETTEXTLENGTH, 0,
					      0);
			char addr[len + 1];
			SendMessage(w->urlbar, WM_GETTEXT, (WPARAM) (len + 1),
				    (LPARAM) addr);
			LOG(("launching %s\n", addr));
			browser_window_go(w->bw, addr, 0, true);
			break;
		}
		case NSWS_ID_URLBAR:
			/* main message should already have been handled */
			break;
		default:
			break;
		}
		break;
	}
	case WM_CREATE:
	{
		HDC hdc = GetDC(hwnd);
		int dpi = GetDeviceCaps(hdc,LOGPIXELSY);
		if (dpi > 10)
			nscss_screen_dpi = INTTOFIX(dpi);
		ReleaseDC(hwnd, hdc);
		return DefWindowProc(hwnd, msg, wparam, lparam);
		break;
	}

	case WM_PAINT:
	{
		DWORD ret = DefWindowProc(hwnd, msg, wparam, lparam);
		if (historyactive)
			current_gui = NULL;
		return ret;
	}

	case WM_SIZE:
		SendMessage(w->statusbar, WM_SIZE, wparam, lparam);
		SendMessage(w->toolbar, WM_SIZE, wparam, lparam);
		nsws_window_resize(w, hwnd, wparam, lparam);
		return DefWindowProc(hwnd, msg, wparam, lparam);

	case WM_CLOSE:
		if (--open_windows == 0) {
			netsurf_quit = true;
		}
		browser_window_destroy(w->bw);
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

	default:
		return DefWindowProc(hwnd, msg, wparam, lparam);
	}
	return 0;
}


static void create_local_windows_classes(void) {
	WNDCLASSEX w;

	w.cbSize	= sizeof(WNDCLASSEX);
	w.style		= 0;
	w.lpfnWndProc	= nsws_window_event_callback;
	w.cbClsExtra	= 0;
	w.cbWndExtra	= 0;
	w.hInstance	= hinstance;
	w.hIcon		= LoadIcon(NULL, IDI_APPLICATION); /* -> NetSurf */
	w.hCursor	= LoadCursor(NULL, IDC_ARROW);
	w.hbrBackground	= (HBRUSH)(COLOR_MENU + 1);
	w.lpszMenuName	= NULL;
	w.lpszClassName = windowclassname_main;
	w.hIconSm	= LoadIcon(NULL, IDI_APPLICATION); /* -> NetSurf */
	RegisterClassEx(&w);

	w.lpfnWndProc	= nsws_window_drawable_event_callback;
	w.hIcon		= NULL;
	w.lpszMenuName	= NULL;
	w.lpszClassName = windowclassname_drawable;
	w.hIconSm	= NULL;

	RegisterClassEx(&w);

}

/**
 * creation of status bar
 */
static void nsws_window_statusbar_create(struct gui_window *w)
{
	HWND hwnd = CreateWindowEx(0, STATUSCLASSNAME, NULL, WS_CHILD |
				   WS_VISIBLE, 0, 0, 0, 0, w->main,
				   (HMENU) NSWS_ID_STATUSBAR, hinstance, NULL);
	SendMessage(hwnd, SB_SETTEXT, 0, (LPARAM)"NetSurf");
	w->statusbar = hwnd;
}


/**
 * creation of a new full browser window
 */
static HWND nsws_window_create(struct gui_window *gw)
{
	HWND hwnd;
	INITCOMMONCONTROLSEX icc;

	LOG(("nsws_window_create %p", gw));

	icc.dwSize = sizeof(icc);
	icc.dwICC = ICC_BAR_CLASSES | ICC_WIN95_CLASSES;
#if WINVER > 0x0501
	icc.dwICC |= ICC_STANDARD_CLASSES;
#endif
	InitCommonControlsEx(&icc);

	gw->mainmenu = LoadMenu(hinstance, MAKEINTRESOURCE(NSWS_ID_MAINMENU));
	gw->rclick = LoadMenu(hinstance, MAKEINTRESOURCE(NSWS_ID_CTXMENU));

	LOG(("creating window for hInstance %p", hinstance));
	hwnd = CreateWindowEx(0,
			      windowclassname_main,
			    "NetSurf Browser",
			    WS_OVERLAPPEDWINDOW |
			    WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CS_DBLCLKS,
			    CW_USEDEFAULT,
			    CW_USEDEFAULT,
			    gw->width,
			    gw->height,
			    NULL,
			    gw->mainmenu,
			    hinstance,
			    NULL);

	if ((option_window_width >= 100) &&
	    (option_window_height >= 100) &&
	    (option_window_x >= 0) &&
	    (option_window_y >= 0))
		SetWindowPos(hwnd, HWND_TOPMOST, option_window_x,
			     option_window_y, option_window_width,
			     option_window_height, SWP_SHOWWINDOW);

	nsws_window_set_accels(gw);
	nsws_window_set_ico(gw);
	gw->toolbar = nsws_window_toolbar_create(gw, hwnd);
	nsws_window_statusbar_create(gw);

	return hwnd;
}

/**
 * create a new gui_window to contain a browser_window
 * \param bw the browser_window to connect to the new gui_window
 */
struct gui_window *
gui_create_browser_window(struct browser_window *bw,
			  struct browser_window *clone,
			  bool new_tab)
{
	struct gui_window *gw;

	gw = calloc(1, sizeof(struct gui_window));

	if (gw == NULL)
		return NULL;

	/* connect gui window to browser window */
	gw->bw = bw;

	gw->width = 800;
	gw->height = 600;
	gw->toolbuttonsize = 24; 
	gw->requestscrollx = 0;
	gw->requestscrolly = 0;
	gw->localhistory = NULL;

	gw->mouse = malloc(sizeof(struct browser_mouse));
	if (gw->mouse == NULL) {
		free(gw);
		return NULL;
	}
	gw->mouse->gui = gw;
	gw->mouse->state = 0;
	gw->mouse->pressed_x = 0;
	gw->mouse->pressed_y = 0;

        /* add window to list */
	if (window_list != NULL)
		window_list->prev = gw;
	gw->next = window_list;
	window_list = gw;

	switch(bw->browser_window_type) {
	case BROWSER_WINDOW_NORMAL:
		gw->main = nsws_window_create(gw);
		gw->drawingarea = CreateWindow(windowclassname_drawable,
					      NULL,
					      WS_VISIBLE | WS_CHILD,
					      0,  0,  0,  0,
					      gw->main,
					      NULL,
					      hinstance,
					      NULL);

		/* set the gui window associated with this toolbar */
		SetProp(gw->drawingarea, TEXT("GuiWnd"), (HANDLE)gw);

		input_window = gw;
		open_windows++;
		ShowWindow(gw->main, SW_SHOWNORMAL);
		break;

	case BROWSER_WINDOW_FRAME:
		gw->drawingarea = CreateWindow(windowclassname_drawable,
					      NULL,
					      WS_VISIBLE | WS_CHILD,
					      0,  0,  0,  0,
					      bw->parent->window->drawingarea,
					      NULL,
					      hinstance,
					      NULL);
		/* set the gui window associated with this toolbar */
		SetProp(gw->drawingarea, TEXT("GuiWnd"), (HANDLE)gw);

		ShowWindow(gw->drawingarea, SW_SHOWNORMAL);
		LOG(("create frame"));
		break;

	case BROWSER_WINDOW_FRAMESET:
		LOG(("create frameset"));
		break;

	case BROWSER_WINDOW_IFRAME:
		LOG(("create iframe"));
		gw->drawingarea = CreateWindow(windowclassname_drawable,
					      NULL,
					      WS_VISIBLE | WS_CHILD,
					      0, 0, 0, 0,
					      bw->parent->window->drawingarea,
					      NULL,
					      hinstance,
					      NULL);

		/* set the gui window associated with this toolbar */
		SetProp(gw->drawingarea, TEXT("GuiWnd"), (HANDLE)gw);

		ShowWindow(gw->drawingarea, SW_SHOWNORMAL);
		break;

	default:
		LOG(("unhandled type"));
	}

	return gw;
}




HICON nsws_window_get_ico(bool large)
{
	return large ? hIcon : hIconS;
}




/**
 * cache pointers for quick swapping
 */
static void nsws_window_init_pointers(void)
{
	nsws_pointer.hand = LoadCursor(NULL, IDC_HAND);
	nsws_pointer.ibeam = LoadCursor(NULL, IDC_IBEAM);
	nsws_pointer.cross = LoadCursor(NULL, IDC_CROSS);
	nsws_pointer.sizeall = LoadCursor(NULL, IDC_SIZEALL);
	nsws_pointer.sizewe = LoadCursor(NULL, IDC_SIZEWE);
	nsws_pointer.sizens = LoadCursor(NULL, IDC_SIZENS);
	nsws_pointer.sizenesw = LoadCursor(NULL, IDC_SIZENESW);
	nsws_pointer.sizenwse = LoadCursor(NULL, IDC_SIZENWSE);
	nsws_pointer.wait = LoadCursor(NULL, IDC_WAIT);
	nsws_pointer.appstarting = LoadCursor(NULL, IDC_APPSTARTING);
	nsws_pointer.no = LoadCursor(NULL, IDC_NO);
	nsws_pointer.help = LoadCursor(NULL, IDC_HELP);
	nsws_pointer.arrow = LoadCursor(NULL, IDC_ARROW);
}



HWND gui_window_main_window(struct gui_window *w)
{
	if (w == NULL)
		return NULL;
	return w->main;
}

HWND gui_window_toolbar(struct gui_window *w)
{
	if (w == NULL)
		return NULL;
	return w->toolbar;
}

HWND gui_window_urlbar(struct gui_window *w)
{
	if (w == NULL)
		return NULL;
	return w->urlbar;
}

HWND gui_window_statusbar(struct gui_window *w)
{
	if (w == NULL)
		return NULL;
	return w->statusbar;
}

HWND gui_window_drawingarea(struct gui_window *w)
{
	if (w == NULL)
		return NULL;
	return w->drawingarea;
}

struct nsws_localhistory *gui_window_localhistory(struct gui_window *w)
{
	if (w == NULL)
		return NULL;
	return w->localhistory;
}

void gui_window_set_localhistory(struct gui_window *w,
				 struct nsws_localhistory *l)
{
	if (w != NULL)
		w->localhistory = l;
}

RECT *gui_window_redraw_rect(struct gui_window *w)
{
	if (w == NULL)
		return NULL;
	return &(w->redraw);
}

RECT *gui_window_clip_rect(struct gui_window *w)
{
	if (w == NULL)
		return NULL;
	return &(w->clip);
}

int gui_window_width(struct gui_window *w)
{
	if (w == NULL)
		return 0;
	return w->width;
}

int gui_window_height(struct gui_window *w)
{
	if (w == NULL)
		return 0;
	return w->height;
}

int gui_window_scrollingx(struct gui_window *w)
{
	if (w == NULL)
		return 0;
	return w->requestscrollx;
}

int gui_window_scrollingy(struct gui_window *w)
{
	if (w == NULL)
		return 0;
	return w->requestscrolly;
}

struct gui_window *gui_window_iterate(struct gui_window *w)
{
	if (w == NULL)
		return NULL;
	return w->next;
}

struct browser_window *gui_window_browser_window(struct gui_window *w)
{
	if (w == NULL)
		return NULL;
	return w->bw;
}

/**
 * window cleanup code
 */
void gui_window_destroy(struct gui_window *w)
{
	if (w == NULL)
		return;

	if (w->prev != NULL)
		w->prev->next = w->next;
	else
		window_list = w->next;

	if (w->next != NULL)
		w->next->prev = w->prev;

	DestroyAcceleratorTable(w->acceltable);

	DestroyWindow(w->main);

	free(w);
	w = NULL;
}

/**
 * set window title
 * \param title the [url]
 */
void gui_window_set_title(struct gui_window *w, const char *title)
{
	if (w == NULL)
		return;
	LOG(("%p, title %s", w, title));
	char *fulltitle = malloc(strlen(title) +
				 SLEN("  -  NetSurf") + 1);
	if (fulltitle == NULL) {
		warn_user("NoMemory", 0);
		return;
	}
	strcpy(fulltitle, title);
	strcat(fulltitle, "  -  NetSurf");
	SendMessage(w->main, WM_SETTEXT, 0, (LPARAM)fulltitle);
	free(fulltitle);
}

/**
 * redraw a rectangle of the window
 */
void gui_window_redraw(struct gui_window *w, int x0, int y0, int x1, int y1)
{
	RECT redrawrect;

	LOG(("redraw %p %d,%d %d,%d", w, x0, y0, x1, y1));
	if (w == NULL)
		return;
	
	redrawrect.left = x0;
	redrawrect.top = y0;
	redrawrect.right = x1;
	redrawrect.bottom = y1;

	RedrawWindow(w->drawingarea, &redrawrect, NULL, RDW_INVALIDATE | RDW_NOERASE);
}

/**
 * redraw the whole window
 */
void gui_window_redraw_window(struct gui_window *gw)
{
	LOG(("redraw window %p", gw));
	if (gw == NULL)
		return;

	RedrawWindow(gw->drawingarea, NULL, NULL, RDW_INVALIDATE | RDW_NOERASE);
}

void gui_window_update_box(struct gui_window *gw,
			   const union content_msg_data *data)
{
	if (gw == NULL)
		return;

	RECT redrawrect;
	
	redrawrect.left = (long)data->redraw.x;
	redrawrect.top = (long)data->redraw.y;
	redrawrect.right =(long)(data->redraw.x + data->redraw.width);
	redrawrect.bottom = (long)(data->redraw.y + data->redraw.height);

	RedrawWindow(gw->drawingarea, &redrawrect, NULL, RDW_INVALIDATE | RDW_NOERASE);

}

bool gui_window_get_scroll(struct gui_window *w, int *sx, int *sy)
{
	LOG(("get scroll"));
	if (w == NULL)
		return false;
	*sx = w->scrollx;
	*sy = w->scrolly;

	return true;
}

/**
 * scroll the window
 * \param sx the new 'absolute' scroll location
 * \param sy the new 'absolute' scroll location
 */
void gui_window_set_scroll(struct gui_window *w, int sx, int sy)
{
	SCROLLINFO si;
	POINT p;

	if ((w == NULL) ||
	    (w->bw == NULL) ||
	    (w->bw->current_content == NULL))
		return;

	/* limit scale range */
	if (abs(w->bw->scale - 0.0) < 0.00001)
		w->bw->scale = 1.0;

	w->requestscrollx = sx - w->scrollx;
	w->requestscrolly = sy - w->scrolly;

	/* set the vertical scroll offset */
	si.cbSize = sizeof(si);
	si.fMask = SIF_ALL;
	si.nMin = 0;
	si.nMax = (content_get_height(w->bw->current_content) * w->bw->scale) - 1;
	si.nPage = w->height;
	si.nPos = MAX(w->scrolly + w->requestscrolly, 0);
	si.nPos = MIN(si.nPos, content_get_height(w->bw->current_content) * w->bw->scale - w->height);
	SetScrollInfo(w->drawingarea, SB_VERT, &si, TRUE);
	LOG(("SetScrollInfo VERT min:%d max:%d page:%d pos:%d", si.nMin, si.nMax, si.nPage, si.nPos));

	/* set the horizontal scroll offset */
	si.cbSize = sizeof(si);
	si.fMask = SIF_ALL;
	si.nMin = 0;
	si.nMax = (content_get_width(w->bw->current_content) * w->bw->scale) -1;
	si.nPage = w->width;
	si.nPos = MAX(w->scrollx + w->requestscrollx, 0);
	si.nPos = MIN(si.nPos, content_get_width(w->bw->current_content) * w->bw->scale - w->width);
	SetScrollInfo(w->drawingarea, SB_HORZ, &si, TRUE);
	LOG(("SetScrollInfo HORZ min:%d max:%d page:%d pos:%d", si.nMin, si.nMax, si.nPage, si.nPos));

	/* Set caret position */
	GetCaretPos(&p);
	HideCaret(w->drawingarea);
	SetCaretPos(p.x - w->requestscrollx, p.y - w->requestscrolly);
	ShowCaret(w->drawingarea);

	RECT r, redraw;
	r.top = 0;
	r.bottom = w->height + 1;
	r.left = 0;
	r.right = w->width + 1;
	ScrollWindowEx(w->drawingarea, - w->requestscrollx, - w->requestscrolly, &r, NULL, NULL, &redraw, SW_INVALIDATE);
	w->scrolly += w->requestscrolly;
	w->scrollx += w->requestscrollx;
	w->requestscrollx = 0;
	w->requestscrolly = 0;

}

void gui_window_scroll_visible(struct gui_window *w, int x0, int y0,
			       int x1, int y1)
{
	LOG(("scroll visible %s:(%p, %d, %d, %d, %d)", __func__, w, x0,
	     y0, x1, y1));
}

void gui_window_position_frame(struct gui_window *w, int x0, int y0,
			       int x1, int y1)
{
	LOG(("position frame %s: %d, %d, %d, %d", w->bw->name,
	     x0, y0, x1, y1));
	MoveWindow(w->drawingarea, x0, y0, x1-x0, y1-y0, true);
}

void gui_window_get_dimensions(struct gui_window *w, int *width, int *height,
			       bool scaled)
{
	LOG(("get dimensions %p w=%d h=%d", w, w->width, w->height));
	if (w == NULL)
		return;
	*width = w->width;
	*height = w->height;
}

void gui_window_update_extent(struct gui_window *w)
{

}

/**
 * set the status bar message
 */
void gui_window_set_status(struct gui_window *w, const char *text)
{
	if (w == NULL)
		return;
	SendMessage(w->statusbar, WM_SETTEXT, 0, (LPARAM)text);
}

/**
 * set the pointer shape
 */
void gui_window_set_pointer(struct gui_window *w, gui_pointer_shape shape)
{
	if (w == NULL)
		return;
	switch (shape) {
	case GUI_POINTER_POINT: /* link */
	case GUI_POINTER_MENU:
		SetCursor(nsws_pointer.hand);
		break;

	case GUI_POINTER_CARET: /* input */
		SetCursor(nsws_pointer.ibeam);
		break;

	case GUI_POINTER_CROSS:
		SetCursor(nsws_pointer.cross);
		break;

	case GUI_POINTER_MOVE:
		SetCursor(nsws_pointer.sizeall);
		break;

	case GUI_POINTER_RIGHT:
	case GUI_POINTER_LEFT:
		SetCursor(nsws_pointer.sizewe);
		break;

	case GUI_POINTER_UP:
	case GUI_POINTER_DOWN:
		SetCursor(nsws_pointer.sizens);
		break;

	case GUI_POINTER_RU:
	case GUI_POINTER_LD:
		SetCursor(nsws_pointer.sizenesw);
		break;

	case GUI_POINTER_RD:
	case GUI_POINTER_LU:
		SetCursor(nsws_pointer.sizenwse);
		break;

	case GUI_POINTER_WAIT:
		SetCursor(nsws_pointer.wait);
		break;

	case GUI_POINTER_PROGRESS:
		SetCursor(nsws_pointer.appstarting);
		break;

	case GUI_POINTER_NO_DROP:
	case GUI_POINTER_NOT_ALLOWED:
		SetCursor(nsws_pointer.no);
		break;

	case GUI_POINTER_HELP:
		SetCursor(nsws_pointer.help);
		break;

	default:
		SetCursor(nsws_pointer.arrow);
		break;
	}
}

struct nsws_pointers *nsws_get_pointers(void)
{
	return &nsws_pointer;
}

void gui_window_hide_pointer(struct gui_window *w)
{
}

void gui_window_set_url(struct gui_window *w, const char *url)
{
	if (w == NULL)
		return;
	SendMessage(w->urlbar, WM_SETTEXT, 0, (LPARAM) url);
}


void gui_window_start_throbber(struct gui_window *w)
{
	if (w == NULL)
		return;
	nsws_window_update_forward_back(w);

	if (w->mainmenu != NULL) {
		EnableMenuItem(w->mainmenu, NSWS_ID_NAV_STOP, MF_ENABLED);
		EnableMenuItem(w->mainmenu, NSWS_ID_NAV_RELOAD, MF_GRAYED);
	}
	if (w->rclick != NULL) {
		EnableMenuItem(w->rclick, NSWS_ID_NAV_STOP, MF_ENABLED);
		EnableMenuItem(w->rclick, NSWS_ID_NAV_RELOAD, MF_GRAYED);
	}
	if (w->toolbar != NULL) {
		SendMessage(w->toolbar, TB_SETSTATE, (WPARAM) NSWS_ID_NAV_STOP,
			    MAKELONG(TBSTATE_ENABLED, 0));
		SendMessage(w->toolbar, TB_SETSTATE,
			    (WPARAM) NSWS_ID_NAV_RELOAD,
			    MAKELONG(TBSTATE_INDETERMINATE, 0));
	}
	w->throbbing = true;
	Animate_Play(w->throbber, 0, -1, -1);
}

void gui_window_stop_throbber(struct gui_window *w)
{
	if (w == NULL)
		return;
	nsws_window_update_forward_back(w);
	if (w->mainmenu != NULL) {
		EnableMenuItem(w->mainmenu, NSWS_ID_NAV_STOP, MF_GRAYED);
		EnableMenuItem(w->mainmenu, NSWS_ID_NAV_RELOAD, MF_ENABLED);
	}
	if (w->rclick != NULL) {
		EnableMenuItem(w->rclick, NSWS_ID_NAV_STOP, MF_GRAYED);
		EnableMenuItem(w->rclick, NSWS_ID_NAV_RELOAD, MF_ENABLED);
	}
	if (w->toolbar != NULL) {
		SendMessage(w->toolbar, TB_SETSTATE, (WPARAM) NSWS_ID_NAV_STOP,
			    MAKELONG(TBSTATE_INDETERMINATE, 0));
		SendMessage(w->toolbar, TB_SETSTATE,
			    (WPARAM) NSWS_ID_NAV_RELOAD,
			    MAKELONG(TBSTATE_ENABLED, 0));
	}
	w->throbbing = false;
	Animate_Stop(w->throbber);
	Animate_Seek(w->throbber, 0);
}

/**
 * place caret in window
 */
void gui_window_place_caret(struct gui_window *w, int x, int y, int height)
{
	if (w == NULL)
		return;
	CreateCaret(w->drawingarea, (HBITMAP)NULL, 1, height * w->bw->scale);
	SetCaretPos(x * w->bw->scale - w->scrollx,
		    y * w->bw->scale - w->scrolly);
	ShowCaret(w->drawingarea);
}

/**
 * clear window caret
 */
void
gui_window_remove_caret(struct gui_window *w)
{
	if (w == NULL)
		return;
	HideCaret(w->drawingarea);
}

void
gui_window_set_icon(struct gui_window *g, hlcache_handle *icon)
{
}

void
gui_window_set_search_ico(hlcache_handle *ico)
{
}

bool
save_complete_gui_save(const char *path,
		       const char *filename,
		       size_t len,
		       const char *sourcedata,
		       content_type type)
{
	return false;
}

int
save_complete_htmlSaveFileFormat(const char *path,
				 const char *filename,
				 xmlDocPtr cur,
				 const char *encoding,
				 int format)
{
	return 0;
}


void gui_window_new_content(struct gui_window *w)
{
}

bool gui_window_scroll_start(struct gui_window *w)
{
	return true;
}

bool gui_window_box_scroll_start(struct gui_window *w,
				 int x0, int y0, int x1, int y1)
{
	return true;
}

bool gui_window_frame_resize_start(struct gui_window *w)
{
	LOG(("resize frame\n"));
	return true;
}

void gui_window_save_link(struct gui_window *g, const char *url, 
		const char *title)
{
}

void gui_window_set_scale(struct gui_window *w, float scale)
{
	if (w == NULL)
		return;
	w->scale = scale;
	LOG(("%.2f\n", scale));
}

void gui_drag_save_object(gui_save_type type, hlcache_handle *c,
			  struct gui_window *w)
{
}

void gui_drag_save_selection(struct selection *s, struct gui_window *w)
{
}

void gui_start_selection(struct gui_window *w)
{
}

void gui_clear_selection(struct gui_window *w)
{
}

void gui_paste_from_clipboard(struct gui_window *w, int x, int y)
{
	HANDLE clipboard_handle;
	char *content;

	clipboard_handle = GetClipboardData(CF_TEXT);
	if (clipboard_handle != NULL) {
		content = GlobalLock(clipboard_handle);
		LOG(("pasting %s", content));
		GlobalUnlock(clipboard_handle);
	}
}

bool gui_empty_clipboard(void)
{
	return false;
}

bool gui_add_to_clipboard(const char *text, size_t length, bool space)
{
	HANDLE hnew;
	char *new, *original;
	HANDLE h = GetClipboardData(CF_TEXT);
	if (h == NULL)
		original = (char *)"";
	else
		original = GlobalLock(h);

	size_t len = strlen(original) + 1;
	hnew = GlobalAlloc(GHND, length + len);
	new = (char *)GlobalLock(hnew);
	snprintf(new, length + len, "%s%s", original, text);

	if (h != NULL) {
		GlobalUnlock(h);
		EmptyClipboard();
	}
	GlobalUnlock(hnew);
	SetClipboardData(CF_TEXT, hnew);
	return true;
}

bool gui_commit_clipboard(void)
{
	return false;
}

static bool
gui_selection_traverse_handler(const char *text,
			       size_t length,
			       struct box *box,
			       void *handle,
			       const char *space_text,
			       size_t space_length)
{
	if (space_text) {
		if (!gui_add_to_clipboard(space_text, space_length, false)) {
			return false;
		}
	}

	if (!gui_add_to_clipboard(text, length, box->space))
		return false;

	return true;
}

bool gui_copy_to_clipboard(struct selection *s)
{
	if ((s->defined) && (s->bw != NULL) && (s->bw->window != NULL) &&
	    (s->bw->window->main != NULL)) {
		OpenClipboard(s->bw->window->main);
		EmptyClipboard();
		if (selection_traverse(s, gui_selection_traverse_handler,
				       NULL)) {
			CloseClipboard();
			return true;
		}
	}
	return false;
}


void gui_create_form_select_menu(struct browser_window *bw,
				 struct form_control *control)
{
}

void gui_launch_url(const char *url)
{
}

void gui_cert_verify(const char *url, const struct ssl_cert_info *certs, 
		unsigned long num, 
		nserror (*cb)(bool proceed, void *pw), void *cbpw)
{
	cb(false, cbpw);
}


void gui_quit(void)
{
	LOG(("gui_quit"));

	hubbub_finalise(ns_realloc, NULL);
}

static void gui_init(int argc, char** argv)
{
	char buf[PATH_MAX], sbuf[PATH_MAX];
	int len;

	LOG(("argc %d, argv %p", argc, argv));

	nsws_find_resource(buf, "Aliases", "./windows/res/Aliases");
	LOG(("Using '%s' as Aliases file", buf));

	hubbub_error he = hubbub_initialise(buf, ns_realloc, NULL);
	LOG(("hubbub init %d", he));
	if (he != HUBBUB_OK)
		die("Unable to initialise HTML parsing library.\n");

	/* set up stylesheet urls */
	getcwd(sbuf, PATH_MAX);
	len = strlen(sbuf);
	strncat(sbuf, "windows/res/default.css", PATH_MAX - len);
	nsws_find_resource(buf, "default.css", sbuf);
	default_stylesheet_url = path_to_url(buf);
	LOG(("Using '%s' as Default CSS URL", default_stylesheet_url));

	getcwd(sbuf, PATH_MAX);
	len = strlen(sbuf);
	strncat(sbuf, "windows/res/quirks.css", PATH_MAX - len);
	nsws_find_resource(buf, "quirks.css", sbuf);
	quirks_stylesheet_url = path_to_url(buf);
	LOG(("Using '%s' as quirks stylesheet url", quirks_stylesheet_url ));


	create_local_windows_classes();

	option_target_blank = false;

}

static void gui_init2(int argc, char** argv)
{
	struct browser_window *bw;
	const char *addr = NETSURF_HOMEPAGE;

	nsws_window_init_pointers();
	LOG(("argc %d, argv %p", argc, argv));

	if (argc > 1)
		addr = argv[1];
	else if (option_homepage_url != NULL && option_homepage_url[0]
		 != '\0')
		addr = option_homepage_url;
	else
		addr = default_page;

	LOG(("calling browser_window_create"));
	bw = browser_window_create(addr, 0, 0, true, false);
}

void gui_stdout(void)
{
	/* mwindows compile flag normally invalidates stdout unless
	   already redirected */
	if (_get_osfhandle(fileno(stdout)) == -1) {
		AllocConsole();
		freopen("CONOUT$", "w", stdout);
	}
}

/* OS program entry point */
int WINAPI
WinMain(HINSTANCE hInstance, HINSTANCE hLastInstance, LPSTR lpcli, int ncmd)
{
	char **argv = NULL;
	int argc = 0, argctemp = 0;
	size_t len;
	LPWSTR * argvw;
	char options[PATH_MAX];
	char messages[PATH_MAX];

	if (SLEN(lpcli) > 0) {
		argvw = CommandLineToArgvW(GetCommandLineW(), &argc);
	}

	hinstance = hInstance;
	setbuf(stderr, NULL);

	/* Construct a unix style argc/argv */
	argv = malloc(sizeof(char *) * argc);
	while (argctemp < argc) {
		len = wcstombs(NULL, argvw[argctemp], 0) + 1;
		if (len > 0) {
			argv[argctemp] = malloc(len);
		}

		if (argv[argctemp] != NULL) {
			wcstombs(argv[argctemp], argvw[argctemp], len);
			/* alter windows-style forward slash flags to
			 * hyphen flags.
			 */
			if (argv[argctemp][0] == '/')
				argv[argctemp][0] = '-';
		}
		argctemp++;
	}

	/* load browser messages */
	nsws_find_resource(messages, "messages", "./windows/res/messages");

	/* load browser options */
	nsws_find_resource(options, "preferences", "~/.netsurf/preferences");
	options_file_location = strdup(options);

	/* initialise netsurf */
	netsurf_init(&argc, &argv, options, messages);

	gui_init(argc, argv);

	gui_init2(argc, argv);

	netsurf_main_loop();

	netsurf_exit();

	return 0;
}
