#include "precomp_wasabi_bfc.h"

#include "wasabi_std_wnd.h"
#include "api.h"
#include <api/wnd/api_window.h>
#include <api/wnd/wndevent.h>
#include <api/wnd/wndevent.h>
#include <shobjidl.h>
#include "../winamp/wa_ipc.h"

#ifndef AC_SRC_ALPHA
const int AC_SRC_ALPHA = 1;
#endif

#ifndef THBN_CLICKED
#define THBN_CLICKED 0x1800
#endif

static int nreal = 0;

#ifdef __APPLE__
OSStatus MyWindowEventHandler(EventHandlerCallRef	inHandlerCallRef, EventRef inEvent, void *inUserData);
OSStatus MyControlEventHandler(EventHandlerCallRef	inHandlerCallRef, EventRef inEvent, void *inUserData);
#endif

#ifdef _WIN32
static HINSTANCE gdi32instance = NULL;

static int(WINAPI *getRandomRgn)(HDC dc, HRGN rgn, int i) = NULL;

static int grrfailed = 0;

static void register_wndClass(HINSTANCE hInstance);

static int versionChecked = 0;
static int isNT = 0;

static int IsNT()
{
	if (versionChecked)
		return isNT;
  
	if (GetVersion() < 0x80000000)
		isNT = 1;
  
	versionChecked = 1;
	return isNT;
}

int  Wasabi::Std::Wnd::alphaStretchBlit(HDC destHDC, int dstx, int dsty, int dstw, int dsth, HDC sourceHDC, int srcx, int srcy, int srcw, int srch)
{
	if (IsNT())
	{
		SetStretchBltMode(destHDC, HALFTONE);
		StretchBlt(destHDC, dstx, dsty, dstw, dsth, sourceHDC, srcx , srcy, srcw, srch, SRCCOPY);
		return 1;
	}
	else
		return 0;
}
#endif

#ifdef __APPLE__
enum
{
	kWasabi	= 'WASA'
};

const ControlID	kWasabiID	= { kWasabi, 0 };

void GetWasabiHIView(WindowRef window, HIViewRef *control)
{
  GetControlByID(window, &kWasabiID, control);
}


OSStatus CreateHIView ( WindowRef inWindow, const Rect* inBounds,
                        ControlRef* outControl )
{
  OSStatus            err;
  ControlRef          root;
  EventRef            event;
  
  
  // Make an initialization event
  err = CreateEvent( NULL, kEventClassHIObject, kEventHIObjectInitialize,
                     GetCurrentEventTime(), 0, &event );
  require_noerr( err, CantCreateEvent );
  
  // If bounds were specified, push the them into the initialization event
  // so that they can be used in the initialization handler.
  if ( inBounds != NULL )
  {
    err = SetEventParameter( event, 'boun', typeQDRectangle,
                             sizeof( Rect ), inBounds );
    require_noerr( err, CantSetParameter );
  }
  
  err = HIObjectCreate( kHIViewClassID, event, (HIObjectRef*)
                        outControl );
  require_noerr( err, CantCreate );
  
  
  // If a parent window was specified, place the new view into the
  // parent window.
  if ( inWindow != NULL )
  {
    err = GetRootControl( inWindow, &root );
    require_noerr( err, CantGetRootControl );
    
    
    err = HIViewAddSubview( root, *outControl );
    SetControlID( *outControl, &kWasabiID );
    HIViewSetVisible(*outControl, true);
  }
  
  
CantCreate:
CantGetRootControl:
CantSetParameter:
CantCreateEvent:
    ReleaseEvent( event );
  
  
CantRegister:
    return err;
}
#endif

