/*         ______   ___    ___
 *        /\  _  \ /\_ \  /\_ \ 
 *        \ \ \L\ \\//\ \ \//\ \      __     __   _ __   ___ 
 *         \ \  __ \ \ \ \  \ \ \   /'__`\ /'_ `\/\`'__\/ __`\
 *          \ \ \/\ \ \_\ \_ \_\ \_/\  __//\ \L\ \ \ \//\ \L\ \
 *           \ \_\ \_\/\____\/\____\ \____\ \____ \ \_\\ \____/
 *            \/_/\/_/\/____/\/____/\/____/\/___L\ \/_/ \/___/
 *                                           /\____/
 *                                           \_/__/
 *
 *      Utility to create mapping from X keycodes to Allegro scancodes.
 *
 *      By Michael Bukin, modified by Elias Pschernig.
 *
 *      See readme.txt for copyright information.
 */

#include <stdio.h>

#include <allegro.h>
#include <xalleg.h>
#include <allegro/internal/aintern.h>

#define FILENAME_LENGTH  1024

char unicode_description[256] = EMPTY_STRING;
static DIALOG main_dialog[];
static DIALOG keymap_dialog[];

static int black = 0;
static int white = 1;
static int red = 2;
static int yellow = 3;

static volatile int waiting_for_key = 0;
static volatile int new_keycode = 0;
static int keycode_to_scancode[256];

static void get_raw_keycode(int pressed, int keycode)
{
   if (pressed && waiting_for_key) {
      new_keycode = keycode;
      waiting_for_key = 0;
   }
}

static AL_CONST char *keycode_getter(int index, int *list_size)
{
   if (index >= 0) {
      return scancode_to_name(index + 1);
   }
   else {
      *list_size = KEY_MAX - 1;
      return NULL;
   }
}

static DIALOG keymap_dialog[] = {
   /* 0 */{d_clear_proc,    0,   0, 250, 230, 0,   0,   0, 0,      0, 0, 0, NULL, NULL},
   /* 1 */{d_ctext_proc,  125,  10,   0,  16, 0, 255,   0, 0,      0, 0, "Select Keycode:", NULL, NULL},
   /* 2 */{d_list_proc,     0,  32, 230,  92, 0, 255,   0, D_EXIT, 0, 0, (void *)keycode_getter, NULL, NULL},
   /* 3 */{d_ctext_proc,  125, 142,   0,  16, 0, 255,   0, 0,      0, 0, unicode_description, NULL, NULL},
   /* 4 */{d_button_proc,  10, 164, 230,  16, 0, 255,   0, D_EXIT, 0, 0, "Define &X Key !", NULL, NULL},
   /* 5 */{d_button_proc,  10, 186, 110,  16, 0, 255,  27, D_EXIT, 0, 0, "&Cancel", NULL, NULL},
   /* 6 */{d_button_proc, 130, 186, 110,  16 , 0, 255,  0, D_EXIT, 0, 0, "&Done", NULL, NULL},
   /* 7 */{d_ctext_proc,  125, 208, 0,    16, 0, 255,   0, 0,      0, 0, "", NULL, NULL},
          {d_yield_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL},
          {NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL}
};

/* handle the setup command */
static void setup_all_keys(void)
{
   int focus = 2;
   int i;

   /* Prepare dialog.  */
   set_dialog_color(keymap_dialog, black, white);
   centre_dialog(keymap_dialog);

   /* Parse input.  */
   while (1) {
      focus = do_dialog(keymap_dialog, focus);
      switch (focus) {
	 case 2:
	 case 4:

	    textprintf_centre_ex (screen, font,
	       keymap_dialog[7].x, keymap_dialog[7].y, red, -1,
	       "Press a key to map to the current scancode");
	    textprintf_centre_ex (screen, font,
	       keymap_dialog[7].x, keymap_dialog[7].y + 8, red, -1,
	       "(or a mouse button to leave it unchanged)");

	    /* Wait for new key press.  */
	    new_keycode = -1;
	    waiting_for_key = 1;

	    do {
	       poll_keyboard();
	       poll_mouse();

	       if (mouse_b)
		  waiting_for_key = 0;
	    }
	    while (waiting_for_key);

	    /* Save keycode to scancode mapping.  */
	    if ((new_keycode >= 0) && (new_keycode < 256)) {
	       keycode_to_scancode[new_keycode] = keymap_dialog[2].d1 + 1;
	    }

	    clear_keybuf();

	    break;
	 case 5:
	    return;
	 case 6:
	    for (i = 0; i < 256; i++) {
               if (keycode_to_scancode[i] >= 0)
		  _xwin.keycode_to_scancode[i] = keycode_to_scancode[i];
            }
	    return;
      }
   }
}

