// ITCPC.cpp: определяет точку входа для консольного приложения.
//

#include "stdafx.h"
#include "ITCPC.h"
#include "getopt.h"
#include <io.h>

#include "bk2ibm.h"
#include "ibm2bk.h"
#include "ibm2vxt.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// Единственный объект приложения

CWinApp theApp;

using namespace std;

int _tmain(int argc, TCHAR *argv[], TCHAR *envp[])
{
	_tsetlocale(LC_ALL, _T("Russian"));
	int nRetCode = 0;
	HMODULE hModule = ::GetModuleHandle(NULL);

	if (hModule != NULL)
	{
		// инициализировать MFC, а также печать и сообщения об ошибках про сбое
		if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
		{
			// TODO: измените код ошибки соответственно своим потребностям
			_tprintf(_T("Критическая ошибка: сбой при инициализации MFC\n"));
			nRetCode = 1;
		}
		else
		{
			// TODO: нужно сделать опцию - сколько пробелов занимает табулятор. причём чтобы это было произвольное число.
			CString strStartPath;   // директория, откуда начинать искать образы
			static struct option long_options[] =
			{
				{_T("help"),        ARG_NONE, 0, _T('h')},
				{_T("input"),       ARG_REQ, 0, _T('i')},
				{_T("output"),      ARG_REQ, 0, _T('o')},
				{_T("tab"),         ARG_REQ, 0, _T('t')},
				{ ARG_NULL , ARG_NULL , ARG_NULL , ARG_NULL }
			};
			static TCHAR optstring[] = _T("hi:o:t:");
			workMode WorkParam;
			WorkParam.chInM = _T('A');
			WorkParam.chOutM = _T('U');
			WorkParam.nTabWidth = 0;

			if (argc < 2)
			{
				//если запускаем без аргументов, то сразу вывести подсказку и выйти.
				Usage();
				return nRetCode;
			}

			CString strTmp;
			int option_index = 0;
			int c;

			while ((c = getopt_long(argc, argv, optstring, long_options, &option_index)) != -1)
			{
				// Handle options
				switch (c)
				{
					case _T('i'):
						strTmp = CString(optarg);

						if (!strTmp.IsEmpty())
						{
							WorkParam.chInM = strTmp.MakeUpper().GetAt(0); //оставим только первую букву
						}

						break;

					case _T('o'):
						strTmp = CString(optarg);

						if (!strTmp.IsEmpty())
						{
							WorkParam.chOutM = strTmp.MakeUpper().GetAt(0); //оставим только первую букву
						}

						break;

					case _T('t'):
						WorkParam.nTabWidth = _tcstol(optarg, NULL, 10);
						break;

					case _T('h'):
						Usage();
						return nRetCode;
				}
			}

			//проверяем параметры на корректность.
			if (!CheckInputParams(&WorkParam))
			{
				Usage();
				return nRetCode;
			}

			if (WorkParam.chInM == WorkParam.chOutM)
			{
				_tprintf(_T("Бессмысленное преобразование делать не будем.\n"));
				return nRetCode;
			}

			if (WorkParam.chInM == _T('A') && (WorkParam.chOutM != _T('E') && WorkParam.chOutM != _T('V')))
			{
				_tprintf(_T("Подозрительное преобразование на всякий случай делать не будем.\n"));
				return nRetCode;
			}

			argc -= optind;
			argv += optind;
			_tprintf(_T("Преобразование %s -> %s\n"), GetFormatName(WorkParam.chInM).GetBuffer(), GetFormatName(WorkParam.chOutM).GetBuffer());
			struct _tfinddatai64_t f_file;
			intptr_t hFile;
			int nFilesCount = 0; //счётчик обработанных файлов
			CMyFile *pInFile = NULL;
			CMyFile *pOutFile = NULL;

			for (; argc > 0; argc--, argv++) //теперь, пока аргументы не кончатся - обрабатывать
			{
				if ((hFile = _tfindfirsti64(*argv, &f_file)) == -1L)
				{
					_tprintf(_T("Файл(ы) '%s' не найден(ы)\n"), *argv);
				}
				else
				{
					do
					{
						if (!(f_file.attrib & _A_SUBDIR)) //если не поддиректория
						{
							//делаем имя входного файла
							WorkParam.strInFileName = CString(f_file.name);
							//делаем имя выходного файла.
							MakeOutName(&WorkParam);
							_tprintf(_T("%s -> %s\n"), WorkParam.strInFileName.GetBuffer(), WorkParam.strOutFileName.GetBuffer());

							switch (WorkParam.chInM)
							{
								default:
								case _T('A'):
									pInFile = new CWinFile(WorkParam.strInFileName);
									break;

								case _T('U'):
									pInFile = new CWinFile(WorkParam.strInFileName, CWinFile::CHARSET_UNICODE);
									break;

								case _T('W'):
									pInFile = new CWinFile(WorkParam.strInFileName, CWinFile::CHARSET_CP1251);
									break;

								case _T('O'):
									pInFile = new CWinFile(WorkParam.strInFileName, CWinFile::CHARSET_CP866);
									break;

								case _T('K'):
									pInFile = new CWinFile(WorkParam.strInFileName, CWinFile::CHARSET_KOI8);
									break;

								case _T('E'):
									pInFile = new CBKFile(WorkParam.strInFileName);
									break;

								case _T('V'):
									pInFile = new CBKFile(WorkParam.strInFileName);
									break;
							}

							if (2 <= WorkParam.nTabWidth && WorkParam.nTabWidth <= 16)
							{
								pInFile->setTabWidth(WorkParam.nTabWidth);
							}

							switch (WorkParam.chOutM)
							{
								default:
								case _T('U'):
									pOutFile = new CWinFile(WorkParam.strOutFileName, CWinFile::CHARSET_UNICODE);
									BK2IBM(pInFile, pOutFile);
									break;

								case _T('8'):
									pOutFile = new CWinFile(WorkParam.strOutFileName, CWinFile::CHARSET_UTF8);
									BK2IBM(pInFile, pOutFile);
									break;

								case _T('W'):
									pOutFile = new CWinFile(WorkParam.strOutFileName, CWinFile::CHARSET_CP1251);
									BK2IBM(pInFile, pOutFile);
									break;

								case _T('O'):
									pOutFile = new CWinFile(WorkParam.strOutFileName, CWinFile::CHARSET_CP866);
									BK2IBM(pInFile, pOutFile);
									break;

								case _T('K'):
									pOutFile = new CWinFile(WorkParam.strOutFileName, CWinFile::CHARSET_KOI8);
									BK2IBM(pInFile, pOutFile);
									break;

								case _T('E'):
									pOutFile = new CBKFile(WorkParam.strOutFileName);
									IBM2BK(pInFile, pOutFile);
									break;

								case _T('V'):
									pOutFile = new CBKFile(WorkParam.strOutFileName);
									IBM2VXT(pInFile, pOutFile);
									break;
							}

							if (pInFile)
							{
								delete pInFile;
								pInFile = NULL;
							}

							if (pOutFile)
							{
								delete pOutFile;
								pOutFile = NULL;
							}

							nFilesCount++;
						}
						else
						{
							//если директория
							if ((lstrcmp(f_file.name, _T("..")) == 0) || (lstrcmp(f_file.name, _T(".")) == 0))
							{
								continue;
							}

							_tchdir(f_file.name); //заходим в поддиректорию
							// TODO: когда понадобится, сделать рекурсивный обход директорий
							_tchdir(_T("..")); //выходим обратно
						}
					}
					while (_tfindnexti64(hFile, &f_file) == 0);

					_findclose(hFile);
				}
			}

			_tprintf(_T("Готово, обработано %d файлов.\n"), nFilesCount);
		}
	}
	else
	{
		// TODO: Измените код ошибки соответственно своим потребностям
		_tprintf(_T("Критическая ошибка: неудачное завершение GetModuleHandle\n"));
		nRetCode = 1;
	}

	return nRetCode;
}

