/* vi:set ts=8 sts=4 sw=4:
 *
 * VIM - Vi IMproved		by Bram Moolenaar
 *
 * Do ":help uganda"  in Vim to read copying and usage conditions.
 * Do ":help credits" in Vim to see a list of people who contributed.
 * See README.txt for an overview of the Vim source code.
 */

/*
 * os_macosx.c -- election of os_mac.c or os_unix.c
 *
 */

#ifdef MACOS_X_UNIX
# ifdef HAVE_CONFIG_H	    /* Using Makefile. */
#  include "vim.h"
# else
#  include "os_unix.c"	    /* Using Project Builder */
# endif
#else
# include "os_mac.c"
# include <TextEncodingConverter.h>
#endif

#ifdef _DEBUG
    void
Trace(char* fmt, ...)
{
    char buf[2048];
    va_list args;

    va_start(args, fmt);
    /* vsnprintf(buf, sizeof(buf), fmt, args);*/
    fprintf(stderr, "%s", buf);
    va_end(args);
}
#endif

#ifdef MACOS_X_ICONVEMU
/*
 * Libiconv emulation layer
 */

struct _iconv_t
{
    TECObjectRef tec;
    TECObjectRef tecReverse;
    TECSnifferObjectRef sniff;
    TextEncoding from;
    TextEncoding to;
};
/* typedef struct _iconv_t *iconv_t; */


static int last_errno = 0;

/*
 * Get TextEncoding from iconv's encoding name
 */
    static TextEncoding
