/*
  Copyright 2002-2003 Sun Microsystems, Inc. All Rights Reserved.

  Permission is hereby granted, free of charge, to any person obtaining a
  copy of this software and associated documentation files (the
  "Software"), to deal in the Software without restriction, including
  without limitation the rights to use, copy, modify, merge, publish,
  distribute, sublicense, and/or sell copies of the Software, and to
  permit persons to whom the Software is furnished to do so, subject to
  the following conditions: The above copyright notice and this
  permission notice shall be included in all copies or substantial
  portions of the Software.


  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  IN NO EVENT SHALL THE OPEN GROUP OR SUN MICROSYSTEMS, INC. BE LIABLE
  FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
  CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH
  THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE EVEN IF
  ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH DAMAGES.


  Except as contained in this notice, the names of The Open Group and/or
  Sun Microsystems, Inc. shall not be used in advertising or otherwise to
  promote the sale, use or other dealings in this Software without prior
  written authorization from The Open Group and/or Sun Microsystems,
  Inc., as applicable.


  X Window System is a trademark of The Open Group

  OSF/1, OSF/Motif and Motif are registered trademarks, and OSF, the OSF
  logo, LBX, X Window System, and Xinerama are trademarks of the Open
  Group. All other trademarks and registered trademarks mentioned herein
  are the property of their respective owners. No right, title or
  interest in or to any trademark, service mark, logo or trade name of
  Sun Microsystems, Inc. or its licensors is granted.

*/
#include <stdio.h>
#include <errno.h>
#include <iconv.h>
#include <strings.h>
#include <X11/Xmd.h>
#include "encode.h"


typedef struct _Encode_Info {
  int 	encode_id;
  char	**called_names;
  char	**support_locales;
  char 	*iconv_codeset_name;
  iconv_t fd_iconv_to_utf8;
  iconv_t fd_iconv_from_utf8;
} Encode_Info;

char *UTF8_names[] = {
  "UTF-8",
  "UTF_8",
  "UTF8",
  NULL
};

#define UTF8_CODESET_NAME	"UTF-8"

/* unknown encode name */
#define UNKNOWN_ENCODE		"UNKNOWN"

char *UTF8_locales[] = {
  NULL
};

Encode_Info  encode_info[ENCODES_NUM + 1] = {
  { 
    ENCODE_UTF8,
    UTF8_names,
    UTF8_locales,
    UTF8_CODESET_NAME,
    NULL,
    NULL,
  }
};

iconv_t  fd_iconv_UTF8_to_UTF16 = NULL;
iconv_t  fd_iconv_UTF16_to_UTF8 = NULL;

int get_encodeid_from_name(char *name)
{
  int encode_id, i, ret;
  char *s;
	
  ret = -1;
  for (encode_id = 0; encode_id < ENCODES_NUM; encode_id++) {
    i = 0;
    while (1) {
      s = encode_info[encode_id].called_names[i];
      if (!s || !*s) break;
      if (!strcmp(s, name)) {
	ret = encode_id;
	break;
      }
      i++;
    }
    if (ret != -1) break;
  }
  return(ret);	
}

int get_encodeid_from_locale(char *locale)
{
  int encode_id, i, ret;
  char *s;
	
  ret = -1;
  for (encode_id = 0; encode_id < ENCODES_NUM; encode_id++) {
    i = 0;
    while (1) {
      s = encode_info[encode_id].support_locales[i];
      if (!s || !*s) break;
      if (!strcmp(s, locale)) {
	ret = encode_id;
	break;
      }
      i++;
    }
    if (ret != -1) break;
  }

  if (ret == -1) ret = ENCODE_ERROR; /* return default encode */

  return(ret);	
}

char *get_name_from_encodeid(int encode_id)
{
  if (encode_id >= 0 && encode_id <= ENCODES_NUM)
    return(encode_info[encode_id].called_names[0]);
  else
    return(NULL);
}

int  get_char_len_by_encodeid(int encode_id, unsigned char *ch_ptr)
{
  int ret = 2;  /* default character length */
  unsigned char code0, code1;

  code0 = ch_ptr[0];
  if (code0 < 0x80) return(1);

  if (encode_id == ENCODE_UTF8) {
    if (code0 > 0xe0)		/* 3 bytes */
      ret = 3;
    else
      ret = 3;
  } 
  return(ret);
}

int is_valid_code(int encode_id, unsigned char *int_code, int code_len)
{
  unsigned char code0, code1;
	
  code0 = int_code[0];
  code1 = int_code[1];

  switch (encode_id) {
  case ENCODE_UTF8:
    break;
  }
  return(0);
}

int is_valid_encode_string(int encode_id, unsigned char *hzstr, int hzlen)
{
  int i, char_len, ret;
  unsigned char *ptr;

  i = 0;
  while (i < hzlen) {
    ptr = hzstr + i;
    if (*ptr < 0x80) {
      if (*ptr == 0x3f && i < hzlen-1) {
	if (*(ptr+1) == 0x3f)
	  return(-1);
      }
      i++;
    } else {
      char_len = get_char_len_by_encodeid(encode_id, ptr);
      ret = is_valid_code(encode_id, ptr, char_len);
      if (ret == -1)
	return(-1);
      i += char_len;
    }
  }

  return (0);
}

