#include "glhand.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <GL/glut.h>

glhand_t glhand ;
char* eventtypes[]=
{ "", "KEY", "SPECIALKEY", "MOUSEDOWN", "MOUSEUP", "MOUSEMOVE" } ;

actionlist_t* actionlist_new (char* name) {
  actionlist_t* list = malloc (sizeof(actionlist_t)) ;
  list->name = strdup (name) ;
  list->n_actions = 0 ;
  list->actions = NULL ;
  list->next = NULL ;
  return list ;
}

void actionlist_delete (actionlist_t* list) {
  free (list->actions) ;
  free (list->name) ;
  free (list) ;
}

void actionlist_chain (actionlist_t* main, actionlist_t* sub) {
  while (main->next) main=main->next ;
  main->next = sub ;
}

void glhand_keyboardFunc(unsigned char key, int x, int y) {
  glhand_motionFunc (x,y) ;
  glhand_postevent (EVENT_KEY, key, x, y) ;
}

void glhand_motionFunc(int x, int y) {
  int deltax = x-glhand.lastx ;
  int deltay = y-glhand.lasty ;
  glhand.lastx = x ;
  glhand.lasty = y ;
  if (deltax==0 && deltay == 0) return ;
  glhand_postevent (EVENT_MOUSEMOVE, 0, deltax, deltay) ;
}

void glhand_mouseFunc(int button, int state, int x, int y) {
  int event = (state==GLUT_DOWN) ? EVENT_MOUSEDOWN : EVENT_MOUSEUP ;
  glhand_motionFunc (x,y) ;
  glhand_postevent (event, button, x, y) ;
}

void glhand_specialFunc(int code, int x, int y) {
  glhand_motionFunc (x,y) ;
  glhand_postevent (EVENT_SPECIALKEY, code, x, y) ;
}

void glhand_installcallbacks () {
  glutKeyboardFunc (glhand_keyboardFunc) ;
  glutMotionFunc (glhand_motionFunc) ;
  glutMouseFunc (glhand_mouseFunc) ;
  glutSpecialFunc (glhand_specialFunc) ;
}

void glhand_init () {
  glhand.root = NULL ;
  glhand.actionlist = NULL ;
}

void glhand_addzone (zone_t* zone) {
  zone->next = glhand.root ;
  glhand.root = zone ;
}

void glhand_addactionlist (actionlist_t* list) {
  list->next = glhand.actionlist ;
  glhand.actionlist = list ;
}


void glhand_postevent (int eventtype, int eventdata, int x, int y) {
  zone_t* zone = glhand.root ;
  while (zone) {
    int i ;
    for (i=0; i<zone->n_mappings; i++)
      if (zone->mappings[i].event == eventtype
	  && zone->mappings[i].eventdata == eventdata) {
	action_t* action = zone->mappings[i].action ;
	int accepted ;
	//fprintf (stderr, "POSTEVENT: %s\n", action->name) ;
	accepted=action->callback (action->data, eventtype, eventdata, x, y) ;
	if (accepted) return ;
      }
    zone = zone->next ;
  }
}

void glhand_dump ()
{
  int i ;
  zone_t* zone = glhand.root ;
  fprintf (stderr, "Dumping all zones:\n") ;
  while (zone) {
    fprintf (stderr, "Zone %x\n", (int)zone) ;
    for (i=0; i<zone->n_mappings; i++) {
      fprintf (stderr, "%4d: eventtype=%-10s ",
	       i, eventtypes[zone->mappings[i].event]) ;
      switch (zone->mappings[i].event) {
      case EVENT_KEY:
	fprintf (stderr, "eventdata='%c'     ", zone->mappings[i].eventdata) ;
	break;
      default:
	fprintf (stderr, "eventdata=%-7d ", zone->mappings[i].eventdata) ;
	break;
      }
      fprintf (stderr, "action=%s\n", zone->mappings[i].action->name) ;
    }
    zone = zone->next ;
  }
  fprintf (stderr, "End of dump.\n") ;
}