get_textencoding(const char* encodename)
{
    static struct {
	const char* name;
	TextEncoding encode;
    } encodetable[] = {
	/* ISO-8859 encodings family */
	{"latin1", kTextEncodingISOLatin1},
	{"latin2", kTextEncodingISOLatin2},
	{"latin3", kTextEncodingISOLatin3},
	{"latin4", kTextEncodingISOLatin4},
	{"latin5", kTextEncodingISOLatin5},
	{"latin6", kTextEncodingISOLatin6},
	{"latin7", kTextEncodingISOLatin7},
	{"latin8", kTextEncodingISOLatin8},
	{"latin9", kTextEncodingISOLatin9},
	{"iso-8859-1", kTextEncodingISOLatin1},
	{"iso-8859-2", kTextEncodingISOLatin2},
	{"iso-8859-3", kTextEncodingISOLatin3},
	{"iso-8859-4", kTextEncodingISOLatin4},
	{"iso-8859-5", kTextEncodingISOLatinCyrillic},
	{"iso-8859-6", kTextEncodingISOLatinArabic},
	{"iso-8859-7", kTextEncodingISOLatinGreek},
	{"iso-8859-8", kTextEncodingISOLatinHebrew},
	{"iso-8859-9", kTextEncodingISOLatin5},
	{"iso-8859-10", kTextEncodingISOLatin6},

	/* Unicode encodings. */
	/* TODO: Add other type of unicode */
	{"ucs-2", kTextEncodingMacUnicode},

	/* Japanese encoding aliases */
	{"cp932", kTextEncodingShiftJIS},
	{"shift-jis", kTextEncodingShiftJIS},
	{"euc-jp", kTextEncodingEUC_JP},
	{"iso-2022-jp", kTextEncodingISO_2022_JP},
	{"iso-2022-jp-1", kTextEncodingISO_2022_JP_1},
	{"iso-2022-jp-2", kTextEncodingISO_2022_JP_2},
	{"iso-2022-jp-3", kTextEncodingISO_2022_JP_3},

	/* Other aliases. These aliases in this block are just guessed. */
	/* TODO: Must be verified. */
	{"gb2312", kTextEncodingGB_2312_80},
	{"cp936", kTextEncodingMacChineseSimp},
	{"euc-cn", kTextEncodingEUC_CN},
	{"cp950", kTextEncodingMacChineseTrad},
	{"euc-tw", kTextEncodingEUC_TW},
	{"cp949", kTextEncodingMacKorean},
	{"euc-kr", kTextEncodingEUC_KR},

	/*
	 * All encodings supported by Macintosh.  You can find these values
	 * in a file:
	 *  /System/Library/Frameworks/CoreServices.framework/Versions/A/
	 *    Frameworks/CarbonCore.framework/Versions/A/Headers/TextCommon.h
	 */
	{"MacRoman", kTextEncodingMacRoman},
	{"MacJapanese", kTextEncodingMacJapanese},
	{"MacChineseTrad", kTextEncodingMacChineseTrad},
	{"MacKorean", kTextEncodingMacKorean},
	{"MacArabic", kTextEncodingMacArabic},
	{"MacHebrew", kTextEncodingMacHebrew},
	{"MacGreek", kTextEncodingMacGreek},
	{"MacCyrillic", kTextEncodingMacCyrillic},
	{"MacDevanagari", kTextEncodingMacDevanagari},
	{"MacGurmukhi", kTextEncodingMacGurmukhi},
	{"MacGujarati", kTextEncodingMacGujarati},
	{"MacOriya", kTextEncodingMacOriya},
	{"MacBengali", kTextEncodingMacBengali},
	{"MacTamil", kTextEncodingMacTamil},
	{"MacTelugu", kTextEncodingMacTelugu},
	{"MacKannada", kTextEncodingMacKannada},
	{"MacMalayalam", kTextEncodingMacMalayalam},
	{"MacSinhalese", kTextEncodingMacSinhalese},
	{"MacBurmese", kTextEncodingMacBurmese},
	{"MacKhmer", kTextEncodingMacKhmer},
	{"MacThai", kTextEncodingMacThai},
	{"MacLaotian", kTextEncodingMacLaotian},
	{"MacGeorgian", kTextEncodingMacGeorgian},
	{"MacArmenian", kTextEncodingMacArmenian},
	{"MacChineseSimp", kTextEncodingMacChineseSimp},
	{"MacTibetan", kTextEncodingMacTibetan},
	{"MacMongolian", kTextEncodingMacMongolian},
	{"MacEthiopic", kTextEncodingMacEthiopic},
	{"MacCentralEurRoman", kTextEncodingMacCentralEurRoman},
	{"MacVietnamese", kTextEncodingMacVietnamese},
	{"MacExtArabic", kTextEncodingMacExtArabic},
	{"MacSymbol", kTextEncodingMacSymbol},
	{"MacDingbats", kTextEncodingMacDingbats},
	{"MacTurkish", kTextEncodingMacTurkish},
	{"MacCroatian", kTextEncodingMacCroatian},
	{"MacIcelandic", kTextEncodingMacIcelandic},
	{"MacRomanian", kTextEncodingMacRomanian},
	{"MacCeltic", kTextEncodingMacCeltic},
	{"MacGaelic", kTextEncodingMacGaelic},
	{"MacKeyboardGlyphs", kTextEncodingMacKeyboardGlyphs},
	{"MacTradChinese", kTextEncodingMacTradChinese},
	{"MacRSymbol", kTextEncodingMacRSymbol},
	{"MacSimpChinese", kTextEncodingMacSimpChinese},
	{"MacGeez", kTextEncodingMacGeez},
	{"MacEastEurRoman", kTextEncodingMacEastEurRoman},
	{"MacUninterp", kTextEncodingMacUninterp},
	{"MacUnicode", kTextEncodingMacUnicode},
	{"MacFarsi", kTextEncodingMacFarsi},
	{"MacUkrainian", kTextEncodingMacUkrainian},
	{"MacInuit", kTextEncodingMacInuit},
	{"MacVT100", kTextEncodingMacVT100},
	{"MacHFS", kTextEncodingMacHFS},
	{"UnicodeDefault", kTextEncodingUnicodeDefault},
	{"UnicodeV1_1", kTextEncodingUnicodeV1_1},
	{"ISO10646_1993", kTextEncodingISO10646_1993},
	{"UnicodeV2_0", kTextEncodingUnicodeV2_0},
	{"UnicodeV2_1", kTextEncodingUnicodeV2_1},
	{"UnicodeV3_0", kTextEncodingUnicodeV3_0},
	{"UnicodeV3_1", kTextEncodingUnicodeV3_1},
	{"UnicodeV3_2", kTextEncodingUnicodeV3_2},
	{"ISOLatin1", kTextEncodingISOLatin1},
	{"ISOLatin2", kTextEncodingISOLatin2},
	{"ISOLatin3", kTextEncodingISOLatin3},
	{"ISOLatin4", kTextEncodingISOLatin4},
	{"ISOLatinCyrillic", kTextEncodingISOLatinCyrillic},
	{"ISOLatinArabic", kTextEncodingISOLatinArabic},
	{"ISOLatinGreek", kTextEncodingISOLatinGreek},
	{"ISOLatinHebrew", kTextEncodingISOLatinHebrew},
	{"ISOLatin5", kTextEncodingISOLatin5},
	{"ISOLatin6", kTextEncodingISOLatin6},
	{"ISOLatin7", kTextEncodingISOLatin7},
	{"ISOLatin8", kTextEncodingISOLatin8},
	{"ISOLatin9", kTextEncodingISOLatin9},
	{"DOSLatinUS", kTextEncodingDOSLatinUS},
	{"DOSGreek", kTextEncodingDOSGreek},
	{"DOSBalticRim", kTextEncodingDOSBalticRim},
	{"DOSLatin1", kTextEncodingDOSLatin1},
	{"DOSGreek1", kTextEncodingDOSGreek1},
	{"DOSLatin2", kTextEncodingDOSLatin2},
	{"DOSCyrillic", kTextEncodingDOSCyrillic},
	{"DOSTurkish", kTextEncodingDOSTurkish},
	{"DOSPortuguese", kTextEncodingDOSPortuguese},
	{"DOSIcelandic", kTextEncodingDOSIcelandic},
	{"DOSHebrew", kTextEncodingDOSHebrew},
	{"DOSCanadianFrench", kTextEncodingDOSCanadianFrench},
	{"DOSArabic", kTextEncodingDOSArabic},
	{"DOSNordic", kTextEncodingDOSNordic},
	{"DOSRussian", kTextEncodingDOSRussian},
	{"DOSGreek2", kTextEncodingDOSGreek2},
	{"DOSThai", kTextEncodingDOSThai},
	{"DOSJapanese", kTextEncodingDOSJapanese},
	{"DOSChineseSimplif", kTextEncodingDOSChineseSimplif},
	{"DOSKorean", kTextEncodingDOSKorean},
	{"DOSChineseTrad", kTextEncodingDOSChineseTrad},
	{"WindowsLatin1", kTextEncodingWindowsLatin1},
	{"WindowsANSI", kTextEncodingWindowsANSI},
	{"WindowsLatin2", kTextEncodingWindowsLatin2},
	{"WindowsCyrillic", kTextEncodingWindowsCyrillic},
	{"WindowsGreek", kTextEncodingWindowsGreek},
	{"WindowsLatin5", kTextEncodingWindowsLatin5},
	{"WindowsHebrew", kTextEncodingWindowsHebrew},
	{"WindowsArabic", kTextEncodingWindowsArabic},
	{"WindowsBalticRim", kTextEncodingWindowsBalticRim},
	{"WindowsVietnamese", kTextEncodingWindowsVietnamese},
	{"WindowsKoreanJohab", kTextEncodingWindowsKoreanJohab},
	{"US_ASCII", kTextEncodingUS_ASCII},
	{"JIS_X0201_76", kTextEncodingJIS_X0201_76},
	{"JIS_X0208_83", kTextEncodingJIS_X0208_83},
	{"JIS_X0208_90", kTextEncodingJIS_X0208_90},
	{"JIS_X0212_90", kTextEncodingJIS_X0212_90},
	{"JIS_C6226_78", kTextEncodingJIS_C6226_78},
	{"ShiftJIS_X0213_00", kTextEncodingShiftJIS_X0213_00},
	{"GB_2312_80", kTextEncodingGB_2312_80},
	{"GBK_95", kTextEncodingGBK_95},
	{"GB_18030_2000", kTextEncodingGB_18030_2000},
	{"KSC_5601_87", kTextEncodingKSC_5601_87},
	{"KSC_5601_92_Johab", kTextEncodingKSC_5601_92_Johab},
	{"CNS_11643_92_P1", kTextEncodingCNS_11643_92_P1},
	{"CNS_11643_92_P2", kTextEncodingCNS_11643_92_P2},
	{"CNS_11643_92_P3", kTextEncodingCNS_11643_92_P3},
	{"ISO_2022_JP", kTextEncodingISO_2022_JP},
	{"ISO_2022_JP_2", kTextEncodingISO_2022_JP_2},
	{"ISO_2022_JP_1", kTextEncodingISO_2022_JP_1},
	{"ISO_2022_JP_3", kTextEncodingISO_2022_JP_3},
	{"ISO_2022_CN", kTextEncodingISO_2022_CN},
	{"ISO_2022_CN_EXT", kTextEncodingISO_2022_CN_EXT},
	{"ISO_2022_KR", kTextEncodingISO_2022_KR},
	{"EUC_JP", kTextEncodingEUC_JP},
	{"EUC_CN", kTextEncodingEUC_CN},
	{"EUC_TW", kTextEncodingEUC_TW},
	{"EUC_KR", kTextEncodingEUC_KR},
	{"ShiftJIS", kTextEncodingShiftJIS},
	{"KOI8_R", kTextEncodingKOI8_R},
	{"Big5", kTextEncodingBig5},
	{"MacRomanLatin1", kTextEncodingMacRomanLatin1},
	{"HZ_GB_2312", kTextEncodingHZ_GB_2312},
	{"Big5_HKSCS_1999", kTextEncodingBig5_HKSCS_1999},
	{"NextStepLatin", kTextEncodingNextStepLatin},
	{"EBCDIC_US", kTextEncodingEBCDIC_US},
	{"EBCDIC_CP037", kTextEncodingEBCDIC_CP037},
	{"MultiRun", kTextEncodingMultiRun},

	/* Terminator */
	{NULL, -1},
    };
    int i;

    i = 0;
    for (i = 0; encodetable[i].name != NULL; ++i)
    {
	if (STRICMP(encodename, encodetable[i].name) == 0)
	    break;
    }
    return encodetable[i].encode;
}

