/*
 *  MS Windows dependent Interactor Code
 */

#include <InterViews\itable.h>
#include <InterViews\bitmap.h>
#include <InterViews\canvas.h>
#include <InterViews\cursor.h>
#include <InterViews\interact.h>
#include <InterViews\sensor.h>
#include <InterViews\shape.h>
#include <InterViews\world.h>
#include <InterViews\worldview.h>
#include <InterViews\X11\eventrep.h>
#include <InterViews\X11\worldrep.h>
#include <InterViews\X11\windata.h>

const int NOGRAB = 0;
const int LEFTGRAB = 1;
const int MIDDLEGRAB = 2;
const int RIGHTGRAB = 3;

POINT newPoint;
HWND  hnewWindow;
HWND  holdWindow = NULL;
int   grabButton = NOGRAB;

inline void SetDummyOldWindow (HWND hWnd) {
    holdWindow = nil;
}

void EnterLeaveMessage () {
    GetCursorPos(&newPoint);
    hnewWindow = WindowFromPoint(newPoint);

    if (hnewWindow != holdWindow) {
	if (IsChild(holdWindow, hnewWindow)) {
	    PostMessage(hnewWindow, WM_ENTERWINDOW, holdWindow, 0);
	} else if (IsChild(hnewWindow, holdWindow)) {
	    PostMessage(holdWindow, WM_LEAVEWINDOW, hnewWindow, 0);
	} else {
	    PostMessage(holdWindow, WM_LEAVEWINDOW, hnewWindow, 0);
	    PostMessage(hnewWindow, WM_ENTERWINDOW, holdWindow, 0);
	}
    }
    holdWindow = hnewWindow;
}

void PropagateEnterLeave (LPMSG m) {
    HWND hWnd = GetParent(m->hwnd);

    switch (m->message) {

	case WM_ENTERWINDOW:
	case WM_LEAVEWINDOW:
	    if ((hWnd != NULL) && (hWnd != m->wParam) &&
	       (!IsChild(hWnd, m->wParam))) {
		PostMessage(
		    hWnd, m->message, m->wParam, m->lParam
		);
	    }
	    break;

	default:
	    break;
    }
}

void PropagateKeyboardMouse(LPMSG m) {
    HWND hWnd;

    switch (m->message) {

	case WM_MOUSEMOVE:
	case WM_LBUTTONDOWN:
	case WM_MBUTTONDOWN:
	case WM_RBUTTONDOWN:
	case WM_LBUTTONUP:
	case WM_MBUTTONUP:
	case WM_RBUTTONUP:
	case WM_CHAR:

            hWnd = GetParent(m->hwnd);
	    if (hWnd != NULL) {
		PostMessage(hWnd, m->message, m->wParam, m->lParam);
	    }
	    break;

	default: break;
    }
}

HWND GetSourceWindow () {
    HWND hFocusWindow = GetFocus();

    if (IsChild(hFocusWindow, hnewWindow)) {
	return hnewWindow;
    }
    return hFocusWindow;
}

void Interactor::Listen (Sensor* s) {
    cursensor = s;
}

int Interactor::Fileno () {
    return 0;
}