int Convert_UTF8_To_Native(int encode_id, char *from_buf, size_t from_left,
			   char **to_buf, size_t * to_left)
{
  const char	*ip;
  char		*op;
  size_t		ileft, oleft;
  iconv_t		fd_iconv;
  char 		*codeset;
  size_t		ret = 0;
	
  if (encode_id < 0 || encode_id >= ENCODES_NUM)
    return(-1);

  if ( (from_left < 0) || (*to_left < 0) )
    return(-1);

  ip = (const char *) from_buf;
  ileft = from_left;

  op = *((char **) to_buf);
  oleft = *to_left;

  if (encode_id == ENCODE_UTF8) {
    if (ileft > oleft)
      return(-1);
    memcpy(op, ip, ileft);
    *to_left = oleft - ileft;
    return(0);
  }

  fd_iconv = encode_info[encode_id].fd_iconv_from_utf8;
  if (fd_iconv == (iconv_t)-1) return(-1);

  if (fd_iconv == NULL) {
    codeset = encode_info[encode_id].iconv_codeset_name;
    fd_iconv = iconv_open(codeset, "UTF-8");
    encode_info[encode_id].fd_iconv_from_utf8 = fd_iconv;
    if ( fd_iconv == (iconv_t) -1 )
      return(-1);
  }

  ret = iconv(fd_iconv, &ip, &ileft, &op, &oleft);
  if ((ret != 0) && (E2BIG != errno)) {
    return(-1);
  }
  *to_left = oleft;
  return(0);
}

#define UTF16_STRLEN    1024
	
int Convert_Native_To_UTF16(int encode_id, char *from_buf, size_t from_left,
			    char **to_buf, size_t *to_left)
{
  const char	*ip;
  char		*op;
  size_t		ileft, oleft;

  char 		*codeset;
  iconv_t		fd_iconv_native_to_utf8;

  size_t		ret = 0;
  int 		skip_native_to_utf8_iconv = 0;

  if (encode_id < 0 || encode_id >= ENCODES_NUM)
    return(-1);

  if ( (from_left < 0) || (*to_left < 0) )
    return(-1);

  /* Initialize the iconv of utf8_to_ucs2 */
  if (fd_iconv_UTF8_to_UTF16 == (iconv_t)-1 )
    return(-1);

  if (fd_iconv_UTF8_to_UTF16 == NULL) {
    fd_iconv_UTF8_to_UTF16 = iconv_open("UCS-2", "UTF-8");
    if (fd_iconv_UTF8_to_UTF16 == (iconv_t)-1 )
      return(-1);
  }

  if (encode_id == ENCODE_UTF8)
    skip_native_to_utf8_iconv = 1;

  ip = (const char *) from_buf;
  ileft = from_left;

  op = *((char **) to_buf);
  oleft = *to_left;

  if (!skip_native_to_utf8_iconv) {
    char		buffer[UTF16_STRLEN];   /* Fix me! */
    const size_t	buf_len = UTF16_STRLEN;
    char		*src, *dst;
    size_t		src_len, dst_len;

    /* Initialize the iconv of native_to_utf8 */
    fd_iconv_native_to_utf8 = encode_info[encode_id].fd_iconv_to_utf8;
    if (fd_iconv_native_to_utf8 == (iconv_t)-1) return(-1);

    if (fd_iconv_native_to_utf8 == NULL) {
      codeset = encode_info[encode_id].iconv_codeset_name;
      fd_iconv_native_to_utf8 = iconv_open("UTF-8", codeset);
      encode_info[encode_id].fd_iconv_to_utf8 = fd_iconv_native_to_utf8;
      if ( fd_iconv_native_to_utf8 == (iconv_t) -1 )
	return(-1);
    }

    while ((ileft > 0) && (oleft > 0)) {
      dst = buffer;
      dst_len = buf_len;
      ret = iconv(fd_iconv_native_to_utf8, &ip, &ileft, (char **) &dst, &dst_len);
      if ((ret != 0) && (E2BIG != errno)) {
	return(-1);
      }
      src = buffer;
      src_len = buf_len - dst_len;
      ret = iconv(fd_iconv_UTF8_to_UTF16, (const char **) &src, &src_len, &op, &oleft);
      if ((ret != 0) && (E2BIG != errno)) {
	return(-1);
      }
    }

  } else {
    ret = iconv(fd_iconv_UTF8_to_UTF16, &ip, &ileft, &op, &oleft);
    if ((ret != 0) && (E2BIG != errno)) {
      return(-1);
    }
  }

  if (0xFEFF == **((CARD16 **) to_buf)) {
    memmove(*to_buf, *to_buf + 2, *to_left - oleft - 2);
    *to_left = (oleft + 2);
  } else {
    *to_left = oleft;
  }

  return(0);
}
