#include "StdHeaders.h"
#include "OverriddenControl.h"
#include "MiniCadCallBacks.h"
#if _MINICAD_
#include "DrawPadFactory.h"
#endif

#include <map>
std::map<void *, OverriddenLayoutControl *> controlMap;
std::map<void *, OverriddenLayoutControl *> :: const_iterator controlIterator;
OverriddenLayoutControl * overriddenCtrlUnderMouse = nil; // which control is under the mouse

OverriddenLayoutControl::OverriddenLayoutControl()
{
#if _MINICAD_
	fpDrawPad = nil;
#endif
	fDlogID = 0;
	fControlID = 0;
	fInitialized = false;
	fUsesDrawPad = false;
	fDrawPadInitialized = false;
	fIdentifier = nil;
	mouseEntered = false;
	fDrawRectInitialized = false;
	fDrawRect.SetLTRB(0, 0, 0, 0);
	
#if !GS_WIN
	initialMouseMove = false;
#endif
}

#if GS_WIN
LRESULT CALLBACK OverriddenLayoutControlProc(HWND controlHWND, UINT message, WPARAM wParam, LPARAM lParam)
{
	controlIterator = controlMap.find((void *)controlHWND);
	if (controlIterator == controlMap.end())
		return false;

	OverriddenLayoutControl *control = controlIterator->second;

	switch (message) {
		case WM_PAINT: {
			PAINTSTRUCT ps;
			BeginPaint(controlHWND, &ps);
			control->SetupDraw();
			
			control->OnDraw();
			control->FinishDraw();
			EndPaint(controlHWND, &ps);
			return true;
		}
		case WM_LBUTTONDOWN: {
			SetCapture(controlHWND);
			ViewPt p;
			p.x = LOWORD(lParam); 
			p.y = HIWORD(lParam);
			control->OnLButtonDown(p.x, p.y);
			return true;
		}
		case WM_LBUTTONUP: {
			ReleaseCapture();
			ViewPt p;
			p.x = LOWORD(lParam); 
			p.y = HIWORD(lParam);
			control->OnLButtonUp(p.x, p.y);
			return true;
		}
		case WM_MBUTTONDOWN: {
			SetCapture(controlHWND);
			ViewPt p;
			p.x = LOWORD(lParam); 
			p.y = HIWORD(lParam);
			control->OnMButtonDown(p.x, p.y);
			return true;
		}
		case WM_MBUTTONUP: {
			ReleaseCapture();
			ViewPt p;
			p.x = LOWORD(lParam); 
			p.y = HIWORD(lParam);
			control->OnMButtonUp(p.x, p.y);
			return true;
		}
		
		case WM_RBUTTONDOWN: {
			SetCapture(controlHWND);
			ViewPt p;
			p.x = LOWORD(lParam); 
			p.y = HIWORD(lParam);
			control->OnRButtonDown(p.x, p.y);
			return true;
		}
		case WM_RBUTTONUP: {
			ReleaseCapture();
			ViewPt p;
			p.x = LOWORD(lParam); 
			p.y = HIWORD(lParam);
			control->OnRButtonUp(p.x, p.y);
			return true;
		}
		case WM_MOUSEWHEEL: {
			short zDelta = GET_WHEEL_DELTA_WPARAM(wParam);
			POINT pt;
			pt.x = LOWORD(lParam); 
			pt.y = HIWORD(lParam); 
			ScreenToClient(controlHWND, &pt);
			control->OnMouseWheel(pt.x, pt.y, (int)(zDelta / WHEEL_DELTA));
			return true;
		}

		case WM_MOUSELEAVE: {
			if( control->mouseEntered ) {
				control->OnMouseLeave();
			}
			control->mouseEntered = false;
			overriddenCtrlUnderMouse = nil;

			return true;
		}
		case WM_MOUSEMOVE: {
			if( !control->mouseEntered ) {
				overriddenCtrlUnderMouse = control;
				control->mouseEntered = true;
				TRACKMOUSEEVENT trackEvent;
				trackEvent.cbSize = sizeof(TRACKMOUSEEVENT);
				trackEvent.dwFlags = TME_LEAVE;
				trackEvent.hwndTrack = controlHWND;
				trackEvent.dwHoverTime = HOVER_DEFAULT;
				TrackMouseEvent(&trackEvent);
				control->OnMouseEnter();
			}
			ViewPt p;
			p.x = LOWORD(lParam); 
			p.y = HIWORD(lParam);
			control->OnMouseMove(p.x, p.y);
			return true; 
		}
	}

	return DefWindowProc(controlHWND, message, wParam, lParam);
}