void Usage()
{
	_tprintf(_T("Конвертор текстов Win <--> БК v3.1\n"));
	_tprintf(_T("Использование:\n"));
	_tprintf(_T("ITCPC -h (--help)\n"));
	_tprintf(_T("  Вывод этой справки.\n"));
	_tprintf(_T("ITCPC [-i<in_mode>] [-o<out_mode>] [-t<n>] <InputFile1.ext> {<InputFileN.ext> ...}\n"));
	_tprintf(_T("  -i<in_mode> (--input <in_mode>) - задать формат входного файла.\n"));
	_tprintf(_T("  Возможные режимы: a - автоопределение (по умолчанию, если параметр\n"));
	_tprintf(_T("      отсутствует). Не работает для БКшных файлов. Они опознаются как\n"));
	_tprintf(_T("      файл в KOI8R, и будут обработаны неправильно.\n"));
	_tprintf(_T("                    u - Unicode UTF-16LE или Unicode UTF-8, форматы\n"));
	_tprintf(_T("      корректно определяются даже без BOM.\n"));
	_tprintf(_T("                    w - Windows CP-1251\n"));
	_tprintf(_T("                    o - OEM CP-866\n"));
	_tprintf(_T("                    k - KOI8R\n"));
	_tprintf(_T("                    e - БКшный файл в формате Edasp\n"));
	_tprintf(_T("                    v - БКшный файл в формате Vortex\n\n"));
	_tprintf(_T("  -o<out_mode> (--output <out_mode>) - задать формат выходного файла.\n"));
	_tprintf(_T("  Возможные режимы: u - Unicode UTF-16LE (по умолчанию, если параметр\n"));
	_tprintf(_T("      отсутствует)\n"));
	_tprintf(_T("                    8 - Unicode UTF-8\n"));
	_tprintf(_T("                    w - Windows CP-1251\n"));
	_tprintf(_T("                    o - OEM CP-866\n"));
	_tprintf(_T("                    k - KOI8R\n"));
	_tprintf(_T("                    e - БКшный файл в формате Edasp\n"));
	_tprintf(_T("                    v - БКшный файл в формате Vortex\n\n"));
	_tprintf(_T("  -t<n> (--tab <n>) - задать ширину табулятора для входного файла.\n"));
	_tprintf(_T("      Допускаются значения от 2 до 16, остальные игнорируются.\n"));
	_tprintf(_T("      Принятые значения по умолчанию: для файлов Windows tab = 4,\n"));
	_tprintf(_T("      для файлов БК tab = 8.\n\n"));
	_tprintf(_T("  InputFile1.ext - входной файл. Допускается использование масок файлов.\n"));
//	_tprintf(_T("      и путей.\n")); //!!! пока без путей.
}

