/* canvas.c: behandelt events afkomstig van een canvas widget */

#include <Xm/Xm.h>
#include <X11/cursorfont.h>

#include "canvas.h"
#include "render.h"
#include "camera.h"
#include "surfedit.h"
#include "globals.h"

#include "debug.h"
#include "error.h"
#include "select.h"

/* grootte van de canvas mode stack */
#define CANVASMODESTACKSIZE 5
static int cursordefined = 0, canvasmode = CANVASMODE_NORMAL;
static int modestack[CANVASMODESTACKSIZE], modestackidx;

static Widget canvas;  

static Cursor working_cursor, select_cursor, render_cursor;
#define WORKING_CURSOR	XC_watch
#define SELECT_CURSOR XC_crosshair
#define RENDER_CURSOR XC_spraycan

void CanvasInit(Widget w)
{
	canvas = w;
	canvasmode = CANVASMODE_NORMAL;
	modestackidx = 0;
	modestack[modestackidx] = canvasmode;
	cursordefined = FALSE;

/* cursor om aan te duiden dat het programma aan het rekenen is */
	working_cursor = XCreateFontCursor(XtDisplay(canvas), WORKING_CURSOR);	
/* cursor om aan te duiden dat een patch gelecteerd kan worden. */
	select_cursor = XCreateFontCursor(XtDisplay(canvas), SELECT_CURSOR); 

	render_cursor = XCreateFontCursor(XtDisplay(canvas), RENDER_CURSOR);   
}

static void CanvasSetMode(int mode)
{
	switch (mode) {
	case CANVASMODE_NORMAL:
		if (cursordefined) {
			XUndefineCursor(XtDisplay(canvas), XtWindow(canvas));
			cursordefined = FALSE;
		}

		canvasmode = CANVASMODE_NORMAL;
		break;
	case CANVASMODE_WORKING:
		/* horloge als cursor */
		XDefineCursor(XtDisplay(canvas), XtWindow(canvas), working_cursor);
		XFlush(XtDisplay(canvas));
		cursordefined = TRUE;

		canvasmode = CANVASMODE_WORKING;
		break;
	case CANVASMODE_SELECT_PATCH:
		/* crosshair cursor */
		XDefineCursor(XtDisplay(canvas), XtWindow(canvas), select_cursor);
		XFlush(XtDisplay(canvas));
		cursordefined = TRUE;

		canvasmode = CANVASMODE_SELECT_PATCH;
		break;
	case CANVASMODE_RENDER:
		/* crosshair cursor */
		XDefineCursor(XtDisplay(canvas), XtWindow(canvas), render_cursor);
		XFlush(XtDisplay(canvas));
		cursordefined = TRUE;

		canvasmode = CANVASMODE_RENDER;
		break;
	default:
		Fatal(4, "CanvasSetMode", "Ongeldige mode %d - interne fout (fataal).", mode);
		break;
	}

	modestack[modestackidx] = canvasmode;
}

void CanvasPushMode(int mode)
{
	modestackidx++;
	if (modestackidx >= CANVASMODESTACKSIZE) 
		Fatal(4, "CanvasPushMode", "Mode stack size (%d) exceeded.", CANVASMODESTACKSIZE);

	CanvasSetMode(mode);
}

void CanvasPullMode(void)
{
	modestackidx--;
	if (modestackidx < 0)
		Fatal(4, "CanvasPullMode", "Canvas mode stack underflow.\n");

	CanvasSetMode(modestack[modestackidx]);
}

void CanvasResizeCallback(Widget canvas, XtPointer client_data, XtPointer call_data)
{
	Dimension newwidth, newheight;

	XtVaGetValues(canvas,
		      XmNheight, &newheight,
		      XmNwidth, &newwidth,
		      NULL);

	CameraSet(&Camera, &Camera.eyep, &Camera.lookp, &Camera.updir,
		           Camera.fov, newwidth, newheight, &Camera.background);
	/* if (!working) */ RenderScene();
}

void CanvasExposeCallback(Widget canvas, XtPointer client_data, XtPointer call_data)
{
	int count = ((XmDrawingAreaCallbackStruct *)call_data)->event->xexpose.count;

	if (count == 0)
		RenderScene();
}

void CanvasExposeEvent(Widget canvas, XtPointer client_data, XEvent *event)
{
	if (event->type == GraphicsExpose && event->xexpose.count == 0)
		RenderScene();
}