void OverriddenLayoutControl::Attach(int dlogID, int controlID, bool wantDrawPad)
{	
	HWND hwDlg = (HWND) GS_OverrideControl(gCBP, dlogID, controlID, OverriddenLayoutControlProc);
	fIdentifier = (void *) GetDlgItem(hwDlg, controlID);
	fDlogID = dlogID;
	fControlID = controlID;
	mouseEntered = false;
	overriddenCtrlUnderMouse = nil;

	controlMap[fIdentifier] = this;
	if (wantDrawPad) {
		fUsesDrawPad = true;
		fDrawPadInitialized = false;
	}
	else {
		fUsesDrawPad = false;
	}
}

void OverriddenLayoutControl::SetupDraw()
{
	// The following SetDrawRect call is very important, otherwise fDrawRect will not be initialized 
	// for the OnInit call - resulting in no image in the Edit Texture dialog for example. <Biplab 05/03/07>
	RECT r;
	GetClientRect((HWND) fIdentifier, &r);
	ViewRect drawRect((ViewCoord)r.left, (ViewCoord)r.top, (ViewCoord)r.right, (ViewCoord)r.bottom);
	SetDrawRect(drawRect);

	if (!fInitialized) {

		if (fUsesDrawPad) {
			if (!fDrawPadInitialized) {
#if _MINICAD_
				fpDrawPad = CreateDrawPad(TWindowDev((unsigned long) fIdentifier));
#endif
				fDrawPadInitialized = true;
			}

#if _MINICAD_		
			if (fpDrawPad) {
				
				RECT r;
				ViewRect vr;

				GetClientRect((HWND) fIdentifier, &r);
				vr.SetLTRB((ViewCoord) r.left,  (ViewCoord) r.top, (ViewCoord) r.right, (ViewCoord) r.bottom);
				fpDrawPad->SetClipRect(vr);
				fpDrawPad->SetPen(1, kGSColorBlack, kGSColorWhite, kSolidForePattern);
			}
#endif
		}

		fInitialized = true;

		this->OnInit();
	}

	
#if _MINICAD_	
	if (fUsesDrawPad)
		fpDrawPad->CaptureDevice();
#endif
}

void OverriddenLayoutControl::FinishDraw()
{
#if _MINICAD_	
	if (fUsesDrawPad)
		fpDrawPad->ReleaseDevice();	
#endif
}

#else	// !GS_WIN

typedef struct {
	Point		wherePort;
	Point		whereLocal;
	EventRecord	macEvent;
	Boolean		delaySelect;
} SMouseEvent;

void OverriddenLayoutControlProc(long message, void *ioParam, void *pane, int dialogID, int componentID) {
	const signed long msg_DrawOrPrint	= 811;	// Rect* (frame of Pane)
	const signed long msg_Click			= 812;	// SMouseEvent*
	const signed long msg_AdjustCursor	= 813;	// SMouseEvent*
	const signed long msg_MouseUp       = 850;	// SMouseEvent*
	
	componentID;
	dialogID;

	controlIterator = controlMap.find(pane);
	if (controlIterator == controlMap.end())
		return;

	OverriddenLayoutControl *control = controlIterator->second;
	
	switch (message) {
		case msg_DrawOrPrint: {
			ViewRect* macDrawRect = (ViewRect*) ioParam;
			ViewRect drawRect(macDrawRect->left, macDrawRect->top, macDrawRect->right, macDrawRect->bottom);
			control->SetDrawRect(drawRect);
			
			control->SetupDraw();
			control->OnDraw();
			control->FinishDraw();
			break;
		}
		case msg_Click: {
			control->OnLButtonDown(
				((SMouseEvent*)ioParam)->whereLocal.h - control->fDrawRect.left,  
				((SMouseEvent*)ioParam)->whereLocal.v - control->fDrawRect.top);
			break;
		}
		case msg_AdjustCursor: {
			if (control->fInitialized) {
				
				control->OnMouseMove(
					((SMouseEvent*)ioParam)->whereLocal.h - control->fDrawRect.left,  
					((SMouseEvent*)ioParam)->whereLocal.v - control->fDrawRect.top);
			} else {
				control->initialMouseMove = true;
				control->initialMouseMoveX = ((SMouseEvent*)ioParam)->whereLocal.h - control->fDrawRect.left;
				control->initialMouseMoveY = ((SMouseEvent*)ioParam)->whereLocal.v - control->fDrawRect.top;
			}
			
			break;
		}
		case msg_MouseUp: {
			control->OnLButtonUp(
				((SMouseEvent*)ioParam)->whereLocal.h - control->fDrawRect.left,  
				((SMouseEvent*)ioParam)->whereLocal.v - control->fDrawRect.top);
			break;
		}
	}
		
	return;
}

