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

#include "stdafx.h"
#include "BKbin2obj.h"
#include "getopt.h"
#include "LabelManager.h"
#include "Globals.h"
#include "Object.h"

#include <locale>
#pragma warning(disable:4996)

UMemory g_Memory;
CReader *g_pReader = nullptr;
wstring strType, strLabel, strInFileName, strOutFileName;
/*
опции:
bColor - чб / цветная картинка
bTransparency - прозрачность
*/
bool bColor = false, bTransparency = false, bEven = false;
int nEvenBound = 0;

int wmain(int argc, wchar_t *argv[], wchar_t *envp[])
{
	_wsetlocale(LC_ALL, L"Russian");
	InitGlobalParameters(&g_GlobalParameters);
	static struct option long_options[] =
	{
		{ L"help",         ARG_NONE, nullptr, L'?' },
		{ L"source",       ARG_REQ,  nullptr, L's' },
		{ L"even",         ARG_OPT,  nullptr, L'e' },
		{ L"label",        ARG_REQ,  nullptr, L'l' },
		{ L"color",        ARG_NONE, nullptr, L'c' },
		{ L"transparency", ARG_NONE, nullptr, L't' },
		{ nullptr,         ARG_NULL, nullptr, ARG_NULL }
	};
	static wchar_t optstring[] = L"?s:e::l:ct";
	bool bShowHelp = false;
	int option_index = 0;
	int c;

	while ((c = getopt_long(argc, argv, optstring, long_options, &option_index)) != -1)
	{
		// Handle options
		c = tolower(c);

		switch (c)
		{
			case L'?':
				bShowHelp = true;
				break;

			case L's':
				if (optarg)
				{
					strType = wstring(optarg);
					strType = strType.substr(0, 3);
					strType[0] = toupper(strType[0]);
					strType[1] = toupper(strType[1]);
					strType[2] = toupper(strType[2]);
				}

				break;

			case L'e':
				bEven = true;

				if (optarg)
				{
					int nTmp = _tcstol(optarg, nullptr, 8) & 0xffff;

					if (0 <= nTmp && nTmp <= 0100000)
					{
						nEvenBound = nTmp;
					}
				}

				break;

			case L'l':
				bEven = true;

				if (optarg)
				{
					strLabel = wstring(optarg);

					if (!strLabel.empty())
					{
						for (wchar_t &n : strLabel)
						{
							n = toupper(n);
						}
					}
				}

				break;

			case L'c':
				bColor = true;
				break;

			case L't':
				bTransparency = true;
				break;
		}

		if (bShowHelp)
		{
			Usage();
			return 0;
		}
	}

	argc -= optind;
	argv += optind;

	if (argc < 1)
	{
		wprintf(L"Ошибка в командной строке: не задано имя входного файла.\n\n");
		Usage();
	}
	else
	{
		strInFileName = wstring(*argv);
		argc--;
		argv++;

		if (argc >= 1)
		{
			strOutFileName = wstring(*argv);
		}
		else
		{
			strOutFileName = strInFileName;
		}

		if (strLabel.empty())
		{
			//если имя метки не было задано
			wchar_t pName[_MAX_FNAME] = { 0 };
			_wsplitpath_s(strInFileName.c_str(), nullptr, 0, nullptr, 0, pName, _MAX_FNAME, nullptr, 0);
			//сделаем имя метки по умолчанию
			strLabel = wstring(pName);
		}

		g_GlobalParameters.nModeCompone = -1;

		if (strType == L"BIN")
		{
			ConvertBin();
		}
		else if (strType == L"IMG")
		{
			ConvertImage();
		}
	}

	return 0;
}

