/*
  screen-uim - GNU screen filter with uim
  base on agent.c in uim-0.1.5

 $Id: screen-uim.c,v 1.4 2003/12/01 06:48:24 knok Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <signal.h>
#include <termios.h>
#include <unistd.h>

#include <uim/uim.h>
#include "udlib.h"

static char esc_delete[]  = {0x1b, 0x5b, 0x33, 0x7e, 0};
static char esc_left[]    = {0x1b, 0x5b, 0x44, 0 };
static char esc_up[]      = {0x1b, 0x5b, 0x41, 0 };
static char esc_right[]   = {0x1b, 0x5b, 0x43, 0 };
static char esc_down[]    = {0x1b, 0x5b, 0x42, 0 };
static char esc_prior[]   = {0x1b, 0x5b, 0x35, 0x7e, 0 };  /* page up */
static char esc_next[]    = {0x1b, 0x5b, 0x36, 0x7e, 0 };   /* page down */
static char esc_home[]    = {0x1b, 0x5b, 0x31, 0x7e, 0 };
static char esc_end[]     = {0x1b, 0x5b, 0x34, 0x7e, 0 };
static char esc_f1[]      = {0x1b, 0x4f, 0x50, 0 };
static char esc_f2[]      = {0x1b, 0x4f, 0x51, 0 };
static char esc_f3[]      = {0x1b, 0x4f, 0x52, 0 };
static char esc_f4[]      = {0x1b, 0x4f, 0x53, 0 }; 
static char esc_f5[]      = {0x1b, 0x5b, 0x31, 0x35, 0x7e, 0 };
static char esc_f6[]      = {0x1b, 0x5b, 0x31, 0x37, 0x7e, 0 };
static char esc_f7[]      = {0x1b, 0x5b, 0x31, 0x38, 0x7e, 0 };
static char esc_f8[]      = {0x1b, 0x5b, 0x31, 0x39, 0x7e, 0 };
static char esc_f9[]      = {0x1b, 0x5b, 0x32, 0x30, 0x7e, 0 };
static char esc_f10[]     = {0x1b, 0x5b, 0x32, 0x31, 0x7e, 0 };
static char esc_f11[]     = {0x1b, 0x5b, 0x32, 0x33, 0x7e, 0 };
static char esc_f12[]     = {0x1b, 0x5b, 0x32, 0x34, 0x7e, 0 };

static struct agent_context {
  uim_context uc;
  int sfd;
  struct agent_context *next;
} default_context;

#define BUFSIZE 2048

char tickbuf[BUFSIZE];
char modebuf[BUFSIZE];

void settermraw(void);
void resetterm(void);
void sighandler(int);

static struct termios tbuf;

#define MAXLINE 2048
char *sockpath = NULL;

void
commit_cb(void *ptr, const char *str)
{
  printf("%s", str);
}

void
clear_cb(void *ptr)
{
  tickbuf[0] = 0;
}

static void
pushback_cb(void *ptr, int attr, const char *str)
{
  strncat(tickbuf, str, BUFSIZE - 1);
}

static void
update_cb(void *ptr)
{
  char tmp[BUFSIZE * 2];
  sprintf(tmp, "[%s]:%s\n", modebuf, tickbuf);
#if DEBUG
  printf("%s", tmp);
#else 
  udsendto(((struct agent_context *) ptr)->sfd, tmp);
#endif
}

static void
mode_update_cb(void *ptr, int mode)
{
  uim_context uc = ((struct agent_context *) ptr)->uc;
  strncpy(modebuf, uim_get_mode_name(uc, mode), BUFSIZE-1);
}

static void
set_init_mode(uim_context uc)
{
  strncpy(modebuf, uim_get_mode_name(uc, uim_get_current_mode(uc)), BUFSIZE-1);
}

static void
init_agent(int sfd)
{
  uim_context uc;
  int nr;
  if (uim_init() == -1) {
    printf("failed to init\n");
    exit(0);
  }
  /**/
  uc =uim_create_context(&default_context,
			 "EUC-JP", NULL, "skk",
			 uim_iconv,
			 commit_cb);
  nr = uim_get_nr_im(uc);
  uim_set_preedit_cb(uc, clear_cb, pushback_cb, update_cb);
  set_init_mode(uc);
  uim_set_mode_cb(uc, mode_update_cb);
  default_context.uc = uc;
  default_context.sfd = sfd;
  default_context.next = NULL;
}

static int
needraw(int uc)
{
  if (uc >= 0x20 && uc <= 0x7e)
    return 0;
  switch (uc) {
  case 0x08: /* BS */
  case 0x0a: /* C-j */
  case 0x0d: /* CR */
  case 0x1b: /* ESC */
    return 0;
  }
  return 1;
}