/*
 * iconv interfaces
 */

    iconv_t
iconv_open(const char* tocode, const char* fromcode)
{
    TextEncoding toEnc, fromEnc;
    iconv_t cd = NULL;
    OSStatus st;

    /* Verify to/from encoding name */
    toEnc = get_textencoding(tocode);
    fromEnc = get_textencoding(fromcode);
    if (toEnc < 0 || fromEnc < 0)
	goto ICONV_OPEN_ERR;

    /* Allocate memory to object */
    cd = (iconv_t)alloc(sizeof(struct _iconv_t));
    if (!cd)
	goto ICONV_OPEN_ERR;
    memset(cd, 0, sizeof(struct _iconv_t));

    /* Create converter */
    if (fromEnc != toEnc)
    {
	TRACE("*** fromEnc=%d toEnc=%d\n", (int)fromEnc, (int)toEnc);
	st = TECCreateConverter(&cd->tec, fromEnc, toEnc);
	if (st != 0)
	{
	    TRACE("*** TECCreateConverter()=%d\n", (int)st);
	    goto ICONV_OPEN_ERR;
	}
	/* Create reverse converter */
	st = TECCreateConverter(&cd->tecReverse, toEnc, fromEnc);
	if (st != 0)
	{
	    TRACE("*** TECCreateConverter()=%d (reverse)\n", (int)st);
	    goto ICONV_OPEN_ERR;
	}
	/* Create Sniffer */
	st = TECCreateSniffer(&cd->sniff, &fromEnc, 1);
	if (st != 0)
	{
	    TRACE("*** TECCreateSniffer()=%d\n", (int)st);
	    goto ICONV_OPEN_ERR;
	}
    }

    cd->from = fromEnc;
    cd->to = toEnc;
    last_errno = 0;
    return cd;

ICONV_OPEN_ERR:
    if (cd)
	iconv_close(cd);
    last_errno = EINVAL;
    return (iconv_t)-1;
}

