#include "pch.h"
#include "BKFile.h"
#include "ibm2vxt.h"

const static std::_tstring cchStrEndEnum = _T(".!?:;");

// просмотр вперёд.
uint8_t ITCPC::VXT_Subr(CBaseFile *pInFile, const unsigned int &dwFlags, const int &nOtstup, uint32_t &wchPrSym)
{
	unsigned int t = 0;
	long pos = pInFile->GetPos();
	int nSpacesCount = 0;
	uint8_t chOut = CHR_N;

	while (!pInFile->isEOF())
	{
		auto wchSym = pInFile->ReadChar();

		if (pInFile->isEOF())
		{
			break;
		}

		if (wchSym == 0)
		{
			t = INPUT_SYMBOL_NULL;
		}
		else if (wchSym == CHR_N)
		{
			t = INPUT_SYMBOL_N;
		}
		else if (wchSym == CHR_R)
		{
			t = INPUT_SYMBOL_R;
		}
		else if (wchSym == CHR_TAB)
		{
			int nNextTabPos = (nSpacesCount / pInFile->getTabWidth() + 1) * pInFile->getTabWidth();

			do
			{
				nSpacesCount++;
			}
			while (nSpacesCount < nNextTabPos);

			continue;
		}
		else if (wchSym == CHR_SPACE)
		{
			nSpacesCount++;
			continue;
		}
		else
		{
			if (nSpacesCount > nOtstup)
			{
				if (cchStrEndEnum.find(wchPrSym) != std::_tstring::npos)
				{
					break;
				}
			}

			chOut = 0;
			break;
		}

		if ((dwFlags & t) != 0)
		{
			break;
		}
	}

	pInFile->SetPos(pos);
	return chOut;
}

void ITCPC::IBM2VXT(std::unique_ptr<CBaseFile> pInFile, std::unique_ptr<CBaseFile> pOutFile)
{
	if (pInFile == nullptr || pOutFile == nullptr)
	{
		std::_tcout << _T("Недостаточно памяти.\n");
		return;
	}

	int nTmpOtstup = 0;       // временный отступ.
	int nOtstup = 1000000;    // отступ в строке.
	int nCharsCount = 0;      // счётчик символов в строке для входного файла
	int nSpacesCount = 0;     // счётчик пробельных символов.
	unsigned int t;           // временные флаги.
	unsigned int dwFlags = 0; // флаги.
	uint32_t wchPrSym = 0;    // предыдущий символ
	uint8_t chOut;            // выходной символ, как правило управляющий

	if (!pInFile->Open(false))
	{
		std::_tcout << _T("Ошибка! '") << pInFile->getFileName()._tstring() << _T("' не открывается.\n");
		return;
	}

	if (!pOutFile->Open(true))
	{
		std::_tcout << _T("Ошибка! '") << pOutFile->getFileName()._tstring() << _T("' не открывается.\n");
		return;
	}

	while (!pInFile->isEOF())
	{
		uint32_t wchSym = pInFile->ReadChar(); // читаем очередной символ

		if (pInFile->isEOF())
		{
			break;
		}

		switch (wchSym) // проверка на конец строки или не конец строки
		{
			case 0: t = INPUT_SYMBOL_NULL;
				break;

			case CHR_N: t = INPUT_SYMBOL_N;
				break;

			case CHR_R: t = INPUT_SYMBOL_R;
				break;

			default:
			{
				// если не символы конца строки, то сбросим все флаги разных вариантов конца строки
				dwFlags &= ~INPUT_SYMBOL_EOLN;

				switch (wchSym)
				{
					case CHR_TAB: // если табуляция
					{
						int nNextTabPos = (nCharsCount / pInFile->getTabWidth() + 1) * pInFile->getTabWidth();

						do  // забъём пробелами сколько нужно
						{
							nCharsCount++;  // увеличим счётчик символов в строке
							nSpacesCount++; // увеличим счётчик пробельных символов

							if (dwFlags & INPUT_FLAG_ABSATZ)
							{
								nTmpOtstup++;   // если нужно считаем новый отступ
							}
						}
						while (nCharsCount < nNextTabPos);

						break;
					}

					case CHR_SPACE:    // если пробел
						nCharsCount++;
						nSpacesCount++;

						if (dwFlags & INPUT_FLAG_ABSATZ)
						{
							nTmpOtstup++;   // если нужно считаем новый отступ
						}

						break;

					default:    // все непробельные символы
					{
						if (dwFlags & INPUT_FLAG_ABSATZ) // отступ считали?
						{
							dwFlags &= ~INPUT_FLAG_ABSATZ;  // больше не нужно

							if (nTmpOtstup < nOtstup)   // и если он меньше текущего
							{
								nOtstup = nTmpOtstup;   // новый отступ будет высчитанным.
							}
						}

						if (nSpacesCount) // если насчитали сколько-то пробелов
						{
							// кодируем пробелы
							while (nSpacesCount > 8)
							{
								chOut = nSpacesCount & 7;
								nSpacesCount >>= 3;

								if (chOut == 0)
								{
									chOut = 8;
									nSpacesCount--;
								}

								pOutFile->WriteByte(chOut);
							}

							chOut = nSpacesCount;
							pOutFile->WriteByte(chOut);
							nSpacesCount = 0;
						}

						wchPrSym = wchSym;
						pOutFile->WriteChar(wchSym); nCharsCount++;
					}
				}

				continue;
			}
		}

		if ((dwFlags & INPUT_SYMBOL_EOLN) == 0) // если ни одного флага конца строки нету
		{
			dwFlags |= t;   // установим тот, который получили
		}
		else
		{
			// если хоть один был
			if ((dwFlags & t) == 0) // и если не точно такой, что был в прошлый раз
			{
				dwFlags &= ~INPUT_SYMBOL_EOLN; // то сбросим флаги конца строки
				continue; // и пойдём читать следующий символ
			}
		}

		// обработка конца строки
		if (nCharsCount != 0)
		{
			chOut = VXT_Subr(pInFile.get(), dwFlags, nOtstup, wchPrSym);
		}
		else
		{
			chOut = CHR_N;
		}

		nSpacesCount = 0;
		nTmpOtstup = 0;
		dwFlags |= INPUT_FLAG_ABSATZ;
		pOutFile->WriteByte(chOut);
		nCharsCount = 0;
	}

	pInFile->Close();
	pOutFile->Close();
}

