/*
 * Copyright (C) 1992 by Software Research Associates, Inc.
 *	Author:	Y. Kawabe <kawabe@sra.co.jp>
 *
 * Permission to use, copy, modify, and distribute, and sell this software
 * and its documentation for any purpose and without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and that
 * both that copyright notice and this permission notice appear in supporting
 * documentation, and that the name of Software Research Associates not be
 * used in advertising or publicity pertaining to distribution of the software
 * without specific, written prior permission.  Software Research Associates
 * makes no representations about the suitability of this software for any
 * purpose.  It is provided "as is" without express or implied warranty.
 *
 */

#include <string.h>
#include <NLS/charset.h>
#include <NLS/codeset.h>
#include <NLS/locale.h>
#include <NLS/nlsfile.h>
#include <NLS/wchar.h>
#include <hyperg/OS/table.h>
#include <hyperg/OS/ustring.h>
#include <hyperg/utils/str.h>
#include <strstream.h>
#include <stdlib.h>

#ifndef IV_CODESET_PATH
#define IV_CODESET_PATH         "CodeSet"
#endif

static int strlen (const WChar* s) {
    if (s == nil) return 0;
    int i;
    for (i = 0; s[i] != '\0'; i++);
    return i;
}
// #if defined(SUN) && (OSMajorVersion<5)
// // bm: bloody includes on SunOS 4 !@#$%^
// static int strlen(const char* s) {
//     const char* p = s;
//     int i = 0;
//     while (p && *p) { p++; i++; }
//     return i;
// }
// #endif

/*
 * class CodeCvt
 */

#include "iso2022.C"
#include "sjis.C"
#include "big5.C"

/*
 * class CodeCvtTable
 */

inline unsigned long key_to_hash(String &s) {
    return (unsigned long) s.hash();
}

declareTable (CodeCvtTable, String, CodeCvt*);
implementTable (CodeCvtTable, String, CodeCvt*);

/*
 * class CodeSet
 */

CodeCvtTable	*CodeSet::table_;

/*
 * Convert
 */

WChar* CodeSet::Convert (const Locale& locale, const char* s) {
    return Convert (locale, s, strlen(s));
}

WChar* CodeSet::Convert (const Locale& locale, const char* str, int len) {
    istrstream is((char*) str, len);
    CodeCvt *f = Find (locale);
    if (f == 0) { f = Find (Locale("C")); }
    WChar* buf = new WChar[len + 1];
    for (register int i = 0; i < len ; i++) {
	if (f->get(is, buf[i]) < 0) {
	    buf[i] = '\0';
	    break;
	}
    }
    buf[len] = '\0';
    delete f;
    return buf;
}

char* CodeSet::Convert (const Locale& locale, const WChar* s) {
    return Convert (locale, s, strlen(s));
}

char* CodeSet::Convert (const Locale& locale, const WChar* str, int len) {
    ostrstream os;
    CodeCvt *f = Find (locale);
    if (f == 0) { f = Find (Locale("C")); }

    int count = 0;
    for (register int i = 0; i < len ; i++) {
	count += f->put(os, str[i]);
    }
    count += f->put(os, EOF);
    
    char* buf = new char[count + 1];
    strncpy (buf, os.str(), count);
    buf[count] = '\0';
    delete [] os.str();
    delete f;
    return buf;
}