OSWINDOWHANDLE  Wasabi::Std::Wnd::createWnd(RECT *r, int nochild, int acceptdrops, OSWINDOWHANDLE parent, OSMODULEHANDLE module, ifc_window *rw)
{
#ifdef _WIN32
	register_wndClass(module);
  
  int style = WS_CLIPSIBLINGS | WS_CLIPCHILDREN;
  int exstyle=0;

  if (parent == NULL) {
    exstyle |= WS_EX_TOOLWINDOW;
    style |= WS_POPUP;
  } else
    style |= WS_CHILD;

  if (nochild) style=WS_POPUP | WS_CLIPSIBLINGS | WS_CLIPCHILDREN;

  if (acceptdrops) exstyle |= WS_EX_ACCEPTFILES;
  
	HWND ret = CreateWindowExW(exstyle, BASEWNDCLASSNAME, NULL, style,
	                          r->left, r->top, r->right - r->left, r->bottom - r->top, parent, NULL, module, (LPVOID)rw);
	if (ret != INVALIDOSWINDOWHANDLE)
	{
		nreal++;
		if(NULL != WASABI_API_APP && 0 != (WS_POPUP & style))
			WASABI_API_APP->app_registerGlobalWindow(ret);
	}
	return ret;
#elif defined(__APPLE__)
	Rect wndRect;
	SetRect(&wndRect, r->left, r->top, r->right, r->bottom);
	WindowRef newWnd;
	CreateNewWindow(kOverlayWindowClass,  kWindowCompositingAttribute, &wndRect, &newWnd);
	SetWindowGroup(newWnd, GetWindowGroupOfClass(kDocumentWindowClass));
	// install a window event handler
	const EventTypeSpec	windowEventList[] =
  {
  {
    kEventClassCommand, kEventProcessCommand
  },
//      { kEventClassWindow, kEventWindowBoundsChanging },
  { kEventClassWindow, kEventWindowBoundsChanged },
//  { kEventClassWindow,    kEventWindowInit },
    
  {kEventClassMouse, kEventMouseDown},
  {kEventClassMouse, kEventMouseUp},
  {kEventClassMouse, kEventMouseMoved},
  {kEventClassMouse, kEventMouseDragged},
    //{kEventClassMouse, kEventMouseEntered},
    //{kEventClassMouse, kEventMouseExited},
    //{kEventClassMouse, kEventMouseWheelMoved},
    
  {kEventClassKeyboard, kEventRawKeyDown},
  {kEventClassKeyboard, kEventRawKeyUp},
        
  };
	InstallWindowEventHandler(newWnd, MyWindowEventHandler, 	GetEventTypeCount(windowEventList), windowEventList, rw, NULL);

  // create the content view
  HIViewRef myHIView;
  CreateHIView(newWnd,&wndRect,(ControlRef*)&myHIView);
  const EventTypeSpec	controlEventList[] =
  {
  {    kEventClassControl, kEventControlDraw},
  {    kEventClassControl, kEventControlApplyBackground},
  };

  InstallEventHandler(GetControlEventTarget(myHIView), MyControlEventHandler, GetEventTypeCount(controlEventList), controlEventList, rw, NULL);  

	return newWnd;
#endif
}

void  Wasabi::Std::Wnd::destroyWnd(OSWINDOWHANDLE wnd)
{
#ifdef _WIN32

	if(NULL != WASABI_API_APP && 0 != (WS_POPUP & GetWindowLongPtr(wnd, GWL_STYLE)))
		WASABI_API_APP->app_unregisterGlobalWindow(wnd);

	DestroyWindow(wnd);
	nreal--;
	if (nreal == 0)
	{
		if (gdi32instance) FreeLibrary(gdi32instance);
    
		gdi32instance = NULL;
		getRandomRgn = NULL;
	}
#elif defined(__APPLE__)
	DisposeWindow(wnd);
#endif
}

int  Wasabi::Std::Wnd::isValidWnd(OSWINDOWHANDLE wnd)
{
#ifdef _WIN32
	return IsWindow(wnd);
#elif defined(__APPLE__)
	// TODO: docs suggest that this function is slow
  if (!wnd)
    return 0;
	return IsValidWindowPtr(wnd);
#endif
}

