#include "stdafx.h"

#include "BK_Util.h"
#include "locale.h"

/*
преобразование имени файла из кои8 в юникод. с удалением запрещённых символов
*/
std::wstring BKToUNICODE(uint8_t *pBuff, const size_t size, const wchar_t table[])
{
	std::wstring strRet = L"";

	for (size_t i = 0; i < size; ++i)
	{
		wchar_t tch = BKToUNICODE_Byte(pBuff[i], table);

		if (tch == 0)
		{
			break;
		}

		strRet.push_back(tch);
	}

	return strRet;
}

wchar_t BKToUNICODE_Byte(const uint8_t b, const wchar_t table[])
{
	if (b == 0)
	{
		return 0;
	}

	if (b < 32)
	{
		return L' ';
	}
	else if (b < 127)
	{
		return static_cast<wchar_t>(b);
	}
	else if (b == 127)
	{
		return wchar_t(0x25a0);
	}

	return table[b - 128];
}


/*
преобразование юникодной строки в бкшный кои8.
вход:
ustr - преобразуемая строка
pBuff - буфер, куда выводится результат
bufSize - размер буфера
bFillBuf - флаг. если строка короче размера буфера, буфер до конца забивается пробелами. (конца строки - 0 нету.)
*/
void UNICODEtoBK(const std::wstring &ustr, uint8_t *pBuff, const size_t bufSize, const bool bFillBuf)
{
	uint8_t b;
	size_t bn = 0;
	const size_t len = ustr.length();

	for (size_t n = 0; n < len; ++n)
	{
		wchar_t ch = ustr[n]; //берём очередной символ

		if (ch < 32) //если символ меньше пробела,
		{
			b = ' ';    //то будет пробел
		}
		else if (ch < 127) //если буквы-цифры- знаки препинания
		{
			b = static_cast<uint8_t>(ch & 0xff); // то буквы-цифры- знаки препинания
		}
		else if (ch == 0x25a0) //если такое
		{
			b = 127; //то это, это единственное исключение в нижней половине аски кодов
		}
		else //если всякие другие символы
		{
			//то ищем в таблице нужный нам символ, а его номер - будет кодом кои8
			b = ' '; //если такого символа нету в таблице - будет пробел

			for (uint8_t i = 0; i < 128; ++i)
			{
				if (ch == koi8tbl10[i])
				{
					b = i + 0200;
					break;
				}
			}
		}

		pBuff[bn] = b;

		if (++bn >= bufSize)
		{
			break;
		}
	}

	if (bFillBuf)
	{
		while (bn < bufSize)
		{
			pBuff[bn++] = ' ';
		}
	}
}

//меняем страшные символы '>','<',':','|','?','*','\','/' на нестрашную '_'
//и все коды меньше пробела - на '_'
std::wstring SetSafeName(const std::wstring &str)
{
	const wchar_t rch = L'_';
	//res = replaceChars(res, std::wstring(L"\"*><|:?\\/"), rch);
	std::wstring res = replaceChars(str, std::wstring(L"*"), 0x2022);
	res = replaceChars(res, std::wstring(L">"), 0x203a);
	res = replaceChars(res, std::wstring(L"<"), 0x2039);
	res = replaceChars(res, std::wstring(L"\""), 0x201D);
	res = replaceChars(res, std::wstring(L"|"), 0xA6);
	res = replaceChars(res, std::wstring(L":?\\/"), rch);

	//а теперь заменим все символы с кодом меньше 32
	for (wchar_t &wch : res)
	{
		if (wch < 32)
		{
			wch = rch;
		}
	}

	//ещё проверка на зарезервированные имена

	const size_t n = res.find(L'.'); //находим первую точку
	std::wstring name = res; //имя без расширений
	if (n != std::wstring::npos)
	{
		name = res.substr(0, n);
	}

	name = strToLower(name);

	//отлавливаем строго трёхсимвольные имена
	if (name == L"aux" || name == L"nul" || name == L"con" || name == L"prn")
	{
		res.insert(3, L"#");	//вставляем после запрещённого имени этот символ, и всё
								//становится разрешённым
	}
	else if (name.length() == 4) //теперь отлавливаем строго 4х символьные имена
	{
		std::wstring str3 = strToLower(name.substr(0, 3)); //первые 3 символа имени
		wchar_t ch4 = name.at(3); //4й символ

		if ((str3 == L"com" || str3 == L"lpt") && (L'0' <= ch4 && ch4 <= L'9'))
		{
			res.insert(3, L"#"); //вставляем после запрещённого имени этот символ, и всё
								//становится разрешённым
		}
	}
	//всё остальное, даже если содержит часть зарезервированного имени, не является запрещённым
	return res;
}

//тупой класс std::wstring не имеет многих нужных функций.

std::wstring trim(const std::wstring &str, const wchar_t trim_char)
{
	std::wstring res = trimLeft(str, trim_char);
	return trimRight(res, trim_char);
}