void ConvertImage()
{
	CImage img;

	if (SUCCEEDED(img.Load(strInFileName.c_str())))
	{
		int w = img.GetWidth();
		int h = img.GetHeight();
		//посчитаем размер БКшного ресурса при таких размерах картинки.
		int size = 0;

		if (bColor)
		{
			//для цветной картинки.
			//1 пиксел - 2 бита
			size = ((w + 3) / 4) * h * (bTransparency ? 2 : 1);
		}
		else
		{
			//для чёрно-белой картинки.
			//1 пиксел - 1 бит
			size = ((w + 7) / 8) * h * (bTransparency ? 2 : 1);
		}

		if (size < 65536)
		{
			if (size >= 32768)
			{
				wprintf(L"Предупреждение! Размер ресурса больше половины доступной памяти БК.\n");
			}

			//конвертируем
			if (bColor)
			{
				convertBitmap2(&g_Memory.b[BASE_ADDRESS], &img, bTransparency);
			}
			else
			{
				convertBitmap1(&g_Memory.b[BASE_ADDRESS], &img, bTransparency);
			}

			CBKToken token(strLabel); //добавляем метку
			g_labelGlobalDefs.AddLabel(&token, BASE_ADDRESS, DEFINITE_LABEL);

			if (bEven)
			{
				if (nEvenBound)
				{
					//выравнивание по произвольной границе степени 2
					size = size ? ((size - 1) | (nEvenBound - 1)) + 1 : 0;
				}
				else
				{
					//выравниваем по чётному адресу
					size++;
					size &= ~1;
				}
			}

			g_GlobalParameters.nProgramLength = size;
			MakeObj(strOutFileName, wstring(L".obj"));
		}
		else
		{
			wprintf(L"Размер ресурса слишком велик.\n");
		}

		img.Destroy();
	}
	else
	{
		wprintf(L"Ошибка загрузки файла изображения %s\n", strInFileName.c_str());
	}
}


//преобразовываем обычный массив бинарных данных
void ConvertBin()
{
	FILE *inf = _wfopen(strInFileName.c_str(), L"rb");

	if (inf)
	{
		//узнаем размер файла
		fseek(inf, 0, SEEK_END);
		long size = ftell(inf);
		fseek(inf, 0, SEEK_SET);

		if (size < 65536)
		{
			if (size >= 32768)
			{
				wprintf(L"Предупреждение! Размер ресурса больше половины доступной памяти БК.\n");
			}

			fread(&g_Memory.b[BASE_ADDRESS], 1, size, inf); //читаем файл
			CBKToken token(strLabel); //добавляем метку
			g_labelGlobalDefs.AddLabel(&token, BASE_ADDRESS, DEFINITE_LABEL);

			if (bEven)
			{
				if (nEvenBound)
				{
					//выравнивание по произвольной границе степени 2
					size = size ? ((size - 1) | (nEvenBound - 1)) + 1 : 0;
				}
				else
				{
					//выравниваем по чётному адресу
					size++;
					size &= ~1;
				}
			}

			g_GlobalParameters.nProgramLength = size;
			MakeObj(strOutFileName, wstring(L".obj"));
		}
		else
		{
			wprintf(L"Размер ресурса слишком велик.\n");
		}

		fclose(inf);
	}
	else
	{
		wprintf(L"Ошибка открытия файла %s\n", strInFileName.c_str());
	}
}

//функции из pdp11asm vinxru
//это цветная картинка
void convertBitmap2(uint8_t *destp, CImage *img, bool t)
{
	int w = img->GetWidth();
	int h = img->GetHeight();
	int destbpl = (w + 3) / 4 * (t ? 2 : 1);

	for (int y = 0; y < h; ++y)
	{
		for (int x = 0; x < w; ++x)
		{
			COLORREF сolor = img->GetPixel(x, y);
			//ваще какая-то непонятная шняга.
			bool tr = (сolor == 0xFF00FF);

//          if (сolor == 0)
//          {
//              сolor = 0;
//          }
//          else if (сolor == 0x0000FF)
//          {
//              сolor = 1;
//          }
//          else if (сolor == 0x00FF00)
//          {
//              сolor = 2;
//          }
//          else
//          {
//              сolor = 3;
//          }

			//теперь определим, какой цвет у пикселя
			if ((сolor & 0xff0000) && !(сolor & 0x00f8f8)) //если в синем канале что-то есть, а в остальных почти нету
			{
				сolor = 1; //то это синий цвет
			}
			else if ((сolor & 0x00ff00) && !(сolor & 0xf800f8)) //если в зелёном канале что-то есть, а в остальных почти нету
			{
				сolor = 2; //то это зелёный цвет
			}
			else if ((сolor & 0x0000ff) && !(сolor & 0xf8f800)) //если в красном канале что-то есть, а в остальных почти нету
			{
				сolor = 3; //то это красный цвет
			}
			else
			{
				//все остальные комбинации - чёрный цвет. нефиг тут.
				сolor = 0; //чёрный
			}

			if (t)
			{
				if (!tr)
				{
					if (w >= 16)
					{
						destp[x / 4 + x / 4 / 2 * 2 + 2 + y * destbpl] |= сolor << ((x & 3) * 2);
						destp[x / 4 + x / 4 / 2 * 2 + y * destbpl] |= 3 << ((x & 3) * 2);
					}
					else
					{
						destp[x / 4 * 2 + 1 + y * destbpl] |= сolor << ((x & 3) * 2);
						destp[x / 4 * 2 + y * destbpl] |= 3 << ((x & 3) * 2);
					}
				}
			}
			else
			{
				destp[x / 4 + y * destbpl] |= (сolor << ((x & 3) * 2));
			}
		}
	}
}