boolean Interactor::GetEvent (Event& m, boolean remove) {
    MSG& msg = m.rep->Message();
    HWND hwindow;
    Interactor* i = nil;
    Cursor* cursor;
    void* c;

    GetMessage(&msg,NULL,0,0);
    switch (msg.message) {

	case WM_SHOWWINDOW:
	    if (_world->itable()->Find(i, (void*)msg.hwnd)) {
		if (msg.wParam != 0) {
		    i->SendActivate();
		} else {
		    i->SendDeactivate();
		}
		SetDummyOldWindow(msg.hwnd);
		EnterLeaveMessage();
	    }
	    return false;

	case WM_PAINT:
	    if (_world->itable()->Find(i, (void*)msg.hwnd)) {
		i->SendRedraw();
	    } else {
                PAINTSTRUCT ps;
                BeginPaint(msg.hwnd, &ps);
                EndPaint(msg.hwnd, &ps);
            }
	    return false;

	case WM_MOVE:
	    if (_world->itable()->Find(i, (void*)msg.hwnd)) {
		i->SendResize(
		    LOWORD(msg.lParam), HIWORD(msg.lParam),
		    i->canvas->width, i->canvas->height
		);
		SetDummyOldWindow(msg.hwnd);
		EnterLeaveMessage();
	    }
	    return false;

	case WM_SIZE:
	    if (_world->itable()->Find(i, (void*)msg.hwnd)) {
                if (msg.wParam == SIZEICONIC) {
                    const char* name = i->GetIconName();
                    if (name) {
                        SetWindowText(msg.hwnd, (LPSTR)name);
                    }
                } else {
                    const char* name = i->rep->GetWndName();
                    if (name) {
                        SetWindowText(msg.hwnd, (LPSTR)name);
                    }
                }

		Coord x, y;
		i->GetOrigin(x, y);
		i->SendResize(
		    x, y, LOWORD(msg.lParam), HIWORD(msg.lParam)
		);
		SetDummyOldWindow(msg.hwnd);
		EnterLeaveMessage();
	    }
	    return false;

	case WM_TIMER:
	    EnterLeaveMessage();
	    return false;

	case WM_MOUSEMOVE:
	    if (_world->itable()->Find(i, (void*)msg.hwnd) &&
		msg.hwnd != _world->root()) {

		cursor = i->GetCursor();
		c = (cursor != nil) ? cursor->Id() : defaultCursor->Id();
		::SetCursor((HCURSOR)c);
	    }

	    EnterLeaveMessage();
	    hwindow = msg.hwnd;
	    break;

	case WM_ENTERWINDOW:
	case WM_LEAVEWINDOW:
	    hwindow = msg.hwnd;
	    break;

	case WM_KEYDOWN:
            msg.hwnd = GetSourceWindow();
	    TranslateMessage(&msg);
	    return false;

	case WM_CHAR:
            hwindow = msg.hwnd;
	    break;

	case WM_LBUTTONDOWN:
	    if (grabButton == NOGRAB) {
		grabButton = LEFTGRAB;
                SetCapture(msg.hwnd);
	    }
	    hwindow = msg.hwnd;
	    break;

	case WM_MBUTTONDOWN:
	    if (grabButton == NOGRAB) {
		grabButton = MIDDLEGRAB;
                SetCapture(msg.hwnd);
	    }
	    hwindow = msg.hwnd;
	    break;

	case WM_RBUTTONDOWN:
	    if (grabButton == NOGRAB) {
		grabButton = RIGHTGRAB;
                SetCapture(msg.hwnd);
	    }
	    hwindow = msg.hwnd;
	    break;

	case WM_LBUTTONUP:
	    if (grabButton == LEFTGRAB) {
		grabButton = NOGRAB;
		ReleaseCapture();
                msg.hwnd = GetSourceWindow();
	    }
	    hwindow = msg.hwnd;
	    break;

	case WM_MBUTTONUP:
	    if (grabButton == MIDDLEGRAB) {
		grabButton = NOGRAB;
		ReleaseCapture();
                msg.hwnd = GetSourceWindow();
	    }
	    hwindow = msg.hwnd;
	    break;

	case WM_RBUTTONUP:
	    if (grabButton == RIGHTGRAB) {
		grabButton = NOGRAB;
		ReleaseCapture();
                msg.hwnd = GetSourceWindow();
	    }
	    hwindow = msg.hwnd;
	    break;

	case WM_SETFOCUS:
	case WM_KILLFOCUS:
	    hwindow = msg.hwnd;
	    break;

	default:
	    DefWindowProc(msg.hwnd, msg.message, msg.wParam, msg.lParam);
	    return false;
    }

    /* only input events should get here */

    if (!_world->itable()->Find(i, (void*)hwindow)) {
	return false;
    } else {
	PropagateEnterLeave(&msg);
	if ((i->cursensor == nil) || (!i->cursensor->Interesting(m))) {
	    PropagateKeyboardMouse(&msg);
	    return false;
	}
    }

    m.target = i;
    m.y = i->ymax - m.y;

    if (!remove) {
	PostMessage(msg.hwnd, msg.message, msg.wParam, msg.lParam);
    }
    return true;
}

