/* -*- mode: C; mode: fold; -*- */
/*
 *  Copyright (c) 1998 Uichi Katsuta  (katsuta@lang2.bs1.fc.nec.co.jp)
 *  All Rights Reserved.
 */
#include "config.h"
#include "jed-feat.h"

#if JED_HAS_CANNA_SUPPORT

#include <sys/types.h>
#include "buffer.h"
#include "sysdep.h"
#include "kanji.h"
#include "canna.h"
#include <canna/jrkanji.h>

static jrKanjiStatus Status;
static jrKanjiStatusWithValue Value;
#define BUFMAX	1024
static u_char Inbuf[BUFMAX];
static u_char Outbuf[BUFMAX];


#if 0
static void
euc2sjis(unsigned char *s, unsigned char *d) {
   unsigned char ch, ch2;

   while(ch = *s++) {
      if (ch & 0x80) {
	 ch2 = *s++;
	 if (ch != SS2) {
	    if (ch & 1) {
	       ch2 -= 0x61;
	    } else {
	       ch2 -= 0x03;
	    }
	    if (ch2 >= 0x7f) {
	       ch2++;
	    }
	    ch = ((ch - 0xa1) >> 1) + 0x81;
	    if (ch > 0x9f) {
	       ch += 0x40;
	    }
	    *d++ = ch;
	 }
	 ch = ch2;
      }
      *d++ = ch;
   }
   *d = '\0';
}

static void
sjis2euc(unsigned char *s, unsigned char *d) {
   unsigned char ch, ch2;

   while(ch = *s++) {
      if (ch & 0x80) {
	 if (ch >= 0xa0 && ch < 0xe0) {
	    ch2 = SS2;
	 } else {
	    if (ch >= 0xe0) {
	       ch -= 0x70;
	    } else {
	       ch -= 0x30;
	    }
	    ch2 = ch << 1;
	    ch = *s++;
	    if (ch >= 0x9f) {
	       ch += 2;
	    } else {
	       ch2--;
	       if (ch & 0x80) {
		  ch += 0x60;
	       } else {
		  ch += 0x61;
	       }
	    }
	 }
	 *d++ = ch2;
      }
      *d++ = ch;
   }
   *d = '\0';
}
#endif

static void
push_string(char *string) {
#if 0
   if (string == NULL) {
      Outbuf[0] = '\0';
   } else {
      euc2sjis(string, Outbuf);
   }
   SLang_push_string(Outbuf);
#else
   char *dst;
   int n = 0;
   
   if (string == NULL)
     {
	Outbuf[0] = '\0';
	SLang_push_string(Outbuf);
     }
   else
     {
	n = strlen(string);
   
	dst = KanjiCodeConv(string, &n, EUC, kSLcode, SKanaToDKana);
	SLang_push_string(dst);
   
	if(dst != string) SLfree(dst);
     }
#endif
}

/* return:
%  echoStr	:  local echo string
%  length	:  
%  revPos	:  
%  revLen	:
%  mode		:  mode string
%  line		:  kouho ichiran string
%  lineLength
%  lineRevPos
%  lineRevLen
*/
static void
push_status(jrKanjiStatus *Status) {
   int len = Status->length;
   char *buf;
   if (len > 0) {
      buf = Status->echoStr;
   } else {
      buf = NULL;
   }
   push_string(buf);
   SLang_push_integer(len);
   SLang_push_integer(Status->revPos);
   SLang_push_integer(Status->revLen);

   if (Status->info & KanjiModeInfo) {
      buf = Status->mode;
   } else {
      buf = NULL;
   }
   push_string(buf);

   if (Status->info & KanjiGLineInfo) {
      len = Status->gline.length;
      if (len == 0) {
	 buf = NULL;
      } else {
	 buf = Status->gline.line;
      }
   } else {
      buf = NULL;
      len = -1;
   }

   push_string(buf);
   SLang_push_integer(len);
   SLang_push_integer(Status->gline.revPos);
   SLang_push_integer(Status->gline.revLen);
}

