// -*- coding:unix; mode:c++; tab-width:4; c-basic-offset:4; indent-tabs-mode:nil -*-
/*------------------------------------------------------------------------------
VnConv: Vietnamese Encoding Converter Library
UniKey Project: http://unikey.sourceforge.net
Copyleft (C) 1998-2002 Pham Kim Long
Contact: longp@cslab.felk.cvut.cz

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
--------------------------------------------------------------------------------*/

#include "charset.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#if defined(_WIN32)
	#include <io.h>
	#include <fcntl.h>
#endif

#include "vnconv.h"

int vnFileStreamConvert(int inCharset, int outCharset, FILE * inf, FILE *outf);

DllExport int genConvert(VnCharset & incs, VnCharset & outcs, ByteInStream & input, ByteOutStream & output)
{
	StdVnChar stdChar;
	int bytesRead, bytesWritten;

	incs.startInput();
	outcs.startOutput();

	int ret = 1;
	while (!input.eos()) {
        stdChar = 0;
		if (incs.nextInput(input, stdChar, bytesRead)) {
			if (stdChar != INVALID_STD_CHAR) {
			  if (VnCharsetLibObj.m_options.toLower)
			    stdChar = StdVnToLower(stdChar);
			  else if (VnCharsetLibObj.m_options.toUpper)
			    stdChar = StdVnToUpper(stdChar);
			  if (VnCharsetLibObj.m_options.removeTone)
			    stdChar = StdVnGetRoot(stdChar);
			  ret = outcs.putChar(output, stdChar, bytesWritten);
			}
		}
		else break;
	}
	return (ret? 0 : VNCONV_OUT_OF_MEMORY);
}

//----------------------------------------------
// Arguments:
//       inCharset: charset of input
//       outCharset: charset of output
//       input: input data
//       output: output data
//       inLen: [in]  size of input. if inLen = -1, input data is null-terminated.
//              [out] if input inLen != -1, output iLen is the numbers of byte left in input.
//       maxOutLen: [in] size of output.
//                  [out] number of bytes output, if enough memory
//                        number of bytes needed for output, if not enough memory
// Returns:  0 if successful
//           error code: if failed
//----------------------------------------------
//int VnConvert(int inCharset, int outCharset, UKBYTE *input, UKBYTE *output, int & inLen, int & maxOutLen)

DllExport int VnConvert(int inCharset, int outCharset, UKBYTE *input, UKBYTE *output,
	      int * pInLen, int * pMaxOutLen)
{
	int inLen, maxOutLen;
	int ret = -1;

	inLen = *pInLen;
	maxOutLen = *pMaxOutLen;

	if (inLen != -1 && inLen < 0) // invalid inLen
		return ret;

	VnCharset *pInCharset = VnCharsetLibObj.getVnCharset(inCharset);
	VnCharset *pOutCharset = VnCharsetLibObj.getVnCharset(outCharset);

	if (!pInCharset || !pOutCharset)
		return VNCONV_INVALID_CHARSET;

	StringBIStream is(input, inLen, pInCharset->elementSize());
	StringBOStream os(output, maxOutLen);

	ret = genConvert(*pInCharset, *pOutCharset, is, os);
	*pMaxOutLen = os.getOutBytes();
	*pInLen = is.left();
	return ret;
}

//---------------------------------------
// Arguments:
//   inFile: input file name. NULL if STDIN is used
//   outFile: output file name, NULL if STDOUT is used
// Returns:
//     0: successful
//     errCode: if failed
//---------------------------------------
DllExport int VnFileConvert(int inCharset, int outCharset, const char *inFile, const char *outFile)
{
	FILE *inf = NULL;
	FILE *outf = NULL;
	int ret = 0;
	char tmpName[32];

	if (inFile == NULL) {
		inf = stdin;
#if defined(_WIN32)
		_setmode( _fileno(stdin), _O_BINARY);
#endif
	}
	else {
		inf = fopen(inFile, "rb");
		if (inf == NULL) {
			ret = VNCONV_ERR_INPUT_FILE;
			goto end;
		}
	}

	if (outFile == NULL)
		outf = stdout;
	else {
		// setup temporary output file (because real output file may be the same as input file
		char outDir[256];
		strcpy(outDir, outFile);

#if defined(_WIN32)
		char *p = strrchr(outDir, '\\');
#else
		char *p = strrchr(outDir, '/');
#endif

		if (p == NULL)
			outDir[0] = 0;
		else
			*p = 0;

		strcpy(tmpName, outDir);
        strcat(tmpName, "XXXXXX");

		if (mkstemp(tmpName) == -1) {
			fclose(inf);
			ret = VNCONV_ERR_OUTPUT_FILE;
			goto end;
		}
		outf = fopen(tmpName, "wb");

		if (outf == NULL) {
			fclose(inf);
			ret = VNCONV_ERR_OUTPUT_FILE;
			goto end;
		}
	}


	ret = vnFileStreamConvert(inCharset, outCharset, inf, outf);
	if (inf != stdin)
		fclose(inf);
	if (outf != stdout) {
		fclose(outf);

		// delete output file if exisits
		if (ret == 0) {
			remove(outFile);
#if !defined(_WIN32)
			char cmd[256];
			sprintf(cmd, "mv %s %s", tmpName, outFile);
			cmd[0] = system(cmd);
#else
			if (rename(tmpName, outFile) != 0) {
				remove(tmpName);
				ret = VNCONV_ERR_OUTPUT_FILE;
				goto end;
			}
#endif
		}
		else
			remove(tmpName);
	}

end:
#if defined(_WIN32)
	if (inf == stdin) {
		_setmode( _fileno(stdin), _O_BINARY);
	}
#endif
	return ret;
}

//------------------------------------------------
// Returns:
//     0: successful
//     errCode: if failed
//---------------------------------------
int vnFileStreamConvert(int inCharset, int outCharset, FILE * inf, FILE *outf)
{
	VnCharset *pInCharset = VnCharsetLibObj.getVnCharset(inCharset);
	VnCharset *pOutCharset = VnCharsetLibObj.getVnCharset(outCharset);

	if (!pInCharset || !pOutCharset)
		return VNCONV_INVALID_CHARSET;

	if (outCharset == CONV_CHARSET_UNICODE) {
		UKWORD sign = 0xFEFF;
		fwrite(&sign, sizeof(UKWORD), 1, outf);
	}

	FileBIStream is;
	FileBOStream os;

	is.attach(inf);
	os.attach(outf);

	return genConvert(*pInCharset, *pOutCharset, is, os);
}

const char *ErrTable[VNCONV_LAST_ERROR] =
{"No error",
 "Unknown error",
 "Invalid charset",
 "Error opening input file",
 "Error opening output file",
 "Error writing to output stream",
 "Not enough memory",
};

DllExport const char * VnConvErrMsg(int errCode)
{
	if (errCode < 0 || errCode >= VNCONV_LAST_ERROR)
		errCode = VNCONV_UNKNOWN_ERROR;
	return ErrTable[errCode];
}