//-----------------------------------------------------------------------------
//это чёрно белая картинка
void convertBitmap1(uint8_t *destp, CImage *img, bool t)
{
	int w = img->GetWidth();
	int h = img->GetHeight();
	size_t destbpl = (w + 7) / 8 * (t ? 2 : 1);

	for (int y = 0; y < h; ++y)
	{
		for (int x = 0; x < w; ++x)
		{
			COLORREF c = img->GetPixel(x, y);
			bool tr = (c == 0xFF00FF);

			if (c == 0)
			{
				c = 0;
			}
			else
			{
				c = 1;
			}

			if (t)
			{
				if (!tr)
				{
					if (w >= 16)
					{
						if (c)
						{
							destp[x / 8 + x / 8 / 2 * 2 + 2 + y * destbpl] |= 1 << (x & 7);
						}

						destp[x / 8 + x / 8 / 2 * 2 + y * destbpl] |= 1 << (x & 7);
					}
					else
					{
						if (c)
						{
							destp[x / 8 * 2 + 1 + y * destbpl] |= 1 << (x & 7);
						}

						destp[x / 8 * 2 + y * destbpl] |= 1 << (x & 7);
					}
				}
			}
			else
			{
				if (c)
				{
					destp[x / 8 + y * destbpl] |= c << (x & 7);
				}
			}
		}
	}
}

void Usage()
{
	wprintf(L"Конвертер бинарных объектов в объектные модули кросс ассемблера Turbo8.\n" \
	        L"(с) 2014-2017 gid\n\n" \
	        L"Использование:\n" \
	        L"BKbin2obj -? (--help)\n" \
	        L"  Вывод этой справки.\n\n" \
	        L"BKbin2obj [-s<type>][-c][-t][-l<label_name>] <input_file_name> [output_file_name]\n" \
	        L"  -s<source> (--source <source>) - тип входного объекта.\n" \
	        L"    Возможные типы:\n" \
	        L"    bin - просто бинарный массив;\n" \
	        L"    img - картинка в формате BMP, GIF, JPEG, PNG и TIFF.\n\n" \
	        L"  -e[0bound] (--even [0bound]) - выравнивание массива данных по границе блока.\n" \
	        L"    Если параметр не задан - делается выравнивание по границе слова.\n" \
	        L"    Параметр - число в восьмеричном виде. Предполагается, что число - степень\n" \
	        L"    двойки. Если задать произвольное число, результат будет совсем не тем, что\n" \
	        L"    ожидался.\n\n" \
	        L"  -l<label_name> (--label <label_name>) - задать имя метки. Если имя метки не\n" \
	        L"    задано, оно формируется из имени входного файла.\n\n" \
	        L"  Ключи, действующие только при выборе типа img:\n" \
	        L"  -c (--color) - обрабатывать картинку как цветное изображение, иначе - чёрно-\n" \
	        L"    белое.\n\n" \
	        L"  -t (--transparency) - использование прозрачности.\n" \
	        L"    Алгоритмы преобразования взяты из проекта pdp11asm vinxru, один к одному,\n" \
	        L"    я даже не разбирался как они работают.\n\n" \
	        L"  input_file_name - входной файл.\n" \
	        L"  output_file_name - необязательное имя выходного файла, если нужно задать\n" \
	        L"  объектному файлу имя, отличное от входного.\n");
}

/*
конвертор всяких бинарных объектов в объектные файлы, чтобы их прилинковывать к
ассемблерным объектникам.
*/
