#include "pch.h"

#include "BaseFile.h"

// нижняя таблица для конвертации ibm 2 bk
const std::array<uint8_t, ARR_LOWROW_SIZE> CBaseFile::m_LowRow =
{
	10,  127, 127, 162, 184, 180, 169,  46,
	127,   9,  10,  43,  43,  13, 127,  36,
	190, 177, 179,  33, 160,  36,  95, 179,
	179, 173, 190, 177,  95, 181, 179, 173
};


// таблица соответствия верхней половины аскии кодов с 128 по 255, включая псевдографику
const std::array<wchar_t, ARR_KOI8_SIZE> CBaseFile::m_koi8tbl =
{
	// {200..237} этих символов на бк нету.
	L' ', L' ', L' ', L' ', L' ', L' ', L' ', L' ',
	L' ', L' ', L' ', L' ', L' ', L' ', L' ', L' ',
	L' ', L' ', L' ', L' ', L' ', L' ', L' ', L' ',
	L' ', L' ', L' ', L' ', L' ', L' ', L' ', L' ',
	// {240..277}
	0xb6,    0x2534,  0x2665,  0x2510,  0x2561,  0x251c,  0x2514,  0x2550,
	0x2564,  0x2660,  0x250c,  0x252c,  0x2568,  0x2193,  0x253c,  0x2551,
	0x2524,  0x2190,  0x256c,  0x2191,  0x2663,  0x2500,  0x256b,  0x2502,
	0x2666,  0x2518,  0x256a,  0x2565,  0x2567,  0x255e,  0x2192,  0x2593,
	// {300..337}
	L'ю', L'а', L'б', L'ц', L'д', L'е', L'ф', L'г',
	L'х', L'и', L'й', L'к', L'л', L'м', L'н', L'о',
	L'п', L'я', L'р', L'с', L'т', L'у', L'ж', L'в',
	L'ь', L'ы', L'з', L'ш', L'э', L'щ', L'ч', L'ъ',
	// {340..377}
	L'Ю', L'А', L'Б', L'Ц', L'Д', L'Е', L'Ф', L'Г',
	L'Х', L'И', L'Й', L'К', L'Л', L'М', L'Н', L'О',
	L'П', L'Я', L'Р', L'С', L'Т', L'У', L'Ж', L'В',
	L'Ь', L'Ы', L'З', L'Ш', L'Э', L'Щ', L'Ч', L'Ъ'
};



CBaseFile::CBaseFile(const fs::path &strName)
	: m_pathFileName(strName)
{
}


CBaseFile::~CBaseFile()
{
	Close();
}

bool CBaseFile::Open(bool bWrite)
{
	return false;
}

void CBaseFile::Close()
{
	if (m_f)
	{
		fclose(m_f);
		m_f = nullptr;
		m_nFileLength = 0;
	}
}

uint32_t CBaseFile::ReadChar()
{
	return 0;
}

uint8_t CBaseFile::ReadByte() const
{
	uint8_t b = 0;

	if (m_f)
	{
		fread(&b, 1, sizeof(uint8_t), m_f);
	}

	return b;
}

uint16_t CBaseFile::ReadWord() const
{
	uint16_t w = 0;

	if (m_f)
	{
		fread(&w, 1, sizeof(uint16_t), m_f);
	}

	return w;
}

bool CBaseFile::WriteChar(uint32_t ch)
{
	return false;
}

bool CBaseFile::WriteByte(uint8_t b) const
{
	size_t n = 0;

	if (m_f)
	{
		n = fwrite(&b, 1, sizeof(uint8_t), m_f);
	}

	return (n == sizeof(uint8_t));
}

bool CBaseFile::WriteWord(uint16_t w) const
{
	size_t n = 0;

	if (m_f)
	{
		n = fwrite(&w, 1, sizeof(uint16_t), m_f);
	}

	return (n == sizeof(uint16_t));
}

long CBaseFile::GetPos() const
{
	if (m_f)
	{
		return ftell(m_f);
	}

	return 0;
}