/*
 * Check to see if any input events of interest are pending.
 * This routine will return true even if the event is for another interactor.
 */

boolean Interactor::Check () {
    Event event;

    while (PeekMessage(
               &event.Rep()->Message(), NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD
           )) {
	if (GetEvent(event, false))
	    return true;
    }
    return false;
}

int Interactor::CheckQueue () {
    MSG msg;
    if (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE | PM_NOYIELD) != 0) {
	return (int)true;
    }
    return (int)false;
}

void Interactor::SendRedraw () {
    PAINTSTRUCT ps;

    BeginPaint((HWND)canvas->id, &ps);

    int w = ps.rcPaint.right - ps.rcPaint.left;
    int h = ps.rcPaint.bottom - ps.rcPaint.top;

    if (w > 0 && h > 0) {
        Redraw(
            ps.rcPaint.left,
            ymax - ps.rcPaint.bottom,
            ps.rcPaint.right,
            ymax - ps.rcPaint.top
        );
    }

    EndPaint((HWND)canvas->id, &ps);
}

void Interactor::SendResize (Coord x, Coord y, int w, int h) {
    left = x;
    bottom = parent->ymax - y - h + 1;
    if (canvas->width != w || canvas->height != h) {
	   canvas->width = w;
	   canvas->height = h;
	   xmax = w - 1;
	   ymax = h - 1;
	   Resize();
    }
}

void Interactor::SendActivate () {
    canvas->status = CanvasMapped;
    Activate();
}

void Interactor::SendDeactivate () {
    canvas->status = CanvasUnmapped;
    Deactivate();
}

void Interactor::Poll (Event& e) {
    POINT p;
    int state;

    GetCursorPos(&p);
    e.wx = p.x;
    e.wy = p.y;
    ScreenToClient((HWND)canvas->id, &p);
    e.x = p.x;
    e.y = ymax - p.y;
    e.FindWorld (canvas->id);

    state = GetAsyncKeyState(VK_CONTROL);
    e.control = (state & 0x8000) != 0;
    state = GetAsyncKeyState (VK_SHIFT);
    e.shift = (state & 0x8000) != 0;

    state = GetAsyncKeyState (VK_LBUTTON);
    e.leftmouse = (state & 0x8000) != 0;
    state = GetAsyncKeyState (VK_MBUTTON);
    e.middlemouse = (state & 0x8000) != 0;
    state = GetAsyncKeyState (VK_RBUTTON);
    e.rightmouse = (state & 0x8000) != 0;
}

void Interactor::Flush () {
}

void Interactor::Sync () {
}

void Interactor::GetRelative (Coord& x, Coord& y, Interactor* rel) {
    register Interactor* t, * r;
    Coord tx, ty, rx, ry;

    if (parent == nil) {
	if (rel == nil || rel->parent == nil) {
	    // world relative to world -- nop
	    return;
	}
	// world relative to interactor is relative to interactor's l, b
	rx = 0; ry = 0;
	rel->GetRelative(rx, ry);
	x = x - rx;
	y = y - ry;
	return;
    }
    tx = x; ty = y;
    t = this;
    for (t = this; t->parent->parent != nil; t = t->parent) {
	tx += t->left;
	ty += t->bottom;
    }
    if (rel == nil || rel->parent == nil) {
	r = nil;
    } else {
	rx = 0; ry = 0;
	for (r = rel; r->parent->parent != nil; r = r->parent) {
	    rx += r->left;
	    ry += r->bottom;
	}
    }
    if (r == t) {
	// this and rel are within same top-level interactor
	x = tx - rx;
	y = ty - ry;
    } else {
	Interactor* w;
	w = (rel == nil) ? t->parent : rel;

	POINT p;
	p.x = x;
	p.y = ymax - y;
	ClientToScreen((HWND)canvas->id, &p);
	ScreenToClient((HWND)w->canvas->id, &p);
	x = p.x;
	y = w->ymax - p.y;
    }
}

void Interactor::DoSetCursor (Cursor* c) {
    HCURSOR hCursor = (c != nil) ?
                      (HCURSOR)c->Id() : (HCURSOR)defaultCursor->Id();
    ::SetCursor((HCURSOR)hCursor);

}