static int
tty2key(char *keybuf, int i)
{
  if(i > 1) {

    if(!strcmp(esc_delete, keybuf))
      return UKey_Delete;
    if(!strcmp(esc_left  , keybuf))
      return UKey_Left;
    if(!strcmp(esc_up    , keybuf))
      return UKey_Up ;
    if(!strcmp(esc_right , keybuf))
      return UKey_Right;
    if(!strcmp(esc_down  , keybuf))
      return UKey_Down;
    if(!strcmp(esc_prior , keybuf))
      return UKey_Prior;
    if(!strcmp(esc_next  , keybuf))
      return UKey_Next;
    if(!strcmp(esc_home  , keybuf))
      return UKey_Home;
    if(!strcmp(esc_end   , keybuf))
      return UKey_End;
    if(!strcmp(esc_f1    , keybuf))
      return UKey_F1;
    if(!strcmp(esc_f2    , keybuf))
      return UKey_F2;
    if(!strcmp(esc_f3    , keybuf))
      return UKey_F3;
    if(!strcmp(esc_f4    , keybuf))
      return UKey_F4;
    if(!strcmp(esc_f5    , keybuf))
      return UKey_F5;
    if(!strcmp(esc_f6    , keybuf))
      return UKey_F6;
    if(!strcmp(esc_f7    , keybuf))
      return UKey_F7;
    if(!strcmp(esc_f8    , keybuf))
      return UKey_F8;
    if(!strcmp(esc_f9    , keybuf))
      return UKey_F9;
    if(!strcmp(esc_f10   , keybuf))
      return UKey_F10;
    if(!strcmp(esc_f11   , keybuf))
      return UKey_F11;
    if(!strcmp(esc_f12   , keybuf))
      return UKey_F12;
    
    return 0; 
    
  } else {
    if (keybuf[0] >= 0x01 && keybuf[0] <= 0x1a)
     return keybuf[0] + ('a' - 0x01);
    switch (keybuf[0]) {
    case 0x7f:
      return UKey_Backspace;
    case 0x0a:
      return UKey_Zenkaku_Hankaku;
    case 0x0d:
      return UKey_Return;
    case 0x1b:
      return UKey_Escape;
    }
    return keybuf[0];
  }
}

static int
tty2mod(char *keybuf, int i)
{
  int modifier = 0;
  if(i > 1) {
    return 0;
  } else {
    if (keybuf[0] >= 0x41 && keybuf[0] <= 0x5a)
      modifier += UMod_Shift;
    if (keybuf[0] >= 0x01 && keybuf[0] <= 0x1a)
      modifier +=  UMod_Control;
    return modifier;
  }
}

static char *
usersockname()
{
    static char buf[BUFSIZE];
    sprintf(buf, "/tmp/screen-uim-%s", getenv("USER"));
    return buf;
}
 
int
main(int argc, char **argv)
{
  struct agent_context *ac = &default_context;
  unsigned char keybuf[10];
  int i, sfd;


  if (isatty(0)) {
    settermraw();
    setbuf(stdout, NULL);
  }
  if (argc > 1)
    sockpath = argv[1];
  else
    sockpath = usersockname();
  sfd = mkudclid(sockpath);
  if (sfd < 0)
    return 1;
  init_agent(sfd);

  while ((i = read(0, keybuf, sizeof(keybuf))) > 0 ) { 
    int key, mod, rf;
    keybuf[i] = 0;
    key = tty2key(keybuf, i);
    mod = tty2mod(keybuf, i);

    rf = uim_press_key(ac->uc, key, mod);
    uim_release_key(ac->uc, key, mod);
    if (rf) {
      printf("%s",keybuf);
    }
  }
  return 0;
 }

void settermraw()
{
  struct termios term;
  if (tcgetattr(0, &term) < 0)
    return;
  memcpy(&tbuf, &term, sizeof(tbuf));

  signal(SIGINT, sighandler);
  signal(SIGQUIT, sighandler);
  signal(SIGTERM, sighandler);
  signal(SIGHUP, sighandler);

  term.c_lflag &= ~(ICANON | ISIG | ECHO | IEXTEN);
  term.c_iflag &= ~(INPCK | ICRNL | ISTRIP | IXON | BRKINT);
  term.c_oflag &= ~OPOST;
  term.c_cflag &= ~(CSIZE | PARENB);
  term.c_cflag |= CS8;
  term.c_cc[VMIN] = 1;
  term.c_cc[VTIME] = 0;
  tcsetattr(0, TCSAFLUSH, &term);
}

void resetterm()
{
  if (tcgetattr(0, &tbuf) < 0)
    return;
}

void sighandler(int s)
{
  resetterm();
  exit(0);
}