void* GetPane(int dlogID, int ctrlID);
ViewRect PaneRect(void * lPane);
WindowPtr WindowPtrFromLPane(void * lPane);

OSStatus MouseTrackingFunc(EventHandlerCallRef inHandlerCallRef, EventRef inEvent, void * inUserData) {
	unsigned int kind = GetEventKind(inEvent);
	
	OverriddenLayoutControl * ctrl = NULL;// = (OverriddenLayoutControl *)inUserData;	

	OSStatus result = noErr;
 
	/*if( kind == kEventControlTrackingAreaEntered ) {
		ctrl->OnMouseEnter();
	} else if( kind == kEventControlTrackingAreaExited ) {
		ctrl->OnMouseLeave();
	}*/
	if( kind == kEventMouseEntered ) {
		MouseTrackingRef mouseTrackingRef;
		GetEventParameter(inEvent, kEventParamMouseTrackingRef, typeMouseTrackingRef, NULL, sizeof(MouseTrackingRef), NULL, &mouseTrackingRef);

		MouseTrackingRegionID regionID;
		GetMouseTrackingRegionID(mouseTrackingRef, &regionID);
		
		ctrl = (OverriddenLayoutControl *)regionID.id;
		
		// order of enter/exit not guaranteed
		if( overriddenCtrlUnderMouse != nil ) {			
			ctrl->mouseEntered = false;
			ctrl->OnMouseLeave();
		}
		overriddenCtrlUnderMouse = ctrl;
		ctrl->mouseEntered = true;
		ctrl->OnMouseEnter();
		
	} else if( kind == kEventMouseExited ) {
		MouseTrackingRef mouseTrackingRef;
		GetEventParameter(inEvent, kEventParamMouseTrackingRef, typeMouseTrackingRef, NULL, sizeof(MouseTrackingRef), NULL, &mouseTrackingRef);

		MouseTrackingRegionID regionID;
		GetMouseTrackingRegionID(mouseTrackingRef, &regionID);
		
		ctrl = (OverriddenLayoutControl *)regionID.id;

		// order of enter/exit not guaranteed
		if( overriddenCtrlUnderMouse == ctrl ) {
			overriddenCtrlUnderMouse = nil; 
			ctrl->mouseEntered = false;
			ctrl->OnMouseLeave();
		}
		
	} else if( kind == kEventMouseWheelMoved ) {
		if( overriddenCtrlUnderMouse != nil ) {
			int amount;
			GetEventParameter(inEvent, kEventParamMouseWheelDelta, typeLongInteger, NULL, sizeof(amount), NULL, &amount );
		
			EventMouseWheelAxis axis;            
			GetEventParameter(inEvent, kEventParamMouseWheelAxis, typeMouseWheelAxis, NULL, sizeof(axis), NULL, &axis );

			HIPoint point;
			GetEventParameter(inEvent, kEventParamWindowMouseLocation, typeHIPoint, NULL, sizeof(point), NULL, &point );
		
			void * lPane = overriddenCtrlUnderMouse->GetIdentifier();
			ViewRect ctrlRect = PaneRect(lPane);
		
			WindowPtr windowPtr = WindowPtrFromLPane(lPane);
			Rect titleBarRect;
			GetWindowBounds(windowPtr, kWindowTitleBarRgn, &titleBarRect);
			int titleBarHeight = titleBarRect.bottom-titleBarRect.top;
		
			if( axis == kEventMouseWheelAxisY ) {
				overriddenCtrlUnderMouse->OnMouseWheel(point.x-ctrlRect.left, point.y-titleBarHeight-ctrlRect.top, amount);
			}
		}

	} else {
		result = eventNotHandledErr;
	}
	
	return result;
}