static void test_key_map(void)
{
   int i, k, u;
   static int key_was_pressed[KEY_MAX + 1] = {0};
   static int key_is_pressed[KEY_MAX + 1] = {0};
   static char *welcome[] = {
      "Key that is pressed now is marked with red",
      "Key that was pressed is marked with yellow",
      "Press mouse button or Escape to exit test",
      0
   };

   /* Clear screen and output prompt.  */
   clear_to_color(screen, white);
   for (i = 0; welcome[i] != 0; i++)
      textout_ex(screen, font, welcome[i], 8, i * 8 + 8, black, -1);

   clear_to_color(screen, white);
   for (i = 0; i < KEY_MAX; i++)
      textprintf_ex(screen, font, 32 + (i % 4) * 160,
	      32 + (i / 4) * 14, black, -1, "%s", scancode_to_name (i));
   do {
      poll_keyboard();
      poll_mouse();
   }
   while ((key[KEY_ESC]) || (mouse_b));

   do {
      
      while (keypressed()) {
	 u = ureadkey (&k);
	 textprintf_centre_ex (screen, font, SCREEN_W / 2, 8,
	    red, white, ">   %c   <", u);
      }
      
      poll_keyboard();
      poll_mouse();

      for (i = 0; i < KEY_MAX; i++) {
	 if (key[i])
	    key_was_pressed[i] = key_is_pressed[i] = 1;
	 else
	    key_is_pressed[i] = 0;
      }

      for (i = 0; i < KEY_MAX; i++) {
	 int x = 16 + (i % 4) * 160;
	 int y = 32 + (i / 4) * 14;

	 if (key_is_pressed[i])
	    rectfill(screen, x, y, x + 7, y + 7, red);
	 else if (key_was_pressed[i])
	    rectfill(screen, x, y, x + 7, y + 7, yellow);
	 else
	    rectfill(screen, x, y, x + 7, y + 7, white);
      }
   }
   while ((!key[KEY_ESC]) && (!mouse_b));

   do {
      poll_keyboard();
      poll_mouse();
   }
   while ((key[KEY_ESC]) || (mouse_b));

   clear_keybuf();
}

/* handle the save command */
static void save_key_map(void)
{
   int i;
   char *section, *option_format, option[80], tmp1[80], tmp2[80];

   set_config_file("xkeymap.cfg");
   section = uconvert_ascii("xkeymap", tmp1);
   option_format = uconvert_ascii("keycode%d", tmp2);

   for (i = 0; i < 256; i++) {
      if (keycode_to_scancode[i] > 0) {
	 usprintf(option, option_format, i);
	 set_config_int(section, option, keycode_to_scancode[i]);
      }
   }
}

/* reads a specific keymapping table from the config file */
void load_table(unsigned short *table, char *section)
{
   char name[80];
   int i;

   for (i=0; i<KEY_MAX; i++) {
      sprintf(name, "key%d", i);
      table[i] = get_config_int(section, name, table[i]);
   }
}

static DIALOG main_dialog[] = {
   /* 0 */{d_clear_proc,   0,   0, 250, 102, 0,   0,   0, 0,      0, 0, 0, NULL, NULL},
   /* 1 */{d_ctext_proc, 120,  10,   0,  16, 0, 255,   0, 0,      0, 0, "X Key Mapping Utility", NULL, NULL},
   /* 2 */{d_ctext_proc,  10,  32, 230,  16, 0, 255,   0, D_EXIT, 0, 0, "~", NULL, NULL},
   /* 3 */{d_button_proc, 10,  54, 230,  16, 0, 255, 'm', D_EXIT, 0, 0, "Setup X key &mappings", NULL, NULL},
   /* 4 */{d_button_proc, 10,  76, 230,  16, 0, 255, 't', D_EXIT, 0, 0, "&Test X key mappings", NULL, NULL},
   /* 5 */{d_button_proc, 10,  98, 230,  16, 0, 255, 's', D_EXIT, 0, 0, "&Save and exit", NULL, NULL},
   /* 6 */{d_button_proc, 10, 120, 230,  16, 0, 255,  27, D_EXIT, 0, 0, "E&xit", NULL, NULL},
          {d_yield_proc, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL},
          {NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, NULL, NULL}
};

static void show_main_dialog(void)
{
   int focus = 1;

   /* Prepare dialog.  */
   set_dialog_color(main_dialog, black, white);
   centre_dialog(main_dialog);

   /* Parse input.  */
   while (1) {
      focus = do_dialog(main_dialog, focus);
      switch (focus) {
	 case 3:
	    /* Setup all key mappings.  */
	    setup_all_keys();
	    break;
	 case 4:
	    /* Test key mappings.  */
	    test_key_map();
	    break;
	 case 5:
	    /* Save and quit.  */
	    save_key_map();
	    return;
	 case 6:
	    /* Just exit.  */
	    return;
      }
   }
}

int main(void)
{
   int i;

   allegro_init();
   install_keyboard();
   install_mouse();
   install_timer();

   if (set_gfx_mode(GFX_SAFE, 640, 480, 0, 0) != 0) {
      set_gfx_mode(GFX_TEXT, 0, 0, 0, 0);
      allegro_message("Unable to set any graphic mode\n%s\n", allegro_error);
      exit(EXIT_FAILURE);
   }
   set_palette(desktop_palette);
   gui_fg_color = palette_color[255];
   gui_bg_color = palette_color[16];

   black = makecol(0, 0, 0);
   white = makecol(255, 255, 255);
   red = makecol(255, 0, 0);
   yellow = makecol(255, 255, 0);

   /* Clear key mappings.  */
   for (i = 0; i < 256; i++)
      keycode_to_scancode[i] = -1;

   /* Hook the X keyboard callback.  */
   _xwin_keyboard_callback = get_raw_keycode;

   show_main_dialog();

   return 0;
}

END_OF_MAIN()