std::wstring trimLeft(const std::wstring &str, const wchar_t trim_char)
{
	std::wstring res = str;

	while (!res.empty() && (res.front() == trim_char))
	{
		res.erase(res.begin());
	}

	return res;
}

std::wstring trimRight(const std::wstring &str, const wchar_t trim_char)
{
	std::wstring res = str;

	while (!res.empty() && (res.back() == trim_char))
	{
		res.pop_back();
	}

	return res;
}

std::wstring  replaceChar(const std::wstring &str, const wchar_t src, const wchar_t dst)
{
	if (!str.empty())
	{
		std::wstring res;

		for (wchar_t wch : str)
		{
			if (wch == src)
			{
				wch = dst;
			}

			res.push_back(wch);
		}

		return res;
	}

	return str;
}

std::wstring strToUpper(const std::wstring &str)
{
	std::wstring res;

	if (!str.empty())
	{
		_locale_t loc = _create_locale(LC_ALL, "Russian");

		for (wchar_t n : str)
		{
			res.push_back(_toupper_l(n, loc));
		}

		_free_locale(loc);
	}

	return res;
}

std::wstring strToLower(const std::wstring &str)
{
	std::wstring res;

	if (!str.empty())
	{
		_locale_t loc = _create_locale(LC_ALL, "Russian");

		for (wchar_t n : str)
		{
			res.push_back(_tolower_l(n, loc));
		}

		_free_locale(loc);
	}

	return res;
}


std::wstring replaceChars(const std::wstring &str, const std::wstring &src, const wchar_t dst)
{
	if (!str.empty() && !src.empty())
	{
		std::wstring res;

		for (wchar_t wch : str)
		{
			for (wchar_t s : src)
			{
				if (wch == s)
				{
					wch = dst;
					break;
				}
			}

			res.push_back(wch);
		}

		return res;
	}

	return str;
}


//сокращение строки до заданной длины.
//Вход: str - строка
// len - желаемая длина
// Выход: сформированая строка
std::wstring CropStr(const std::wstring &str, const size_t len)
{
	std::wstring res = trim(str);

	if (res.length() >= len)
	{
		std::wstring crop = res.substr(0, len - 1);
		crop += res.back();
		return crop;
	}

	return res;
}


/*
анализ импортируемого файла, проверка bin это или нет
вход:
a - указатель на структуру данных, которые корректируются в процессе анализа
выход: поля в структуре AnalyseFileStr:
file - открытый файл
strName - имя файла
strExt - расширение файла
nAddr - адрес загрузки
nLen - длина файла

true - данные скорректированы
false - нет, можно игнорировать
*/
bool AnalyseImportFile(AnalyseFileStruct *a)
{
	bool bRet = false;
	uint16_t mBinHeader[2];
	int nOffset = 0;

	//проверим, а не бин формат ли подсунули?
	if (sizeof(mBinHeader) == fread(mBinHeader, 1, sizeof(mBinHeader), a->file))
	{
		a->bIsCRC = false;

		//если формат действительно бин, то nOffset будет != 0,
		//но есть конечно шанс случайного совпадения
		if (mBinHeader[1] == a->nLen - 4)
		{
			nOffset = 4;
		}
		else if (mBinHeader[1] == a->nLen - 6)
		{
			a->bIsCRC = true;
			nOffset = 4;
		}
		else if (mBinHeader[1] == a->nLen - 22)
		{
			a->bIsCRC = true;
			nOffset = 20;
		}

		//если действительно бин
		if (nOffset)
		{
			//да, бин однако
			a->nAddr = mBinHeader[0];
			a->nLen = mBinHeader[1];

			if (nOffset == 20) //прочитаем оригинальное имя
			{
				fread(a->OrigName, 1, 16, a->file);
			}

			if (a->bIsCRC)
			{
				fseek(a->file, 2, SEEK_END);
				fread(&a->nCRC, 1, 2, a->file);
			}
			//тут можно ещё попробовать убрать расширение .bin
			std::wstring ext = strToLower(a->strExt);

			if (ext == L".bin")
			{
				size_t t = a->strName.rfind(L'.'); //ищем, может ещё есть расширения

				if (t != std::wstring::npos) //если расширение есть
				{
					size_t l = a->strName.length();
					a->strExt = a->strName.substr(t, l);
					a->strName = a->strName.substr(0, t);
				}
				else //если расширения нету, то просто убираем бин
				{
					a->strExt.clear();
				}

				a->strName = trim(a->strName);
				a->strExt = trim(a->strExt);
			}

			bRet = true;
		}
	}

	fseek(a->file, nOffset, SEEK_SET); //пропустить заголовок
	return bRet;
}

uint16_t CalcCRC(uint8_t *buffer, size_t len)
{
	uint32_t crc = 0;

	for (size_t i = 0; i < len; ++i)
	{
		crc += buffer[i];

		if (crc & 0xFFFF0000)
		{
			//если случился перенос в 17 разряд (т.е. бит С для word)
			crc &= 0x0000FFFF; //его обнулим
			crc++; //но прибавим к сумме
		}
	}

	return static_cast<uint16_t>(crc & 0xffff);
}

