/*
 *  Copyright (C) Samuel Thibault <samuel.thibault@ens-lyon.org>
 *
 * This program is free software ; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation ; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY ; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with the program ; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/keyboard.h>
#include <linux/kd.h>
#include <linux/input.h>
#ifndef KT_BRL
#define KT_BRL 14
#endif
#define KG_BRL KG_CTRLR

int fd;

void setentry(struct kbentry *entry) {
  if (ioctl(fd, KDSKBENT, entry)) {
    if (errno == EINVAL) {
      fprintf(stderr,"Invalid argument while setting key. Do you really have a >= 2.6.17-rc1 kernel?\n");
      exit(2);
    }
    perror("ioctl");
    fprintf(stderr, "(%d,KDSKBENT,{%d,%d,%x})\n", fd, entry->kb_table, entry->kb_index, entry->kb_value);
  }
}

int main(int argc, char *argv[]) {
  char c;
  struct kbentry entries[] = {
#define NORM_ENTRY(i,c) \
    { (1<<KG_BRL), (i), (c) }, \
    { (1<<KG_BRL)|(1<<KG_SHIFT), (i), (c) },
#define BRL_ENTRY(i,c) \
    { (1<<KG_ALT)|(1<<KG_ALTGR), (i), K(KT_BRL, c) }, \
    { (1<<KG_ALT)|(1<<KG_ALTGR)|(1<<KG_SHIFT), (i), K(KT_BRL, c) }, \
    NORM_ENTRY(i, K(KT_BRL, c))
    BRL_ENTRY(KEY_KP4, 1)
    BRL_ENTRY(KEY_KP1, 2)
    BRL_ENTRY(KEY_KP0, 3)
    BRL_ENTRY(KEY_KP5, 4)
    BRL_ENTRY(KEY_KP6, 5)
    BRL_ENTRY(KEY_KPPLUS, 6)
    { 0, KEY_PAUSE, K(KT_LOCK, KG_BRL) },
    { 1<<KG_SHIFT, KEY_PAUSE, K(KT_LOCK, KG_BRL) },
    NORM_ENTRY(KEY_PAUSE, K(KT_LOCK, KG_BRL))
    NORM_ENTRY(KEY_BACKSPACE, '\b')
    NORM_ENTRY(KEY_ENTER, '\n')
    NORM_ENTRY(KEY_SPACE, ' ')
    NORM_ENTRY(KEY_KPENTER, '\n')
    NORM_ENTRY(KEY_HOME, K_FIND)
    NORM_ENTRY(KEY_UP, K_UP)
    NORM_ENTRY(KEY_PAGEUP, K_PGUP)
    NORM_ENTRY(KEY_LEFT, K_LEFT)
    NORM_ENTRY(KEY_RIGHT, K_RIGHT)
    NORM_ENTRY(KEY_END, K_SELECT)
    NORM_ENTRY(KEY_DOWN, K_DOWN)
    NORM_ENTRY(KEY_PAGEDOWN, K_PGDN)
    NORM_ENTRY(KEY_INSERT, K_INSERT)
    NORM_ENTRY(KEY_DELETE, K_REMOVE)
    { (1<<KG_ALT), KEY_RIGHTALT, K_ALTGR },
    { (1<<KG_ALT)|(1<<KG_SHIFT), KEY_RIGHTALT, K_ALTGR },
    { (1<<KG_ALT)|(1<<KG_ALTGR), KEY_RIGHTALT, K_ALTGR },
    { (1<<KG_ALT)|(1<<KG_ALTGR)|(1<<KG_SHIFT), KEY_RIGHTALT, K_ALTGR },
    { (1<<KG_ALTGR), KEY_LEFTALT, K_ALT },
    { (1<<KG_ALTGR)|(1<<KG_SHIFT), KEY_LEFTALT, K_ALT },
    { (1<<KG_ALT)|(1<<KG_ALTGR), KEY_LEFTALT, K_ALT },
    { (1<<KG_ALT)|(1<<KG_ALTGR)|(1<<KG_SHIFT), KEY_LEFTALT, K_ALT },
  },
  alphaentries [] = {
    BRL_ENTRY(KEY_A, 7)
    BRL_ENTRY(KEY_S, 3)
    BRL_ENTRY(KEY_D, 2)
    BRL_ENTRY(KEY_F, 1)
    BRL_ENTRY(KEY_G, 0)
    BRL_ENTRY(KEY_V, 0)
    BRL_ENTRY(KEY_N, 0)
    BRL_ENTRY(KEY_H, 0)
    BRL_ENTRY(KEY_J, 4)
    BRL_ENTRY(KEY_K, 5)
    BRL_ENTRY(KEY_L, 6)
    BRL_ENTRY(KEY_SEMICOLON, 8)
  },
  rightentries [] = {
    BRL_ENTRY(KEY_J, 1)
    BRL_ENTRY(KEY_N, 2)
    BRL_ENTRY(KEY_B, 3)
    BRL_ENTRY(KEY_K, 4)
    BRL_ENTRY(KEY_L, 5)
    BRL_ENTRY(KEY_M, 6)
  },
  leftentries [] = {
    BRL_ENTRY(KEY_Q, 3)
    BRL_ENTRY(KEY_S, 2)
    BRL_ENTRY(KEY_D, 1)
    BRL_ENTRY(KEY_F, 4)
    BRL_ENTRY(KEY_V, 5)
    BRL_ENTRY(KEY_B, 6)
  },
    *entry;
  int i, left = 0, right = 0;
  for (i=1;i<argc;i++) {
    if (!strcmp(argv[i],"--help")
      ||!strcmp(argv[i],"-h")
	) {
      printf("usage: %s [ -h | -l | -r]\n\n", argv[0]);
      printf("\t-h print this help\n");
      printf("\t-l bind left hand's [aq]sdfvb to 6-dot braille keyboard\n");
      printf("\t-r bind right hand's bnjkl[m;] to 6-dot braille keyboard\n");
      return 0;
    } else if (!strcmp(argv[i], "-l")) {
      if (right) {
	fprintf(stderr,"can't set left hand binding when right hand is already set\n");
	return 1;
      }
      left = 1;
    } else if (!strcmp(argv[i], "-r")) {
      if (left) {
	fprintf(stderr,"can't set right hand binding when left hand is already set\n");
	return 1;
      }
      right = 1;
    }
  }
  fd = open("/dev/tty", O_RDONLY);
  if (fd<0 || ioctl(fd, KDGKBTYPE, &c)) {
    fd = open("/dev/tty0", O_RDONLY);
    if (fd<0) {
      perror("opening /dev/tty0");
      fprintf(stderr,"must be run on the text console or as root\n");
      return 1;
    }
  }

#define DOENTRIES(entries) \
  for (entry = entries; entry < &entries[sizeof(entries)/sizeof(*entries)]; entry++) setentry(entry)

  DOENTRIES(entries);

  if (left)
    DOENTRIES(leftentries);
  else if (right)
    DOENTRIES(rightentries);
  else DOENTRIES(alphaentries);

  return 0;
}