BOOL CheckInputParams(workMode *pwmode)
{
	CString strInP = _T("AUWOKEV");
	CString strOutP = _T("8UWOKEV");

	if (strInP.Find(pwmode->chInM) < 0)
	{
		return FALSE;
	}

	if (strOutP.Find(pwmode->chOutM) < 0)
	{
		return FALSE;
	}

	return TRUE;
}

CString GetFormatName(TCHAR ch)
{
	switch (ch)
	{
		case _T('A'):
			return _T("Авто");

		case _T('U'):
			return _T("Unicode UTF-16LE");

		case _T('W'):
			return _T("Windows 1251");

		case _T('O'):
			return _T("OEM CP-866");

		case _T('K'):
			return _T("KOI8R");

		case _T('E'):
			return _T("Edasp");

		case _T('V'):
			return _T("Vortex");

		case _T('8'):
			return _T("Unicode UTF-8");
	}

	return _T("Неизвестно");
}

//делаем имя выходного файла
void MakeOutName(workMode *pwmode)
{
	//сперва скопируем имя файла
	pwmode->strOutFileName = pwmode->strInFileName;
	//если есть расширение, найдём его
	int l = pwmode->strOutFileName.GetLength();
	int nCommaPos = -1;

	for (int i = l - 1; i >= 0; --i)
	{
		if (pwmode->strOutFileName.GetAt(i) == _T('.'))
		{
			nCommaPos = i;
			break;
		}
	}

	CString outExt;

	switch (pwmode->chOutM)
	{
		case _T('E'):
			outExt = _T(".edp");
			break;

		case _T('V'):
			outExt = _T(".vxt");
			break;

		default:
			outExt = _T(".txt");
			break;
	}

	if (nCommaPos >= 0) //если расширение есть
	{
		if (pwmode->strOutFileName.Right(l - nCommaPos).CompareNoCase(outExt))
		{
			//если расширения не равны
			pwmode->strOutFileName = pwmode->strOutFileName.Left(nCommaPos) + outExt; //то заменим старое на новое
		}
		else
		{
			//а если расширения равны
			pwmode->strOutFileName += outExt; //то добавим ещё одно.
		}
	}
	else
	{
		//а если расширения нету
		pwmode->strOutFileName += outExt; //то просто добавим новое.
	}
}