static void DoMotion(int x, int y, int lastx, int lasty, int buttonpressed)
{
	Dimension maxx, maxy;
	float fov, aspect, w, view_dist;
	VECTOR d;

	XtVaGetValues(canvas,
		      XmNheight, &maxy,
		      XmNwidth, &maxx,
		      NULL);

	switch (buttonpressed) {
/* rond zich heen kijken: camera blijft op dezelfde positie, kijkrichting verandert */
	case 1:	
		fov = 2. * Camera.fov * M_PI / 180.;
		aspect = (float)maxx/(float)maxy;
		if (aspect > 1) fov *= aspect;
		
/* links-rechts kijken */
		if (x != lastx) CameraTurnRight(&Camera, 
						(float)(x - lastx)/(float)maxx * fov);
/* naar boven-onder kijken: geeft problemen als je teveel naar boven of naar onder kijkt */
		if (y != lasty) CameraTurnUp(&Camera,
					     (float)(lasty - y)/(float)maxy * fov / aspect);
		break;
	case 2:
		VECTORSUBSTRACT(Camera.lookp, Camera.eyep, d);
		fov = 2. * Camera.fov * M_PI / 180.;
		view_dist = VECTORNORM(d);
		aspect = (float)maxx/(float)maxy;
		if (aspect > 1) fov *= aspect;
		w = view_dist * fov;

#ifdef NEVER
/* Camera rond de kijkrichting draaien. Kijkrichting blijft onveranderd */
		if (x != lastx) CameraTilt(&Camera, 
					   (float)(x - lastx)/(float)maxx * fov);
#endif /*NEVER*/

/* links-rechts bewegen met de camera. Kijkrichting blijft onveranderd */
		if (x != lastx) CameraMoveRight(&Camera, 
						(float)(x - lastx)/(float)maxx * w);

/* boven-onder bewegen met de camera. Kijkrichting blijft onveranderd */
		if (y != lasty) CameraMoveUp(&Camera, 
					     (float)(y - lasty)/(float)maxx * w / aspect);

		break;
	case 3:
		VECTORSUBSTRACT(Camera.eyep, Camera.lookp, d);
		view_dist = VECTORNORM(d);
		w = view_dist * 2. * tan(Camera.fov * M_PI / 180.);
		aspect = (float)maxx/(float)maxy;
		if (aspect > 1) w *= aspect;
			
/* links-rechts bewegen met de camera. Kijkrichting blijft onveranderd */
		if (x != lastx) CameraMoveRight(&Camera, 
						(float)(x - lastx)/(float)maxx * w);
				       
/* volgens de kijkrichting bewegen met de camera. Kijkrichting blijft onveranderd */
		if (y != lasty) CameraMoveForward(&Camera, 
						  (float)(lasty - y)/(float)maxy * 2. * view_dist);
		break;
	default:
		/* niets doen */
	}
	
	/* if (!working) */ RenderScene();
}

void CanvasMouseEvent(Widget canvas, XtPointer client_data, XEvent *event)
{
	static int lastx, lasty, buttonpressed;
	int x, y;

	switch (event->type) {
	case ButtonPress: 
		lastx = event->xbutton.x;
		lasty = event->xbutton.y;

		switch (event->xbutton.button) {
		case Button1: buttonpressed = 1; break;
		case Button2: buttonpressed = 2; break;
		case Button3: buttonpressed = 3; break;
		default: buttonpressed = 0; break;
		}
		break;
	
	case ButtonRelease: 
		x = event->xbutton.x;
		y = event->xbutton.y;

		if (canvasmode == CANVASMODE_SELECT_PATCH) {
			switch (buttonpressed) {
			case 1:	/* SelectPatch() is supposed to know what to do with the selected patch ... */
				SelectPatch(event->xbutton.x, event->xbutton.y);
				break;
			default: /* cancel the selection */
				CanvasPullMode();
				break;
			}
		} else {
#ifdef SLOW_RENDERER
			DoMotion(x, y, lastx, lasty, buttonpressed);

			lastx = x;
			lasty = y;
#endif /*SLOW_RENDERER*/
		}

		buttonpressed = 0;
		break;
	
	case MotionNotify:
		x = event->xmotion.x;
		y = event->xmotion.y;
#ifndef SLOW_RENDERER
		DoMotion(x, y, lastx, lasty, buttonpressed);

		lastx = x;
		lasty = y;
#endif /*SLOW_RENDERER*/
		break;

	default:
		DPRINTF(stderr, "event->type = %d\n", event->type);
	}
}