int CBaseFile::SetPos(long pos, long mode) const
{
	if (m_f)
	{
		return fseek(m_f, pos, mode);
	}

	return 0;
}

bool CBaseFile::isEOF() const
{
	if (m_f)
	{
		// о том, что конец файла,мы узнаем только после чтения
		if (feof(m_f) != 0)
		{
			return true;
		}

		// а надо перед чтением, чтобы лишнего нуля не было в конце
		if (m_nFileLength && ftell(m_f) >= m_nFileLength)
		{
			return true;
		}

		return false;
	}

	return true;
}

// Проверим, а не существует ли уже такой файл?
bool CBaseFile::CheckOutFile()
{
	constexpr auto MAX_COUNT = 10000;
	int nNum = 0;
	fs::path newFileName = m_pathFileName;
	auto strExt = newFileName.extension()._tstring();
	auto strName = newFileName.stem()._tstring();

	while (fs::exists(newFileName) && nNum < MAX_COUNT)
	{
		// сделаем новое имя
		nNum++;
		auto strNameN = strName + _T("(") + std::_tto_string(nNum) + _T(")") + strExt;
		newFileName.replace_filename(strNameN);
	} // будем проверять имена с цифрой до тех пор, пока не найдём

	if (nNum >= MAX_COUNT) // если вышли по такому условию, то явно что-то не так
	{
		return false;
	}

	m_pathFileName = newFileName;
	return true;
}

uint32_t CBaseFile::BKToUNICODE_Byte(uint8_t b) const
{
	if (b == 0)
	{
		return 0;
	}

	if (b < CHR_SPACE)
	{
		return uint32_t(b); // на БК коды меньше пробела - обычно управляющие символы. и требуют обработки по своему.
	}

	if (CHR_SPACE <= b && b < CHR_127)
	{
		return uint32_t(b);
	}

	if (b == CHR_127)
	{
		return uint32_t(CHR_USCQR);
	}

	if (b >= ARR_KOI8_SIZE)
	{
		return m_koi8tbl[static_cast<size_t>(b) - ARR_KOI8_SIZE];
	}

	return 0; // этот return не должен выполниться в принципе.
}

/*
преобразование юникодного символа, в бкшный кои8.
вход:
tch - преобразуемый символ
выход:
кои8 символ
*/
uint8_t CBaseFile::UNICODEtoBK_Byte(uint32_t tch) const
{
	if (tch < CHR_SPACE) // если символ меньше пробела,
	{
		return m_LowRow[tch];
	}

	if (CHR_SPACE <= tch && tch < CHR_127) // если буквы-цифры-знаки препинания
	{
		return (tch & 0xff); // то буквы-цифры-знаки препинания
	}

	if (tch == CHR_USCQR) // если такое
	{
		return CHR_127; // то это. это единственное исключение в нижней половине аски кодов
	}

	// если всякие другие символы
	// то ищем в таблице нужный нам символ, а его номер - будет кодом кои8
	if (tch == L'ё')
	{
		tch = L'е'; // меняем ё на е, т.к. на БК нету такой буквы
	}
	else if (tch == L'Ё')
	{
		tch = L'Е'; // меняем ё на е, т.к. на БК нету такой буквы
	}

	for (int i = CHR_SPACE; i < ARR_KOI8_SIZE; ++i)
	{
		if (m_koi8tbl[i] == tch)
		{
			return uint8_t(i & 0xff) + 0200;
		}
	}

	return CHR_SPACE; // если такого символа нету в таблице - будет пробел
}

FILE *CBaseFile::BKOpen(const fs::path &filepath, const std::_tstring &mode)
{
#if defined(_WIN32)
	FILE *file = ::_wfopen(filepath.c_str(), mode.c_str());
#else
	fs::path convertedMode = mode;
	FILE *file = ::fopen(filepath.c_str(), convertedMode.c_str());
#endif

	if (!file)
	{
		const char *reason = std::strerror(errno);
		std::string err{ "opening '" };
		err += filepath.generic_string() + std::string{ "' failed: " } + reason;
		printf("%s", err.c_str());
	}

	return file;
}