/*
 * Used when there are same value in 'from encoding' and 'to encoding'.
 * TEC doesn't support conversion between same encodings, and
 * TECCreateConverter() failed.
 */
    static size_t
null_conv(iconv_t cd, const char **inbuf, size_t *inbytesleft,
	char **outbuf, size_t *outbytesleft)
{
    const char* buf_in = inbuf && *inbuf ? *inbuf : NULL;
    char* buf_out = outbuf && *outbuf ? *outbuf : NULL;

    if (buf_in)
    {
	int in_len = inbytesleft ? *inbytesleft : 0;
	int out_len = outbytesleft ? *outbytesleft : 0;

	if (!buf_out || out_len <= 0)
	{
	    last_errno = E2BIG;
	    return -1;
	}
	else if (in_len > 0)
	{
	    int len = in_len < out_len ? in_len : out_len;

	    memcpy (buf_out, buf_in, len);
	    *inbuf += len;
	    *outbuf += len;
	    *inbytesleft -= len;
	    *outbytesleft -= len;
	    if (*outbytesleft <= 0)
	    {
		last_errno = E2BIG;
		return -1;
	    }
	}
    }
    last_errno = 0;
    return 0;
}

    size_t
iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft,
	char **outbuf, size_t *outbytesleft)
{
    ConstTextPtr    buf_in;
    TextPtr	    buf_out;
    ByteCount	    out_len, out_true;
    ByteCount	    in_len, in_true;
    OSStatus	    st;

    if (!cd)
    {
	last_errno = ENOENT; /* TODO: Another error code should be set */
	return -1;
    }
    if (cd->from == cd->to)
	return null_conv(cd, inbuf, inbytesleft, outbuf, outbytesleft) ;

    buf_in = (TextPtr) inbuf ;
    buf_out = (TextPtr) outbuf ;
    out_len = out_true = -1;
    in_len = in_true = -1;

    if (buf_in && buf_out)
    {
	ItemCount error, feature;

	/* Normal convert mode */
	if (!inbytesleft || !outbytesleft)
	{
	    last_errno = EFAULT;
	    return -1;
	}
	in_len = *inbytesleft;
	out_len = *outbytesleft;

	/* Check stream is form in expected encoding or not */
	st = TECSniffTextEncoding(cd->sniff, (TextPtr)buf_in, in_len,
		&cd->from, 1, &error, 1, &feature, 1);
	TRACE("TECSniffTextEncoding()=%d error=%d feature=%d\n",
		(int)st, (int)error, (int)feature);
	if ((error != 0 || feature == 0)
		&& !(error == 0xffffffff && feature == 0xffffffff))
	    /* Not expected encoding */
	    st = kTECUnmappableElementErr;
	else
	{
	    /* Do convert */
	    st = TECConvertText(cd->tec,
		    buf_in, in_len, &in_true,
		    buf_out, out_len, &out_true);
	    /* Verify converted text.  Compare original text with reverse
	     * converted text.  If not match, there is some problem on
	     * converting. */
	    if (st == 0 && in_true > 0)
	    {
		ByteCount rev_in, rev_out;
		TextPtr buf_rev = (TextPtr)alloc(in_true);

		if (buf_rev)
		{
		    st = TECConvertText(cd->tecReverse,
			    buf_out, out_true, &rev_in,
			    buf_rev, in_true, &rev_out);
		    if (st != 0 || rev_in != out_true || rev_out != in_true
			    || memcmp(buf_rev, buf_in, rev_out) != 0)
		    {
#ifdef ICONVOSX_DEBUG
			fprintf(stderr, "  reverse conversion failed.\n");
#endif
			st = kTECUnmappableElementErr;
		    }
		    vim_free(buf_rev);
		}
		else
		    st = kTECUnmappableElementErr;
	    }
	}
    }
    else if (!buf_in && buf_out)
    {
	/* Flush all buffered strings to buffer, and reset status */
	if (!outbytesleft)
	{
	    last_errno = EFAULT;
	    return -1;
	}
	out_len = *outbytesleft;
	st = TECFlushText(cd->tec,
		buf_out, out_len, &out_true);
    }
    else if (!buf_in && !buf_out)
    {
	/* Reset cd's status and cancel buffered strings */
	unsigned char tmp_out[256];

	buf_out = tmp_out;
	out_len = sizeof(tmp_out);
	st = TECFlushText(cd->tec,
		buf_out, out_len, &out_true);
    }
    else
    {
	last_errno = EFAULT;
	return -1;
    }
    TRACE("st=%d, buf_in=%p, in_len=%d, in_true=%d\n"
	    "  buf_out=%p, out_len=%d, out_true=%d\n", (int)st,
	    buf_in, (int)in_len, (int)in_true,
	    buf_out, (int)out_len, (int)out_true);

    switch (st)
    {
	case 0:
	    /* No error */
	    if (inbytesleft)
		*inbytesleft -= in_true;
	    if (outbytesleft)
		*outbytesleft -= out_true;
	    if (inbuf && *inbuf)
		*inbuf += in_true;
	    if (outbuf && *outbuf)
		*outbuf += out_true;
	    last_errno = 0;
	    return 0; /* No error */
	case kTECUnmappableElementErr:
	    last_errno = EILSEQ;
	case kTECIncompleteElementErr:
	    last_errno = EINVAL;
	case kTECOutputBufferFullStatus:
	    last_errno = E2BIG;
	    return -1;
	default:
	    TRACE("iconv(%p, %p, %p, %p, %p) failed. (%d)\n",
		    cd, inbuf, inbytesleft, outbuf, outbytesleft, (int)st);
	    last_errno = EFAULT;
	    return -1;
    }
}

    int
