/*
 * Unicode Conversion Library (Shift_JIS to UTF-16)
 * 1999-2004 by yoshidam
 *
 */

#ifdef USE_SJIS

#include <string.h>
#include "uconv.h"
#ifdef USE_WIN32API
#  include <windows.h>
#  define SJIS_CODEPAGE 932
#else
#  include "s2u.h"
#endif
#include "ustring.h"


#define REPLACEMENT_CHAR '?'

static unsigned long
call_unknown_s_conv(UString* u,
                    unknown_sjis unknown_s_conv,
                    const unsigned char* s, int len) {
  VALUE ret;
  unsigned char us[3];
  int i;

  if (unknown_s_conv == NULL)
    return REPLACEMENT_CHAR;

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

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


int
s2u_conv2(const unsigned char* s, UString* u,
          unknown_sjis unknown_s_conv,
          unknown_sjis s2u_hook)
{
  int i;
  int len = strlen((char*)s);

  UStr_alloc(u);

  for (i = 0; i < len; i++) {
    unsigned long sc = 0;
    int clen = 0;
    if (s2u_hook != NULL) {
      VALUE ret;
      unsigned char sstr[3];

      if (s[i] >= 0xa0 && s[i] <= 0xdf) { /* JIS X 0201 kana */
        sstr[0] = s[i]; sstr[1] = '\0';
      }
      else if (i < len - 1 &&
               s[i] >= 0x80 && s[i] <= 0xfc &&
               s[i + 1] >= 0x40 && s[i + 1] <= 0xfc && s[i + 1] != 0x7f) {
        sstr[0] = s[i]; sstr[1] = s[i+1]; sstr[2] = '\0';
        clen = 1;
      }
      else {
        sstr[0] = s[i]; sstr[1] = '\0';
      }
      if ((ret = s2u_hook(sstr)) != Qnil) {
        if (TYPE(ret) != T_FIXNUM) {
          UStr_free(u);
          rb_exc_raise(ret);
        }
        sc = FIX2INT(ret);

        if (sc == 0)
          sc = call_unknown_s_conv(u, unknown_s_conv, s + i, clen + 1);

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

    clen = 0;
    if (s[i] < 0x80) {	/* ASCII */
      sc = s[i];
    }
    else if (s[i] > 0xa0 && s[i] <= 0xdf) { /* JIS X 0201 kana */
      sc = 0xff00 | (s[i] - 0x40);
    }
    else if (i < len - 1 &&
             s[i] <= 0xfc &&
             s[i + 1] >= 0x40 && s[i + 1] <= 0xfc && s[i + 1] != 0x7f) {
      /* JIX X 0208 */
#ifdef USE_WIN32API
      unsigned char ustr[sizeof(WCHAR)*2];
      int ulen = MultiByteToWideChar(SJIS_CODEPAGE, MB_PRECOMPOSED, s + i, 2,
                                     (LPWSTR)ustr, sizeof(ustr)/sizeof(WCHAR));
      if (ulen == 1)
        sc = ustr[0] | (ustr[1] << 8);
      else if (ulen == 2) {
        /* surrogate pair ? */
      }
#else
      int hi = s[i];
      int low = s[i + 1];
      int key;
      
      if (hi >= 0xe0)
        key = (hi - 0xc1)*188;
      else
        key = (hi - 0x81)*188;
      if (low >= 0x80)
        key += low - 0x41;
      else
        key += low - 0x40;
      if (key < sizeof(s2u_tbl)/sizeof(unsigned short))
        sc = s2u_tbl[key];
#endif /* USE_WIN32API */
      clen = 1;
    }
    if (sc == 0)
      sc = call_unknown_s_conv(u, unknown_s_conv, s + i, clen + 1);

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

  return u->len;
}

#endif /* USE_SJIS */