void OverriddenLayoutControl::Attach(int dlogID, int controlID, bool wantDrawPad)
{
	WindowPtr window = (WindowPtr)GS_OverrideControl(gCBP, dlogID, controlID, (void *)OverriddenLayoutControlProc);
	fIdentifier = GetPane(dlogID, controlID);
	fDlogID = dlogID;
	fControlID = controlID;

	
	controlMap[fIdentifier] = this;
	if (wantDrawPad) {
		fUsesDrawPad = true;
		fDrawPadInitialized = false;
	}
	else {
		fUsesDrawPad = false;
	}
		
	/*HIViewRef view = HIViewGetRoot(window);
	HIViewTrackingAreaRef trackingArea;
	HIViewNewTrackingArea(view, NULL, (HIViewTrackingAreaID)this, &trackingArea);
	*/
	RgnHandle region = NewRgn();
	Rect rect = ViewToMacRect(PaneRect(fIdentifier));
	RectRgn(region, &rect);
	//GetWindowRegion(window, kWindowContentRgn, region);
	MouseTrackingRef trackingRef;
	MouseTrackingRegionID id = { 'jstn', (int)this };
	CreateMouseTrackingRegion(window, region, NULL, kMouseTrackingOptionsLocalClip, id, this, NULL, &trackingRef);
	DisposeRgn(region);
	
	EventHandlerRef eventHandler;
	EventTypeSpec events[] = { 
		//{ kEventClassControl, kEventControlTrackingAreaEntered },
		//{ kEventClassControl, kEventControlTrackingAreaExited },
		{ kEventClassMouse, kEventMouseEntered },
		{ kEventClassMouse, kEventMouseExited },
		{ kEventClassMouse, kEventMouseWheelMoved }
	};
	InstallWindowEventHandler(window, NewEventHandlerUPP(MouseTrackingFunc), 3, events, (void *)this, &eventHandler);
}


void OverriddenLayoutControl::SetupDraw()
{
	if (!fInitialized) {

		if (fUsesDrawPad) {
			if (!fDrawPadInitialized) {
				GrafPtr port;
				GetPort (&port);
#if _MINICAD_
				fpDrawPad = ::CreateDrawPad(TWindowDev((unsigned long) fIdentifier, (void *)port));
#endif
				fDrawPadInitialized = true;
			}

#if _MINICAD_
			if (fpDrawPad) {
				ViewRect vr;
				vr.SetLTRB((ViewCoord) fDrawRect.left,  (ViewCoord) fDrawRect.top, 
						   (ViewCoord) fDrawRect.right, (ViewCoord) fDrawRect.bottom);
				fpDrawPad->SetClipRect(vr);
				fpDrawPad->SetPen(1, kGSColorBlack, kGSColorWhite, kSolidForePattern);
			}
#endif
		}

		fInitialized = true;

		this->OnInit();
		
		if( initialMouseMove ) {
			overriddenCtrlUnderMouse = this;
			mouseEntered = true;
			OnMouseEnter();
		
			OnMouseMove(initialMouseMoveX, initialMouseMoveY);
			initialMouseMove = false;
		}
	}

#if _MINICAD_
	if (fUsesDrawPad) 
		fpDrawPad->CaptureDevice();
#endif
}

void OverriddenLayoutControl::FinishDraw()
{
#if _MINICAD_
	if (fUsesDrawPad) 
		fpDrawPad->ReleaseDevice();
#endif
}

#endif	// !GS_WIN


void OverriddenLayoutControl::Detach()
{
	controlMap.erase(fIdentifier);
#if _MINICAD_
	if (fUsesDrawPad)
		delete fpDrawPad;
	fpDrawPad = NULL;
#endif
	fInitialized = false;
	OnDetatch();
}