iconv_close(iconv_t cd)
{
    if (cd)
    {
	/* Free all elements of iconv_t */
	if (cd->tec)
	    TECDisposeConverter(cd->tec);
	if (cd->tecReverse)
	    TECDisposeConverter(cd->tecReverse);
	if (cd->sniff)
	    TECDisposeSniffer(cd->sniff);
	vim_free(cd);
	last_errno = 0;
	return 0;
    }
    else
    {
	last_errno = EINVAL;
	return -1;
    }
}

    int *
iconv_errno()
{
    return &last_errno;
}
#endif /* MACOS_X_ICONVEMU */

#ifdef USE_MCH_GETTEXT

#define GETTEXT_BUFNUM		64
#define GETTEXT_BUFSIZE		256

    char *
mch_gettext(const char *msgid)
{
    static char		buf[GETTEXT_BUFNUM][GETTEXT_BUFSIZE];
    static int		bufnum = 0;
    const char		*msg = NULL;
    CFStringRef		strkey = NULL, strmsg = NULL;
    CFStringEncoding	enc;

    if (!msgid)
	goto MCH_GETTEXT_FINISH;
    enc = CFStringGetSystemEncoding();
    TRACE("mch_gettext(%s)\n", msgid);

    strkey = CFStringCreateWithCString(NULL, msgid, enc);
    if (!strkey)
    {
	TRACE("  Can't create a CFString for msgid.\n");
	goto MCH_GETTEXT_FINISH;
    }

    strmsg = CFCopyLocalizedString(strkey, NULL);
    if (!strmsg)
    {
	TRACE("  No localized strings for msgid.\n");
	goto MCH_GETTEXT_FINISH;
    }

    msg = CFStringGetCStringPtr(strmsg, enc);
    if (!msg)
    {
	/* This is as backup when CFStringGetCStringPtr was failed */
	CFStringGetCString(strmsg, buf[bufnum], GETTEXT_BUFSIZE, enc);
	msg = buf[bufnum];
	if (++bufnum >= GETTEXT_BUFNUM)
	    bufnum = 0;
    }
    TRACE("  Localized to: %s\n", msg);

MCH_GETTEXT_FINISH:
    if (strkey)
	CFRelease(strkey);
    if (strmsg)
	CFRelease(strmsg);
    return (char *)(msg ? msg : msgid);
}

    char *
mch_bindtextdomain(const char *domain, const char *dirname)
{
    TRACE("mch_bindtextdomain(%s, %s)\n", domain, dirname);
    return (char*)dirname;
}

    char *
mch_textdomain(const char *domain)
{
    TRACE("mch_textdomain(%s)\n", domain);
    return (char*)domain;
}
#endif