#ifdef _WIN32
void  Wasabi::Std::Wnd::setLayeredWnd(OSWINDOWHANDLE wnd, int layered)
{
	if (layered)
	{
		// have to clear and reset, can't just set
		SetWindowLong(wnd, GWL_EXSTYLE, GetWindowLong(wnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
		SetWindowLong(wnd, GWL_EXSTYLE, GetWindowLong(wnd, GWL_EXSTYLE) | WS_EX_LAYERED);
	}
	else
	{
		SetWindowLong(wnd, GWL_EXSTYLE, GetWindowLong(wnd, GWL_EXSTYLE) & ~WS_EX_LAYERED);
	}
}

int  Wasabi::Std::Wnd::isLayeredWnd(OSWINDOWHANDLE wnd)
{
	DWORD dwLong = GetWindowLong(wnd, GWL_EXSTYLE);
	return !!(dwLong & WS_EX_LAYERED);
}



void  Wasabi::Std::Wnd::setLayeredAlpha(OSWINDOWHANDLE wnd, int amount)
{
	if (!isDesktopAlphaAvailable()) return;
	SetLayeredWindowAttributes(wnd, RGB(0, 0, 0), amount, LWA_ALPHA);
}


void  Wasabi::Std::Wnd::updateLayeredWnd(OSWINDOWHANDLE wnd, int x, int y, int w, int h, HDC surfdc, int alpha)
{
	if (!isDesktopAlphaAvailable()) return;
	BLENDFUNCTION blend = {AC_SRC_OVER, 0, (BYTE)alpha, AC_SRC_ALPHA };
	POINT sp = {x, y}, pt = {0, 0};
	SIZE ss = { w, h };
	//HDC sysdc = GetDC(NULL);
	UpdateLayeredWindow(wnd, NULL/*sysdc*/, &sp, &ss, surfdc, &pt, 0, &blend, ULW_ALPHA);
	//ReleaseDC(NULL, sysdc);
}
#endif

void  Wasabi::Std::Wnd::setWndPos(OSWINDOWHANDLE wnd, OSWINDOWHANDLE zorder, int x, int y, int w, int h,
                       int nozorder, int noactive, int nocopybits, int nomove, int noresize)
{
#ifdef _WIN32
	SetWindowPos(wnd, zorder, x, y, w, h,
	             SWP_DEFERERASE |		// we ignore WM_SYNCPAINT anyway
	             (nozorder ? SWP_NOZORDER : 0) |
	             (noactive ? SWP_NOACTIVATE : 0) |
	             (nocopybits ? SWP_NOCOPYBITS : 0) |
	             (nomove ? SWP_NOMOVE : 0) |
	             (noresize ? SWP_NOSIZE : 0));
#elif defined(__APPLE__)
  if (!nomove)
  {
    MoveWindow(wnd, x, y, false);
  }
  if (!noresize)
  {
    Rect newRect;
    newRect.left = x;
    newRect.right=x+w;
    newRect.top = y;
    newRect.bottom = y+h;
//    SetWindowBounds(wnd, kWindowStructureRgn, &newRect);
    SizeWindow(wnd, w, h, false);
  }
  if (!noactive)
    SelectWindow(wnd);
  if (!nocopybits)
     Wasabi::Std::Wnd::invalidateRect(wnd, 0);
  
#endif
}

void  Wasabi::Std::Wnd::bringToFront(OSWINDOWHANDLE wnd)
{
#ifdef _WIN32
	SetWindowPos(wnd, HWND_TOP, 0, 0, 0, 0,
	             SWP_NOMOVE | SWP_NOSIZE | SWP_DEFERERASE | SWP_NOOWNERZORDER);
#elif defined(__APPLE__)
	BringToFront(wnd);
#endif
}

void  Wasabi::Std::Wnd::sendToBack(OSWINDOWHANDLE wnd)
{
#ifdef _WIN32
	SetWindowPos(wnd, HWND_BOTTOM, 0, 0, 0, 0,
	             SWP_NOMOVE | SWP_NOSIZE | SWP_DEFERERASE | SWP_NOOWNERZORDER);
#elif defined(__APPLE__)
	SendBehind(wnd, 0);
#endif
}

OSWINDOWHANDLE  Wasabi::Std::Wnd::getWindowFromPoint(POINT pt)
{
#ifdef _WIN32
  return ::WindowFromPoint(pt);
#else
#warning port me
return 0;
#endif
}

int  Wasabi::Std::Wnd::isWndVisible(OSWINDOWHANDLE wnd)
{
	return IsWindowVisible(wnd);
}

void  Wasabi::Std::Wnd::showWnd(OSWINDOWHANDLE wnd, int noactivate)
{
#ifdef _WIN32
	ShowWindow(wnd, noactivate ? SW_SHOWNA : SW_SHOWNORMAL);
#elif defined(__APPLE__)
	ShowWindow(wnd);
	if (!noactivate)
		ActivateWindow(wnd, 1);
#endif
}

void  Wasabi::Std::Wnd::hideWnd(OSWINDOWHANDLE wnd)
{
#ifdef _WIN32
	ShowWindow(wnd, SW_HIDE);
#elif defined(__APPLE__)
	HideWindow(wnd);
#endif
}

int  Wasabi::Std::Wnd::isPopup(OSWINDOWHANDLE wnd)
{
#ifdef _WIN32
	return !!(GetWindowLong(wnd, GWL_STYLE) & WS_POPUP);
#elif defined(__APPLE__)
	return 0; // TODO: maybe use window class or window group to determine
#endif
}

#ifdef _WIN32
void  Wasabi::Std::Wnd::setEnabled(OSWINDOWHANDLE wnd, int enabled)
{
	EnableWindow(wnd, enabled);
}
#endif

void  Wasabi::Std::Wnd::setFocus(OSWINDOWHANDLE wnd)
{
#ifdef _WIN32
	SetFocus(wnd);
#elif defined(__APPLE__)
	SetUserFocusWindow(wnd);
#endif
}

OSWINDOWHANDLE  Wasabi::Std::Wnd::getFocus()
{
#ifdef _WIN32
	return GetFocus();
#elif defined(__APPLE__)
	return GetUserFocusWindow();
#endif
}

#ifdef _WIN32
void  Wasabi::Std::Wnd::setTopmost(OSWINDOWHANDLE wnd, int topmost)
{
	SetWindowPos(wnd, topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER | SWP_NOACTIVATE);
}
#endif

void  Wasabi::Std::Wnd::invalidateRect(OSWINDOWHANDLE wnd, RECT *r)
{
#ifdef _WIN32
	OSREGIONHANDLE reg = NULL;
	if (r == NULL)
	{
		RECT cr;
		if (!IsWindow(wnd))
			return;
    
		GetClientRect(wnd, &cr);
		reg = CreateRectRgnIndirect(&cr);
	}
	else
		reg = CreateRectRgnIndirect(r);
	invalidateRegion(wnd, reg);
	DeleteObject(reg);
#elif defined(__APPLE__)
  HIViewRef view;
  GetWasabiHIView(wnd, &view);
	if (r == 0)
  {
    HIViewSetNeedsDisplay(view, true);
  }
  else
  {
    HIRect rect = CGRectMake(r->left, r->top, r->right-r->left, r->bottom - r->top);
    HIViewSetNeedsDisplayInRect(view, &rect, true);
  }
#endif
}


void  Wasabi::Std::Wnd::invalidateRegion(OSWINDOWHANDLE wnd, OSREGIONHANDLE region)
{
#ifdef _WIN32
	clipOSChildren(wnd, region);
	InvalidateRgn(wnd, region, FALSE);
#elif defined(__APPLE__)
  HIViewRef view;
  GetWasabiHIView(wnd, &view);
  HIViewSetNeedsDisplayInShape(view, region, true);
#endif
}

void  Wasabi::Std::Wnd::validateRect(OSWINDOWHANDLE wnd, RECT *r)
{
#ifdef _WIN32
	ValidateRect(wnd, r);
#elif  defined(__APPLE__)
  HIViewRef view;
  GetWasabiHIView(wnd, &view);
	if (r == 0)
  {
    HIViewSetNeedsDisplay(view, false);
  }
  else
  {
    HIRect rect = CGRectMake(r->left, r->top, r->right-r->left, r->bottom - r->top);
    HIViewSetNeedsDisplayInRect(view, &rect, false);
  }
#endif
}

void  Wasabi::Std::Wnd::validateRegion(OSWINDOWHANDLE wnd, OSREGIONHANDLE region)
{
#ifdef _WIN32
	ValidateRgn(wnd, region);
#elif defined(__APPLE__)
  HIViewRef view;
  GetWasabiHIView(wnd, &view);
  HIViewSetNeedsDisplayInShape(view, region, false);  
#endif
}

void  Wasabi::Std::Wnd::update(OSWINDOWHANDLE wnd)
{
#ifdef _WIN32
	if (wnd != NULL)
		UpdateWindow(wnd);
#elif defined(__APPLE__)
  if (wnd)
  {
  HIViewRef view;
  GetWasabiHIView(wnd, &view);
  HIViewRender(view);  
  }
#endif
}


int  Wasabi::Std::Wnd::getUpdateRect(OSWINDOWHANDLE wnd, RECT *r)
{
#ifdef _WIN32
	return GetUpdateRect(wnd, r, FALSE);
#else
  Rect updateRect;
		GetWindowBounds(wnd, kWindowUpdateRgn, &updateRect);
    r->left=updateRect.left;
    r->right = updateRect.right;
    r->top = updateRect.top;
    r->bottom = updateRect.bottom;
#endif
}

#ifdef _WIN32
void  Wasabi::Std::Wnd::getUpdateRegion(OSWINDOWHANDLE wnd, OSREGIONHANDLE region)
{
	GetUpdateRgn(wnd, region, FALSE);
}
#elif defined(__APPLE__)
// TODO:  GetWindowRegion(wnd, kWindowUpdateRgn, region);
#endif

#ifdef _WIN32
int  Wasabi::Std::Wnd::haveGetRandomRegion()
{
	// I assume linux will just return FALSE
	if (gdi32instance == NULL && !grrfailed)
	{
		gdi32instance = LoadLibrary(L"GDI32.dll");
		if (gdi32instance != NULL)
		{
			getRandomRgn = (int(WINAPI *)(HDC, HRGN, int)) GetProcAddress(gdi32instance, "GetRandomRgn");
			if (getRandomRgn == NULL)
			{
				grrfailed = 1;
				FreeLibrary(gdi32instance);
				gdi32instance = NULL;
			}
		}
		else
		{
			grrfailed = 1;
		}
	}
	return (getRandomRgn != NULL);
}

void  Wasabi::Std::Wnd::getRandomRegion(HDC hdc, OSREGIONHANDLE region)
{
	if (!haveGetRandomRegion()) return;
	(*getRandomRgn)(hdc, region, SYSRGN);
}
#endif
void  Wasabi::Std::Wnd::setWndRegion(OSWINDOWHANDLE wnd, OSREGIONHANDLE region, int redraw)
{
#ifdef _WIN32
	SetWindowRgn(wnd, region, !!redraw);
#elif defined(__APPLE__)
#warning port me?  
#endif
}
#ifdef _WIN32
int  Wasabi::Std::Wnd::isDesktopAlphaAvailable()
{
	return 1; // we're only targetting windows 2000 and up, so it's always available
}
#endif
int  Wasabi::Std::Wnd::isTransparencyAvailable()
{
#ifdef _WIN32 
  // there is no win32 implementation that supports setLayeredWindowAttributes but not updateLayeredWindow
	return  Wasabi::Std::Wnd::isDesktopAlphaAvailable();
#elif defined(__APPLE__)
  return 1;
#else
#error port me
#endif
}

void  Wasabi::Std::Wnd::getClientRect(OSWINDOWHANDLE wnd, RECT *r)
{
#ifdef _WIN32
	GetClientRect(wnd, r);
#elif defined(__APPLE__)
	Rect temp;
	GetWindowBounds(wnd, kWindowContentRgn, &temp);
  r->left = 0;
  r->top = 0;
  r->right = temp.right-temp.left;
  r->bottom = temp.bottom-temp.top;
#endif
}

void  Wasabi::Std::Wnd::getWindowRect(OSWINDOWHANDLE wnd, RECT *r)
{
#ifdef _WIN32
	GetWindowRect(wnd, r);
#elif defined(__APPLE__)
	Rect temp;
	GetWindowBounds(wnd, kWindowGlobalPortRgn, &temp);
  r->left = temp.left;
  r->top = temp.top;
  r->right = temp.right;
  r->bottom = temp.bottom;
#endif
}

void  Wasabi::Std::Wnd::clientToScreen(OSWINDOWHANDLE wnd, int *x, int *y)
{
#ifdef _WIN32
	POINT p = { x ? *x : 0, y ? *y : 0 };
	ClientToScreen(wnd, &p);
	if (x) *x = p.x;
	if (y) *y = p.y;
#elif defined(__APPLE__)
  Point pt;
  pt.h = x?*x:0;
  pt.v = y?*y:0;
	QDLocalToGlobalPoint(GetWindowPort(wnd) , &pt);
	if (x) *x = pt.h;
	if (y) *y = pt.v;  
#endif
}

void  Wasabi::Std::Wnd::screenToClient(OSWINDOWHANDLE wnd, int *x, int *y)
{
#ifdef _WIN32
	POINT p = { x ? *x : 0, y ? *y : 0 };
	ScreenToClient(wnd, &p);
	if (x) *x = p.x;
	if (y) *y = p.y;
#elif defined(__APPLE__)
  Point pt;
  pt.h = x?*x:0;
  pt.v = y?*y:0;
		QDGlobalToLocalPoint(GetWindowPort(wnd) , &pt);
    if (x) *x = pt.h;
    if (y) *y = pt.v;  
#endif
}

#ifdef _WIN32
void  Wasabi::Std::Wnd::setParent(OSWINDOWHANDLE child, OSWINDOWHANDLE newparent)
{
	SetParent(child, newparent);
}
#endif

OSWINDOWHANDLE  Wasabi::Std::Wnd::getParent(OSWINDOWHANDLE wnd)
{
#ifdef _WIN32
	return GetParent(wnd);
#else
	return 0;
#endif
}

#ifdef _WIN32
OSWINDOWHANDLE  Wasabi::Std::Wnd::getTopmostChild(OSWINDOWHANDLE wnd)
{
	return GetWindow(wnd, GW_CHILD);
}

void  Wasabi::Std::Wnd::setCapture(OSWINDOWHANDLE wnd)
{
	SetCapture(wnd);
}

void  Wasabi::Std::Wnd::releaseCapture()
{
	ReleaseCapture();
}

OSWINDOWHANDLE  Wasabi::Std::Wnd::getCapture()
{
	return GetCapture();
}

void  Wasabi::Std::Wnd::revokeDragNDrop(OSWINDOWHANDLE wnd)
{
	if (wnd == NULL) return;
	RevokeDragDrop(wnd);
}
#endif
void  Wasabi::Std::Wnd::setWndName(OSWINDOWHANDLE wnd, const wchar_t *name)
{
#ifdef _WIN32
	SetWindowTextW(wnd, name);
#elif defined(__APPLE__)
	// TODO:
	//SetWindowTitleWithCFString(wnd,
#endif
}


void  Wasabi::Std::Wnd::getWndName(OSWINDOWHANDLE wnd, wchar_t *name, int maxlen)
{
  #ifdef _WIN32
	GetWindowTextW(wnd, name, maxlen);
#else
  name[0]=0;
#warning port me
#endif
}

void  Wasabi::Std::Wnd::setIcon(OSWINDOWHANDLE wnd, OSICONHANDLE icon, int large)
{
#ifdef _WIN32
	SendMessageW(wnd, WM_SETICON, !large ? ICON_SMALL : ICON_BIG, (LPARAM)icon);
#else
#warning port me
#endif
}

OSWINDOWHANDLE  Wasabi::Std::Wnd::getActiveWindow()
{
#ifdef _WIN32
	return GetActiveWindow();
#elif defined(__APPLE__)
  return ActiveNonFloatingWindow();
#endif
}

void  Wasabi::Std::Wnd::setActiveWindow(OSWINDOWHANDLE wnd)
{
#ifdef _WIN32
	SetActiveWindow(wnd);
#elif defined(__APPLE__)
  SelectWindow(wnd);
#endif
}

#ifdef _WIN32
// clip oswindow children off the invalidation region
void  Wasabi::Std::Wnd::clipOSChildren(OSWINDOWHANDLE wnd, OSREGIONHANDLE reg)
{
	HWND w = GetWindow(wnd, GW_CHILD);
	while (w != NULL)
	{
		if (IsWindowVisible(w))
		{
			RECT r;
			GetClientRect(w, &r);
			POINT p = {r.left, r.top};
			ClientToScreen(w, &p);
			ScreenToClient(wnd, &p);
			OffsetRect(&r, p.x, p.y);
			OSREGIONHANDLE cr = CreateRectRgnIndirect(&r);
			OSREGIONHANDLE or = CreateRectRgn(0, 0, 0, 0);
			CombineRgn(or, reg, 0, RGN_COPY);
			CombineRgn(reg, or, cr, RGN_DIFF);
			DeleteObject(cr);
			DeleteObject(or);
		}
		w = GetWindow(w, GW_HWNDNEXT);
	}
}

//////////////////////////

static LRESULT CALLBACK wndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	// fetch out the api_window *
	if (uMsg == WM_CREATE)
	{
		CREATESTRUCT *cs = (CREATESTRUCT *)lParam;
		ASSERT(cs->lpCreateParams != NULL);
		// stash pointer to self
		SetWindowLongPtrW(hWnd, GWLP_USERDATA, (LONG_PTR)cs->lpCreateParams);
	}
  
	// pass the messages into the BaseWnd
	ifc_window *rootwnd = (ifc_window*)GetWindowLongPtrW(hWnd, GWLP_USERDATA);
	if (rootwnd == NULL || rootwnd->getOsWindowHandle() != hWnd)
	{
		return DefWindowProcW(hWnd, uMsg, wParam, lParam);
	}
	else
	{
		LRESULT r = 0;
		switch (uMsg)
		{
			case WM_CLOSE: 
				PostMessage(GetParent(hWnd), WM_CLOSE, wParam, lParam); 
				return 0;
			case WM_NCPAINT: return 0;
			case WM_SYNCPAINT: return 0;
        //      case WM_SETFOCUS: r = rootwnd->windowEvent(WndEvent::SETFOCUS);
        //      case WM_KILLFOCUS: r = rootwnd->windowEvent(WndEvent::KILLFOCUS);
			case WM_ACTIVATE:
				if ( (LOWORD(wParam) == WA_ACTIVE || LOWORD(wParam) == WA_CLICKACTIVE) && IsIconic(GetParent(hWnd)) )
				{
					ShowWindow(GetParent(hWnd), SW_RESTORE);
				}
				r = rootwnd->wndProc(hWnd, uMsg, wParam, lParam);
				break;
			case WM_COMMAND:		
				if (HIWORD(wParam) == THBN_CLICKED)
				{
					HWND parentWnd = GetParent(hWnd);
					if (parentWnd == NULL) 
						parentWnd = hWnd;

					switch (LOWORD(wParam))
					{
					case 0: //previous
						{
							SendMessage(parentWnd, WM_COMMAND, 40044, 0);
						}
						break;
					case 1: //play
						{
							SendMessage(parentWnd, WM_COMMAND, 40045, 0);									
						}
						break;
					case 2: //pause
						{
							SendMessage(parentWnd, WM_COMMAND, 40046, 0);									
						}
						break;
					case 3: //stop
						{
							SendMessage(parentWnd, WM_COMMAND, 40047, 0);
						}
						break;
					case 4: //next
						{
							SendMessage(parentWnd, WM_COMMAND, 40048, 0);
						}
						break;					
					}
				}
			default:
				r = rootwnd->wndProc(hWnd, uMsg, wParam, lParam);
		}
		if (IsWindow(hWnd)) // wndProc may have switched skin and killed this window
			rootwnd->performBatchProcesses();
		return r;
	}
}

static void register_wndClass(HINSTANCE hInstance)
{
	WNDCLASSW wc;
	if (GetClassInfoW(hInstance, BASEWNDCLASSNAME, &wc)) return;
  
	// regiszter pane class
	wc.style = 0;
	wc.lpfnWndProc = (WNDPROC)wndProc;
	wc.cbClsExtra		= 0;
	wc.cbWndExtra		= 0;
	wc.hInstance		= hInstance;
	wc.hIcon		= NULL;
	wc.hCursor		= NULL;
	wc.hbrBackground	= (HBRUSH)GetStockObject(NULL_BRUSH);
	wc.lpszMenuName	= NULL;
	wc.lpszClassName	= BASEWNDCLASSNAME;
  
	int r = RegisterClassW(&wc);
	if (r == 0)
	{
		int res = GetLastError();
		if (res != ERROR_CLASS_ALREADY_EXISTS)
		{
			char florp[WA_MAX_PATH] = {0};
			SPRINTF(florp, "Failed to register class, err %d", res);
			ASSERTPR(0, florp);
		}
	}
}
#endif

#ifdef __APPLE__

// TODO: call into BaseWnd and take this out of here
#include <tataki/canvas/canvas.h>
#include <tataki/region/region.h>
#include <api/wnd/basewnd.h>

OSStatus MyWindowEventHandler(EventHandlerCallRef	inHandlerCallRef, EventRef inEvent, void *inUserData)
{
	HICommand hiCommand;
	OSStatus err = eventNotHandledErr;
	UInt32 eventClass = GetEventClass(inEvent);
	UInt32 eventKind = GetEventKind(inEvent);
	ifc_window *wasabiWnd = (ifc_window *)inUserData;
	WindowRef window = wasabiWnd->getOsWindowHandle();
  
	switch (eventClass)
	{
		case kEventClassKeyboard:
			switch (eventKind)
			{
				case kEventRawKeyDown:
				{
					UInt32 keyCode;
					GetEventParameter(inEvent, kEventParamKeyCode, typeUInt32,
					                  NULL, sizeof(keyCode), NULL, &keyCode);
          
					if (wasabiWnd->onKeyDown(keyCode))
						err = noErr;
				}
          break;
				case kEventRawKeyUp:
				{
					UInt32 keyCode;
					GetEventParameter(inEvent, kEventParamKeyCode, typeUInt32,
					                  NULL, sizeof(keyCode), NULL, &keyCode);
          
					if (wasabiWnd->onKeyUp(keyCode))
						err = noErr;
				}
          break;
			}
			break;
		case kEventClassMouse:
			switch (eventKind)
			{
				case kEventMouseDown:
				{
					HIPoint point;
          
					GetEventParameter(inEvent, kEventParamWindowMouseLocation, typeHIPoint,
					                  NULL, sizeof(point), NULL, &point);
          
					EventMouseButton button;
          
					GetEventParameter(inEvent, kEventParamMouseButton, typeMouseButton,
					                  NULL, sizeof(button), NULL, &button);
          
          UInt32 modifiers;
          GetEventParameter(inEvent, kEventParamKeyModifiers, typeUInt32,
					                  NULL, sizeof(modifiers), NULL, &modifiers);
          
          
					switch (button)
					{
						case kEventMouseButtonPrimary:
              if (modifiers & controlKey) // fake right click
                wasabiWnd->onRightButtonDown(point.x, point.y);
              else
							  wasabiWnd->onLeftButtonDown(point.x, point.y);
							break;
						case kEventMouseButtonSecondary:
							wasabiWnd->onRightButtonDown(point.x, point.y);
							break;
					}
				}
          break;
				case kEventMouseUp:
				{
					HIPoint point;
					GetEventParameter(inEvent, kEventParamWindowMouseLocation, typeHIPoint,
					                  NULL, sizeof(point), NULL, &point);
          
					EventMouseButton button;
					GetEventParameter(inEvent, kEventParamMouseButton, typeMouseButton,
					                  NULL, sizeof(button), NULL, &button);
          
					switch (button)
					{
						case kEventMouseButtonPrimary:
							wasabiWnd->onLeftButtonUp(point.x, point.y);
							break;
						case kEventMouseButtonSecondary:
							wasabiWnd->onRightButtonUp(point.x, point.y);
							break;
					}
				}
          break;
				case kEventMouseMoved:
				case kEventMouseDragged:
				{
					HIPoint point;
					GetEventParameter(inEvent, kEventParamWindowMouseLocation, typeHIPoint,
					                  NULL, sizeof(point), NULL, &point);
          
					wasabiWnd->onMouseMove(point.x, point.y);
          
				}
          break;
			}
			break;
		case kEventClassCommand:
			if (eventKind == kEventProcessCommand)
			{
				err = GetEventParameter(inEvent, kEventParamDirectObject, typeHICommand,
				                        NULL, sizeof(HICommand), NULL, &hiCommand);
        
				switch (hiCommand.commandID)
				{
					default:
						err = eventNotHandledErr;
						break;
				}
			}
			break;
		case kEventClassWindow:
			switch (eventKind)
			{
        case  kEventWindowBoundsChanging:
          break;
				case  kEventWindowBoundsChanged:
				{
          		HIRect		bounds;
					UInt32		attributes;
					Rect		origBounds;
					Rect		currBounds;
          		ControlRef	control;
              
					// Get the change attributes
					err = GetEventParameter(inEvent, kEventParamAttributes, typeUInt32,
					                        NULL, sizeof(UInt32), NULL, &attributes);
          
					// If the window size changed
          //kWindowBoundsChangeOriginChanged
					if (attributes & kWindowBoundsChangeSizeChanged)
					{
						GetEventParameter(inEvent, kEventParamOriginalBounds, typeQDRectangle,
						                        NULL, sizeof(Rect), NULL, &origBounds);
            
						GetEventParameter(inEvent, kEventParamCurrentBounds, typeQDRectangle,
						                        NULL, sizeof(Rect), NULL, &currBounds);
            
						// Get the clock control
						err = GetControlByID( window, &kWasabiID, &control );
            
						// Hide it (to avoid remnants when downsizing)
									//HIViewSetVisible( control, false );
            
						// Resize it
									HIViewGetFrame( control, &bounds );
#define QDRECTWIDTH(R)	((R).right - (R).left)
#define QDRECTHEIGHT(R)	((R).bottom - (R).top)

									bounds.size.width += QDRECTWIDTH( currBounds ) - QDRECTWIDTH( origBounds );
									bounds.size.height += QDRECTHEIGHT( currBounds ) - QDRECTHEIGHT( origBounds );
									HIViewSetFrame( control, &bounds );
            
						// Show it
									//HIViewSetVisible( control, true );

					}
          err = noErr;
				}
          break;
        case kEventWindowInit:
          break;
			}
			break;
      
	}
  
	return err;
}
OSStatus MyControlEventHandler(EventHandlerCallRef	inHandlerCallRef, EventRef inEvent, void *inUserData)
{
  HICommand hiCommand;
	OSStatus err = eventNotHandledErr;
	UInt32 eventClass = GetEventClass(inEvent);
	UInt32 eventKind = GetEventKind(inEvent);
	ifc_window *wasabiWnd = (ifc_window *)inUserData;
  switch(eventClass)
  {
    case kEventClassControl:
      switch(eventKind)
      {

        case kEventControlDraw:
        {
          CGContextRef context;
          RgnHandle rgn;
          HIShapeRef shape;
          HIViewRef view;
          if (GetEventParameter(inEvent, kEventParamCGContextRef, typeCGContextRef, NULL, sizeof(context), NULL, &context) == noErr
              && GetEventParameter(inEvent, kEventParamRgnHandle, typeQDRgnHandle, NULL, sizeof(rgn), NULL, &rgn) == noErr
               && GetEventParameter(inEvent, kEventParamDirectObject, typeControlRef, NULL, sizeof(view), NULL, &view) == noErr)
          {
          GetEventParameter(inEvent, kEventParamShape, typeHIShapeRef, NULL, sizeof(shape), NULL, &shape);
            
            HIRect bounds;
            HIViewGetBounds(view, &bounds);
                        
          Canvas canvas(context);
//          RegionI region(rgn);
          RegionI region(shape);

          //((RootWndI *)wasabiWnd)->rootwnd_paintTree(&canvas, &region); 
          wasabiWnd->paint(&canvas, &region); // TODO: should be virtualOnPaint()

          err = noErr;            
          }
        }
          break;
      }
      break;
  }
  return err;
}
#endif