static char* defcodsets[]={
  "ISO2022 CTEXT G0 ASCII G1 ISO8859-1",
  "ISO2022 ISO8859-1 G0 ASCII G1 ISO8859-1",
  "ISO2022 ISO8859-2 G0 ASCII G1 ISO8859-2",
  "ISO2022 ISO8859-3 G0 ASCII G1 ISO8859-3",
  "ISO2022 ISO8859-4 G0 ASCII G1 ISO8859-4",
  "ISO2022 ISO8859-5 G0 ASCII G1 ISO8859-5",
  "ISO2022 ISO8859-6 G0 ASCII G1 ISO8859-6",
  "ISO2022 ISO8859-7 G0 ASCII G1 ISO8859-7",
  "ISO2022 ISO8859-8 G0 ASCII G1 ISO8859-8",
  "ISO2022 ISO8859-9 G0 ASCII G1 ISO8859-9",
  "ISO2022 ja_JP.JIS G0 ASCII G1 JISX0201.1976-K OPT OLD",
  "ISO2022 ja_JP.EUC G0 ASCII G1 JISX0208.1983 G2 JISX0201.1976-K G3 JISX0212.1990 OPT EUC",
  "SJIS ja_JP.SJIS GR ASCII GK JISX0201.1976-K GJ JISX0208.1983",
  "ISO2022 ko_KR.EUC G0 ASCII G1 KSC5601.1987 OPT EUC",
  "ISO2022 zh_CN.EUC G0 ASCII G1 GB2312.1980 OPT EUC",
  "BIG5 zh_TW.BIG5 GL ASCII BIG5.HKU",
  ""
};


CodeCvt* CodeSet::Find(const Locale& locale) {

    if (table_ == nil) {
	// Initialization of CodeCvtTable
	
	table_ = new CodeCvtTable (16);
	table_->insert(String("C"), new isoCodeCvt());

	UniqueString	*codeset = nil;
	CodeCvt		*codecvt = nil;

        // initialize default codesets
        int i=0;
        RString codesetline;
        RString word;
        while ((codesetline=defcodsets[i]).length())
        {
          int idx=0;
          codesetline.gWordChar(idx,' ',word);
          if (word== "ISO2022") {
            codecvt = new isoCodeCvt();
          } else if (word == "SJIS") {
            codecvt = new sjisCodeCvt();
          } else if (word == "BIG5") {
            codecvt = new big5CodeCvt();
          }

          codesetline.gWordChar(idx,' ',word);
          UniqueString* codesetname= new UniqueString(word.string());
          RString arg1;
          RString arg2;
          while(codesetline.gWordChar(idx,' ',arg1))
          {
            codesetline.gWordChar(idx,' ',arg2);
            codecvt->setparam(arg1,arg2);
          }
          table_->insert(*codesetname, codecvt);
          codecvt=nil;
          delete codesetname;
          i++;
        }

	int		argc;
	const int	argsize = 64;
	char		*argv[argsize];
	nlsFile		file (IV_CODESET_PATH);
	
	while ((argc = file.getline(argv, argsize)) >= 0) {
	    if (argc == 0) {
		continue;
	    } else if (strcmp(argv[0], "BEGIN") == 0) {
		if (argc == 2 && strcmp(argv[1], "ISO2022") == 0) {
		    codecvt = new isoCodeCvt();
		} else if (argc == 2 && strcmp(argv[1], "SJIS") == 0) {
		    codecvt = new sjisCodeCvt();
		} else if (argc == 2 && strcmp(argv[1], "BIG5") == 0) {
		    codecvt = new big5CodeCvt();
		}
	    } else if (strcmp(argv[0], "END") == 0) {
		if (codecvt && codeset) {
		    table_->insert(*codeset, codecvt);
		}
		delete codeset;
		codeset = nil;
		codecvt = nil;
	    } else if (codecvt && argc == 2) {
		if (strcmp(argv[0], "NAME") == 0) {
		    CodeCvt		*oldcvt;
		    codeset = new UniqueString(argv[1]);
		    if (table_->find_and_remove(oldcvt, *codeset)) {
			delete oldcvt;
		    }
		} else {
		    codecvt->setparam (argv[0], argv[1]);
		}
	    }
	}
    }

    CodeCvt *codecvt;
    if (table_->find(codecvt, locale.codeset())) {
	return codecvt->duplicate();
    }
    if (table_->find(codecvt, locale.name())) {
	return codecvt->duplicate();
    }
    return nil;
}