static void
push_value(jrKanjiStatusWithValue *Value) {
   Value->buffer[Value->val] = '\0';
   push_string(Value->buffer);
   push_status(Value->ks);
}

static void
push_warning(char **w) {
   int n = 0;
   if (w != NULL) {
      while(w[n]) {
         push_string(w[n]);
	 n++;
      }
   }
   SLang_push_integer(n);
}

extern void (*jrBeepFunc)();

static void
canna_beep() {
   Status.gline.length = -1;
   jed_beep();
}

#define KC_STRING	1000	/* call jrKanjiString() */

void
canna_control(int *recp) {
   char *arg = NULL, **warning;
   u_char *buf;
   int req = *recp, n, r;

   if (req == KC_STRING) {
      SLang_pop_integer(&n);
      r = jrKanjiString(0, n, Inbuf, sizeof(Inbuf), &Status);
      if (r < 0) {
	 Status.echoStr = jrKanjiError;
	 Status.length = -2;  /* -2 means error... */
      } else {
	 Inbuf[r] = '\0';
      }
      push_string(Inbuf);
      push_status(&Status);
      return;
   }
   switch(req) {
    case KC_INITIALIZE:
      Value.ks = &Status;
      Value.buffer = Inbuf;
      Value.bytes_buffer = sizeof(Inbuf);
      /* not break */
    case KC_FINALIZE:
      arg = (char *)&warning;
      break;
    case KC_QUERYMODE:
      arg = Inbuf;
      break;
    case KC_DEFINEKANJI:
	{
	   int i;
	   
	   (void)SLang_pop_string(&arg, &r);
	   i = strlen(arg);
	   buf = KanjiCodeConv(arg, &i, kSLcode, EUC, SKanaToDKana);
	   strcpy(Outbuf, buf);
	   if((char *)buf != arg) SLfree(buf);
/*	   sjis2euc(arg, Inbuf); */
	   Value.ks->echoStr = Outbuf;
	   if (r == 1) {
	      SLfree(arg);
	   }
	}
      /* not break */
    case KC_KAKUTEI:
    case KC_KILL:
      arg = (char *)&Value;
      break;
    case KC_CHANGEMODE:
      SLang_pop_integer(&n);
      Value.val = n;
      arg = (char *)&Value;
      break;
    case KC_SETWIDTH:
    case KC_INHIBITHANKAKUKANA:
    case KC_SETBUNSETSUKUGIRI:
    case KC_SETMODEINFOSTYLE:
      SLang_pop_integer(&n);
      arg = (char *)n;
      break;
    case KC_SETAPPNAME:
      (void)SLang_pop_string(&arg, &n);
      break;
    default:
      break;
   }
   (void)jrKanjiControl(0, req, arg);
   switch(req) {
    case KC_DEFINEKANJI:
    case KC_CHANGEMODE:
    case KC_KAKUTEI:
    case KC_KILL:
      push_value(&Value);
      break;
    case KC_QUERYMODE:
      push_string(Inbuf);
      break;
    case KC_SETAPPNAME:
      if (n == 1) {
	 SLfree(arg);
      }
      break;
    case KC_INITIALIZE:
      jrBeepFunc = canna_beep;
      jrKanjiControl(0, KC_SETUNDEFKEYFUNCTION, kc_normal);
      /* not break */
    case KC_FINALIZE:
      push_warning(warning);
      break;
      /*
    case KC_SETWIDTH:
    case KC_INHIBITHANKAKUKANA:
    case KC_SETBUNSETSUKUGIRI:
    case KC_SETMODEINFOSTYLE:
       */
    default:
      break;
   }
}


static SLang_Intrin_Fun_Type SLCanna_ITable[] = 
{
   MAKE_INTRINSIC_I("canna_control", canna_control, VOID_TYPE),
   SLANG_END_TABLE
};


int init_SLCanna()
{
   if (-1 == SLadd_intrin_fun_table(SLCanna_ITable, NULL)) return -1;
   return SLdefine_for_ifdef ("CANNA");
}

#endif
