/*
 * Unicode Conversion Library (EUC-JP to UTF-16)
 * 1997-2004 by yoshidam
 *
 */

#ifdef USE_EUC

#include <string.h>
#include "uconv.h"
#include "e2u.h"
#include "hojo2u.h"
#include "ustring.h"

#define REPLACEMENT_CHAR '?'

static unsigned long
call_unknown_e_conv(UString* u,
                    unknown_euc unknown_e_conv,
                    const unsigned char* e, int len) {
  VALUE ret;
  unsigned char ue[4];
  int i;

  if (unknown_e_conv == NULL)
    return REPLACEMENT_CHAR;

  for (i = 0; i < len && i < sizeof(ue) - 1; i++)
    ue[i] = e[i];
  ue[i] = '\0';
  ret = unknown_e_conv(ue);
  if (TYPE(ret) != T_FIXNUM) {
    UStr_free(u);
    rb_exc_raise(ret);
  }
  return FIX2INT(ret);
}

static void
append_uchar(UString* u, unsigned long ec) {
  if (ec >= 0x10000) {
    unsigned int high = ((ec - 0x10000) >> 10) | 0xd800;
    unsigned int low = (ec & 1023) | 0xdc00;
    UStr_addChar4(u, high & 0xff, high >> 8, low & 0xff, low >> 8);
  }
  else
    UStr_addChar2(u, ec & 0xff, ec >> 8);
}

int
e2u_conv2(const unsigned char* e, UString* u,
          unknown_euc unknown_e_conv,
          unknown_euc e2u_hook) {
  int i;
  int len = strlen((char*)e);

  UStr_alloc(u);

  for (i = 0; i < len; i++) {
    unsigned long ec = 0;
    int clen = 0;

    if (e2u_hook != NULL) {
      VALUE ret;
      unsigned char estr[4];
      if (e[i] == 0x8e && i < len - 1) { /* JIS X 0201 kana */
        estr[0] = e[i]; estr[1] = e[i+1]; estr[2] = '\0';
        clen = 1;
      }
      else if (e[i] == 0x8f && i < len - 2) { /* JIS X 0212 */
        estr[0] = e[i]; estr[1] = e[i+1]; estr[2] = e[i+2]; estr[3] = '\0';
        clen = 2;
      }
      else if (e[i] >= 0xa0 && e[i] != 0xff && i < len - 1) { /* JIX X 0208 */
        estr[0] = e[i]; estr[1] = e[i+1]; estr[2] = '\0';
        clen = 1;
      }
      else { /* ASCII or C1 */
        estr[0] = e[i]; estr[1] = '\0';
      }
      if ((ret = e2u_hook(estr)) != Qnil) {
        if (TYPE(ret) != T_FIXNUM) {
          UStr_free(u);
          rb_exc_raise(ret);
        }
        ec = FIX2INT(ret);

        if (ec == 0)
          ec = call_unknown_e_conv(u, unknown_e_conv, e + i, clen + 1);

        append_uchar(u, ec);
        i += clen;
        continue;
      }
    }

    clen = 0;
    if (e[i] == 0x8e && i < len - 1) { /* JIS X 0201 kana */
      if (e[i + 1] >= 0xa1 && e[i + 1] <= 0xdf)
        ec = 0xff00 | (e[i + 1] - 0x40);
      clen = 1;
    }
    else if (e[i] == 0x8f && i < len - 2) { /* JIS X 0212 */
      int hi = e[i + 1] &  0x7f;
      int low = e[i + 2] &  0x7f;
      int key = (hi - 32) * 96 + (low - 32);
      if (hi >= 32 && low >= 32 &&
          key < sizeof(hojo2u_tbl)/sizeof(unsigned short))
        ec = hojo2u_tbl[key];
      clen = 2;
    }
    else if (e[i] >= 0xa0 && e[i] != 0xff && i < len - 1) { /* JIX X 0208 */
      int hi = e[i] &  0x7f;
      int low = e[i + 1] &  0x7f;
      int key = (hi - 32) * 96 + (low - 32);
      if (hi >= 32 && low >= 32 &&
          key < sizeof(e2u_tbl)/sizeof(unsigned short))
        ec = e2u_tbl[key];
      clen = 1;
    }
    else if (e[i] < 0xa0) { /* ASCII or C1 */
      ec = e[i];
    }

    if (ec == 0)
      ec = call_unknown_e_conv(u, unknown_e_conv, e + i, clen + 1);

    append_uchar(u, ec);
    i += clen;
  }

  return u->len;
}

#endif /* USE_EUC */