void Interactor::DoSetName (const char* s) {
    SetWindowText((HWND)canvas->id, (LPSTR)s);
}

void Interactor::DoSetGeometry() {
/*    Coord x, y;
    unsigned int w, h;
    x = left;
    y = bottom;
    World* world = GetWorld();
    unsigned int m = world->GetGeometry(this, x, y, w, h);
    if (m != 0 && w > 0 && h > 0) {
	Coord new_top = world->ymax - y - h;
	MoveWindow((HWND)canvas->id, x, new_top, w, h, false);
	left = x;
	bottom = y;
	xmax = w - 1;
	ymax = h - 1;
	canvas->width = w;
	canvas->height = h;
	Resize();
    }
*/
}

void Interactor::DoSetGroupLeader (Interactor* leader) {
}

void Interactor::DoSetTransientFor (Interactor* owner) {
}

void Interactor::DoSetIconName (const char* name) {
}

void Interactor::DoSetIconBitmap (Bitmap* bitmap) {
}

void Interactor::DoSetIconMask (Bitmap* mask) {
}

void Interactor::DoSetIconInteractor (Interactor* icon) {
    Canvas* dummycanvas = nil;
    Canvas*& iconcanvas = icon ? icon->canvas : dummycanvas;
    if (icon != nil) {
	PlaceIcon(icon, iconcanvas);
    }
}

void Interactor::DoSetIconGeometry (const char* g) {
/*    if (g!= nil) {
	Coord x = 0;
	Coord y = 0;
	unsigned int w = shape->width;
	unsigned int h = shape->height;
	Bitmap* b = GetIconBitmap();
	if (b != nil) {
	    w = b->Width();
	    h = b->Height();
	}
	Interactor* icon = GetIconInteractor();
	if (icon != nil) {
	    w = icon->GetShape()->width;
	    h = icon->GetShape()->height;
	}

	World* world = GetWorld();
	unsigned r = world->ParseGeometry(g, x, y, w, h);
	if ((r & GeomXNegative) != 0) {
	    x = world->Width() + x -w;
	}
	if ((r & GeomYNegative) != 0) {
	    y = world->Height() + y - h;
	}
    }
*/
}

void Interactor::Iconify () {
    Interactor* icon_ia = GetIconInteractor();
    if (canvas != nil && canvas->status == CanvasMapped) {
	canvas->status = CanvasUnmapped;
	if (icon_ia == nil) {
	    CloseWindow((HWND)canvas->id);
	} else {
	    ShowWindow((HWND)canvas->id, SW_HIDE);
	    Canvas* c = icon_ia->GetCanvas();
	    if (c != nil && c->status == CanvasUnmapped) {
		ShowWindow((HWND)c->id, SW_SHOWNORMAL);
		c->status = CanvasMapped;
	    }
	}
    }
}

void Interactor::DeIconify () {
    Interactor* icon_ia = GetIconInteractor();
    if (canvas != nil) {
	if (canvas->status == CanvasUnmapped) {
	    if (icon_ia == nil) {
		OpenIcon((HWND)canvas->id);
		return;
	    } else {
		Canvas* c = icon_ia->GetCanvas();
		if (c != nil) {
		    if (c->status == CanvasMapped) {
			ShowWindow((HWND)c->id, SW_HIDE);
			c->status = CanvasUnmapped;
		    }
		}
	    }
	    ShowWindow((HWND)canvas->id, SW_SHOWNORMAL);
	    canvas->status = CanvasMapped;
	}
    }
}

void Interactor::GetOrigin(Coord& x, Coord& y) {
    RECT rect;
    GetWindowRect((HWND)canvas->id, &rect);
    if (parent->parent != nil) {
	    POINT p;
	    p.x = rect.left;
	    p.y = rect.top;
	    ScreenToClient((HWND)parent->canvas->id, &p);
	    x = p.x;
	    y = p.y;
	return;
    }
    x = rect.left + _world->XBorder;
    y = rect.top + _world->YBorder + _world->YCaption;
    return;
}

