#include "stdafx.h"
#include "resource.h"
#include "BKImage.h"
#include "BKListCtrl.h"

#include "BKFloppyImage_ANDos.h"
#include "BKFloppyImage_MKDos.h"
#include "BKFloppyImage_RT11.h"
#include "BKFloppyImage_Csidos3.h"
#include "BKFloppyImage_HCDos.h"
#include "BKFloppyImage_AODos.h"
#include "BKFloppyImage_NORD.h"
#include "BKFloppyImage_MicroDos.h"
#include "BKFloppyImage_MSDOS.h"

#include <io.h>

#pragma warning(disable:4996)

wstring g_AddOpErrorStr[] =
{
	L"Успешно.",
	L"Файл образа не открыт.",
	L"Файл слишком большой.",
	L"Операция отменена пользователем.",
	L"", //ошибку смотри в nImageErrorNumber
	L"" //на будущее
};


CBKImage::CBKImage()
	: m_pFloppyImage(nullptr)
	, m_pListCtrl(nullptr)
	, m_bCheckUseBinStatus(false)
	, m_bCheckLogExtractStatus(false)
{
}

CBKImage::~CBKImage()
{
	ClearImgVector();
}



bool CBKImage::Open(PARSE_RESULT &pr, bool bLogDisk)
{
	if (bLogDisk)
	{
		PushCurrentImg();
	}
	else
	{
		Close();
	}

	switch (pr.imageOSType)
	{
		case IMAGE_TYPE_ANDOS:
			m_pFloppyImage = new CBKFloppyImage_ANDos(pr);
			break;

		case IMAGE_TYPE_DXDOS:
			// тоже использует андосный класс. (он теперь универсальный,
			// понимает любой формат фат12 за исключением правильных каталогов)
			// как только понадобится, на его основе можно будет сделать настоящий понимальщик мсдосного формата
			m_pFloppyImage = new CBKFloppyImage_ANDos(pr); //!!! неверно, нужно исправить работу с подкаталогами
			break;

		case IMAGE_TYPE_MSDOS:
			m_pFloppyImage = new CBKFloppyImage_MSDOS(pr); //!!! Недоделано!
			break;

		case IMAGE_TYPE_MKDOS:
			m_pFloppyImage = new CBKFloppyImage_MKDos(pr);
			break;

		case IMAGE_TYPE_NCDOS:
			m_pFloppyImage = new CBKFloppyImage_HCDos(pr);
			break;

		case IMAGE_TYPE_AODOS:
			m_pFloppyImage = new CBKFloppyImage_AODos(pr);
			break;

		case IMAGE_TYPE_NORD:
			m_pFloppyImage = new CBKFloppyImage_Nord(pr);
			break;

		case IMAGE_TYPE_MIKRODOS:
			m_pFloppyImage = new CBKFloppyImage_MicroDos(pr);
			break;

		case IMAGE_TYPE_CSIDOS3:
			m_pFloppyImage = new CBKFloppyImage_Csidos3(pr);
			break;

		case IMAGE_TYPE_RT11:
			m_pFloppyImage = new CBKFloppyImage_RT11(pr);
			break;

		default:
			ASSERT(FALSE);
	}

	if (m_pFloppyImage)
	{
		ASSERT(m_pListCtrl);
		m_pListCtrl->SetSpecificColumn(m_pFloppyImage->HasSpecificData());
		m_pFloppyImage->EnableGUIControls();
		return true;
	}

	return false;
}

bool CBKImage::ReOpen()
{
	if (m_pFloppyImage)
	{
		ASSERT(m_pListCtrl);
		m_pListCtrl->SetSpecificColumn(m_pFloppyImage->HasSpecificData());
		m_pFloppyImage->EnableGUIControls();
		return true;
	}

	return false;
}


void CBKImage::Close()
{
	if (m_pFloppyImage)
	{
		m_pFloppyImage->CloseFloppyImage();
		delete m_pFloppyImage;
		m_pFloppyImage = nullptr;
	}
}

UINT CBKImage::GetImgFormatName(IMAGE_TYPES nType)
{
	if (nType == IMAGE_TYPE_UNKNOWN)
	{
		if (m_pFloppyImage)
		{
			nType = m_pFloppyImage->GetImgOSType();
		}
	}

	switch (nType)
	{
		case IMAGE_TYPE_ERROR:
			return (IDS_IMGTYPE_ERROR);

		case IMAGE_TYPE_ANDOS:
			return (IDS_IMGTYPE_ANDOS);

		case IMAGE_TYPE_MKDOS:
			return (IDS_IMGTYPE_MKDOS);

		case IMAGE_TYPE_AODOS:
			return (IDS_IMGTYPE_AODOS);

		case IMAGE_TYPE_NORD:
			return (IDS_IMGTYPE_NORD);

		case IMAGE_TYPE_MIKRODOS:
			return (IDS_IMGTYPE_MICRODOS);

		case IMAGE_TYPE_CSIDOS3:
			return (IDS_IMGTYPE_CSIDOS3);

		case IMAGE_TYPE_RT11:
			return (IDS_IMGTYPE_RT11);

		case IMAGE_TYPE_NCDOS:
			return (IDS_IMGTYPE_NCDOS);

		case IMAGE_TYPE_DXDOS:
			return (IDS_IMGTYPE_DXDOS);

		case IMAGE_TYPE_MSDOS:
			return (IDS_IMGTYPE_MSDOS);

		default:
			return (IDS_IMGTYPE_UNKNOWN);
	}
}

void CBKImage::ClearImgVector()
{
	Close();

	while (!m_vpImages.empty())
	{
		CBKFloppyImage_Prototype *bkdi = m_vpImages.back();
		m_vpImages.pop_back();

		if (bkdi)
		{
			delete bkdi;
		}
	}

	m_PaneInfo.clear();
}

void CBKImage::PushCurrentImg()
{
	m_vpImages.push_back(m_pFloppyImage);
}

bool CBKImage::PopCurrentImg()
{
	if (m_vpImages.empty())
	{
		return false;
	}

	if (m_pFloppyImage)
	{
		delete m_pFloppyImage;
	}

	m_pFloppyImage = m_vpImages.back();
	m_vpImages.pop_back();
	return true;
}

bool CBKImage::ReadCurrentDir(int nCursorPos)
{
	ASSERT(m_pListCtrl);

	if (m_pFloppyImage->ReadCurrentDir())
	{
		vector<AbstractFileRecord> *pLS = m_pFloppyImage->CurrDirectory();
		int item = 0;

		for (auto &fr : *pLS)
		{
			wstring str;
			m_pListCtrl->InsertItem(item, fr.strName.c_str());  //сюда можно добавить иконку

			if (fr.nAttr & FR_ATTR_DIR)
			{
				if (fr.nAddress)
				{
					fr.nAttr |= FR_ATTR_LINK; //добавляем, что это к тому же не просто директория, но ссылка, в некоторых ОС
					fr.nRecType = RECORD_TYPE_LINK;
				}

				//теперь выведем тип записи
				switch (fr.nRecType)
				{
					case RECORD_TYPE_UP:
						str = L"UP";
						break;

					case RECORD_TYPE_DIR:
						str = L"DIR";
						break;

					case RECORD_TYPE_LINK:
						str = L"LINK";
						break;

					default:
						str = L"???";
						break;
				}

				m_pListCtrl->SetItemText(item, CBKListCtrl::LC_TYPE_POS, str.c_str());
				str.clear();
				m_pListCtrl->SetItemText(item, CBKListCtrl::LC_BLK_SIZE_POS, str.c_str());
				m_pListCtrl->SetItemText(item, CBKListCtrl::LC_ADDRESS_POS, str.c_str());
				m_pListCtrl->SetItemText(item, CBKListCtrl::LC_SIZE_POS, str.c_str());
			}
			else
			{
				//теперь выведем тип записи
				switch (fr.nRecType)
				{
					case RECORD_TYPE_LOGDSK:
						str = L"LOG";
						break;

					case RECORD_TYPE_FILE:
						str = L"";
						break;

					default:
						str = L"???";
						break;
				}

				m_pListCtrl->SetItemText(item, CBKListCtrl::LC_TYPE_POS, str.c_str());
				wchar_t buff[32];
				_snwprintf(buff, 32, L"%d\0", fr.nBlkSize);
				m_pListCtrl->SetItemText(item, CBKListCtrl::LC_BLK_SIZE_POS, buff);
				_snwprintf(buff, 32, L"%o\0", fr.nAddress);
				m_pListCtrl->SetItemText(item, CBKListCtrl::LC_ADDRESS_POS, buff);
				_snwprintf(buff, 32, L"%o\0", fr.nSize);
				m_pListCtrl->SetItemText(item, CBKListCtrl::LC_SIZE_POS, buff);
			}

			//выводим атрибуты
			str.clear();

			if (fr.nAttr & FR_ATTR_DELETED)
			{
				str += L"x";
			}

			if (fr.nAttr & FR_ATTR_BAD)
			{
				str += L"B";
			}

			if (fr.nAttr & FR_ATTR_DIR)
			{
				str += L"D";
			}

			if (fr.nAttr & FR_ATTR_LOGDISK)
			{
				str += L"d";
			}

			if (fr.nAttr & FR_ATTR_LINK)
			{
				str += L"L";
			}

			if (fr.nAttr & FR_ATTR_READONLY)
			{
				str += L"R";
			}

			if (fr.nAttr & FR_ATTR_HIDDEN)
			{
				str += L"H";
			}

			if (fr.nAttr & FR_ATTR_PROTECTED)
			{
				str += L"P";
			}

			if (fr.nAttr & FR_ATTR_TEMPORARY)
			{
				str += L"T";
			}

			m_pListCtrl->SetItemText(item, CBKListCtrl::LC_ATTR_POS, str.c_str());
			AbstractFileRecord *nfr = std::addressof(fr);

			if (m_pFloppyImage->HasSpecificData())
			{
				if (fr.nRecType == RECORD_TYPE_UP)
				{
					str.clear();
				}
				else
				{
					str = m_pFloppyImage->GetSpecificData(nfr);
				}

				m_pListCtrl->SetItemText(item, CBKListCtrl::LC_SPECIFIC_POS, str.c_str());
			}

			m_pListCtrl->SetItemData(item, reinterpret_cast<DWORD_PTR>(nfr));
		}

		m_pListCtrl->SortItems(CBKListCtrl::MyCompareProc, reinterpret_cast<DWORD_PTR>(m_pListCtrl));
		m_pListCtrl->SetFocus();
		m_pListCtrl->SetItemState(nCursorPos, LVIS_FOCUSED | LVIS_SELECTED, LVIS_FOCUSED | LVIS_SELECTED);
		m_pListCtrl->SetHotItem(nCursorPos);

		//и убедимся, чтобы выделенный элемент всегда был на виду
		if (!m_pListCtrl->EnsureVisible(nCursorPos, FALSE))
		{
			CRect r;
			m_pListCtrl->GetItemRect(nCursorPos, &r, LVIR_BOUNDS);
			m_pListCtrl->Scroll(CSize(0, r.Height()));
		}

		OutCurrFilePath();
		AfxGetMainWnd()->SendMessage(WM_OUT_CURR_IMG_INFO, 0, reinterpret_cast<LPARAM>(&m_pFloppyImage->GetImageInfo()));
	}
	else
	{
		AfxGetMainWnd()->SendMessage(WM_SEND_ERRORNUM, WPARAM(0), LPARAM(m_pFloppyImage->GetErrorNumber()));
	}

	return false;
}

void CBKImage::ItemProcessing(int nItem, AbstractFileRecord *fr)
{
	if (nItem < 0)
	{
		return;
	}

	m_PaneInfo.nCurItem = nItem;

	if (fr->nAttr & FR_ATTR_DIR)
	{
		if (fr->nRecType == RECORD_TYPE_UP)
		{
			//выходим из подкаталога
			if (!StepUptoDir(fr)) //если после выхода текущая директория стала -1, то это выход ТОЛЬКО из образа
			{
				//.. на корневой директории вызывает закрытие текущего открытого образа
				if (S_OK != AfxGetMainWnd()->SendMessage(WM_OUT_OF_IMAGE, WPARAM(0), LPARAM(0)))
				{
					return;
				}
			}
		}
		else
		{
			//заходим в директорию
			StepIntoDir(fr);
		}

		m_pListCtrl->DeleteAllItems();
		ReadCurrentDir(m_PaneInfo.nCurItem);
	}
	else if (fr->nAttr & FR_ATTR_LOGDISK)
	{
		StepIntoDir(fr); //нужно, чтобы имя логдиска отображалось в пути, при входе в него.
		AfxGetMainWnd()->SendMessage(WM_PUT_INTO_LOG, WPARAM(m_pFloppyImage->GetBaseOffset() + fr->nStartBlock * BLOCK_SIZE), LPARAM(fr->nSize));
	}
	else
	{
		//кликнули на файле, передаём запись файла в просмотрщик.
		if (m_pFloppyImage->GetImgOSType() == IMAGE_TYPE_RT11)
		{
			ViewFileRT11(fr);
		}
		else
		{
			ViewFile(fr); //проверка, файл это или нет, делается внутри.
		}
	}
}

void CBKImage::ViewSelected(bool bViewAsText)
{
	size_t uSelectedCount = m_pListCtrl->GetSelectedCount();
	int nItem = -1;

	if (uSelectedCount > 0)
	{
		//если что-то выделенное есть
		for (size_t i = 0; i < uSelectedCount; ++i)
		{
			nItem = m_pListCtrl->GetNextItem(nItem, LVNI_SELECTED);
			auto *fr = reinterpret_cast<AbstractFileRecord *>(m_pListCtrl->GetItemData(nItem));

			if (bViewAsText)
			{
				if (m_pFloppyImage->GetImgOSType() == IMAGE_TYPE_RT11)
				{
					ViewFileRT11(fr);
				}
				else
				{
					ViewFile(fr); //проверка, файл это или нет, делается внутри.
				}
			}
			else
			{
				ViewFileAsSprite(fr); //проверка, файл это или нет, делается внутри.
			}
		}
	}
}

void CBKImage::StepIntoDir(AbstractFileRecord *fr)
{
	if (m_pFloppyImage->ChangeDir(fr))
	{
		m_vSelItems.push_back(m_PaneInfo); //сохраняем текущее
		//заполняем новое.
		wstring name = fr->strName;
		replaceChar(&name, L'/', L'_'); //меняем '/' на '_', чтобы не путалась.
		m_PaneInfo.strCurrPath += name + L"/";
		m_PaneInfo.nCurDir = fr->nDirNum;
		m_PaneInfo.nParentDir = fr->nDirBelong;
		m_PaneInfo.nCurItem = 0;
		OutCurrFilePath();
	}
	else
	{
		AfxGetMainWnd()->SendMessage(WM_SEND_ERRORNUM, WPARAM(0), LPARAM(m_pFloppyImage->GetErrorNumber()));
	}
}

//выход: true - обычно
//		false - выход из лог диска или образа

bool CBKImage::StepUptoDir(AbstractFileRecord *fr)
{
	bool bRet = true;

	if (m_pFloppyImage->ChangeDir(fr))
	{
		if (m_pFloppyImage->GetCurrDirNum() == -1)
		{
			bRet = false;
		}

		//различие выхода из образа и из лог.диска - не пустой вектор.
		//если вектор пуст - то выход из образа, иначе - выход из лог.диска
		if (!m_vSelItems.empty())
		{
			m_PaneInfo = m_vSelItems.back();
			m_vSelItems.pop_back();
		}
		else
		{
			m_PaneInfo.clear();
			m_PaneInfo.nCurDir = -1;
		}

		OutCurrFilePath();
	}
	else
	{
		AfxGetMainWnd()->SendMessage(WM_SEND_ERRORNUM, WPARAM(0), LPARAM(m_pFloppyImage->GetErrorNumber()));
	}

	return bRet;
}

void CBKImage::OutCurrFilePath()
{
	AfxGetMainWnd()->SendMessage(WM_OUT_CURR_FILE_PATH, 0, reinterpret_cast<LPARAM>(&m_PaneInfo.strCurrPath));
}

void CBKImage::ExtractSelected(const wstring &strOutPath)
{
	size_t uSelectedCount = m_pListCtrl->GetSelectedCount();
	int nItem = -1;

	if (uSelectedCount > 0)
	{
		//если что-то выделенное есть
		m_strStorePath = strOutPath;

		for (size_t i = 0; i < uSelectedCount; ++i)
		{
			nItem = m_pListCtrl->GetNextItem(nItem, LVNI_SELECTED);
			auto *fr = reinterpret_cast<AbstractFileRecord *>(m_pListCtrl->GetItemData(nItem));

			if (fr->nRecType == RECORD_TYPE_UP)
			{
				//если встретился выход в родительскую директорию, то
				//его проигнорируем.
			}
			else
			{
				if (!ExtractObject(fr)) //тут либо директория. либо файл. директорию надо рекурсивно извлечь целиком
				{
					AfxGetMainWnd()->SendMessage(WM_SEND_ERRORNUM, WPARAM(0), LPARAM(m_pFloppyImage->GetErrorNumber()));
				}
			}
		}
	}
}

void CBKImage::RenameRecord(AbstractFileRecord *fr)
{
	//1. конвертируем абстрактную запись в реальную. только имя
	//2. Изменяем соответствующую запись в каталоге
	m_pFloppyImage->RenameRecord(fr);
	//3. Перечитываем каталог заново.
	int nItem = m_pListCtrl->GetNextItem(-1, LVIS_FOCUSED);

	if (nItem < 0)
	{
		nItem = 0;
	}

	m_pListCtrl->DeleteAllItems();
	ReadCurrentDir(nItem);
	m_pListCtrl->SetFocus();
}

void CBKImage::DeleteSelected()
{
	size_t uSelectedCount = m_pListCtrl->GetSelectedCount();
	int nItem = -1;

	if (uSelectedCount > 0)
	{
		//если что-то выделенное есть
		for (size_t i = 0; i < uSelectedCount; ++i)
		{
			nItem = m_pListCtrl->GetNextItem(nItem, LVNI_SELECTED);
			auto *fr = reinterpret_cast<AbstractFileRecord *>(m_pListCtrl->GetItemData(nItem));

			if (fr->nRecType == RECORD_TYPE_UP)
			{
				//если встретился выход в родительскую директорию, то
				//его проигнорируем.
			}
			else
			{
				ADDOP_RESULT res = DeleteObject(fr); //тут либо директория. либо файл. директорию надо рекурсивно удалить целиком после подтверждения

				if (res.bFatal)
				{
l_fatal:            //если фатальная - то просто выведем сообщение.

					if (res.nError == ADD_ERROR_IMGERR)
					{
						AfxGetMainWnd()->SendMessage(WM_SEND_ERRORNUM, WPARAM(MB_ICONERROR), LPARAM(res.nImageErrorNumber));
					}
					else
					{
						AfxGetMainWnd()->SendMessage(WM_SEND_ERRORNUM, WPARAM(MB_ICONERROR), LPARAM(0x10000 | res.nError));
					}

					break;
				}
				else if (res.nError != ADD_ERROR_OK)
				{
					if (fr->nRecType == RECORD_TYPE_DIR)
					{
						//обрабатываем ситуацию с директорией
						if (res.nImageErrorNumber == IMAGE_ERROR_FS_DIR_NOT_EMPTY) //если удалить не получается потому что не пустая
						{
							LRESULT definite = AfxGetMainWnd()->SendMessage(WM_SEND_MESSAGEBOX, WPARAM(MB_YESNO | MB_ICONINFORMATION), reinterpret_cast<LPARAM>(_T("Директория не пуста. Удалить рекурсивно всё её содержимое?")));

							switch (definite)
							{
								case IDYES:
									DeleteRecursive(fr); //удалять всё!
									break;

								default:
								case IDNO:
									break;
							}
						}
						else
						{
							goto l_fatal;
						}
					}
					else
					{
						//обрабатываем ситуацию с файлом
						if (res.nImageErrorNumber == IMAGE_ERROR_FS_FILE_PROTECTED) //если удалить не получается из-за атрибутов
						{
							LRESULT definite = AfxGetMainWnd()->SendMessage(WM_SEND_MESSAGEBOX, WPARAM(MB_YESNO | MB_ICONINFORMATION), reinterpret_cast<LPARAM>(_T("Файл защищён. Всё равно удалить?")));

							switch (definite)
							{
								case IDYES:
								{
									ADDOP_RESULT res3 = DeleteObject(fr, true);  //удаляем с форсированием

									if (res3.nError != ADD_ERROR_OK) //если всё равно не удалилось
									{
										goto l_fatal;
									}

									break;
								}

								default:
								case IDNO:
									break;
							}
						}
						else
						{
							goto l_fatal;
						}
					}
				}
			}
		}

		nItem = m_pListCtrl->GetNextItem(-1, LVIS_FOCUSED);

		if (nItem < 0)
		{
			nItem = 0;
		}

		m_pListCtrl->DeleteAllItems();
		ReadCurrentDir(nItem);
		m_pListCtrl->SetFocus();
	}
}
//рекурсивно удаляем директорию и всё её содержимое
//на входе - запись удаляемой директории
bool CBKImage::DeleteRecursive(AbstractFileRecord *fr)
{
	bool bRet = true;

	if (fr->nAttr & FR_ATTR_DIR)
	{
		AbstractFileRecord efr = *fr; //запись для выхода из директории
		m_pFloppyImage->ChangeDir(fr);
		//если директория, надо в неё зайти и получить список всех записей.
		AbstractFileRecord cur_fr;
		bool bEnd = m_pFloppyImage->GetStartFileName(&cur_fr);

		if (bEnd)
		{
			do
			{
				if (cur_fr.strName != L"..")
				{
					bRet = DeleteRecursive(&cur_fr); //обрабатываем все записи

					if (!bRet)
					{
						break;
					}
				}
			}
			while (bEnd = m_pFloppyImage->GetNextFileName(&cur_fr));
		}
		//тут надо выйти из директории.
		efr.nDirNum = fr->nDirBelong;
		efr.nRecType = RECORD_TYPE_UP;
		efr.strName = L"..";
		m_pFloppyImage->ChangeDir(&efr);
		bRet = m_pFloppyImage->DeleteDir(fr); //и под конец удаляем и саму директорию
	}
	else
	{
		bRet = m_pFloppyImage->DeleteFile(fr, true); //если файл - удаляем с форсированием
	}

	return bRet;
}

void CBKImage::SetStorePath(const wstring &str)
{
	m_strStorePath = str;
	trimRight(&m_strStorePath, L'\\');
	m_strStorePath += L'\\';
}

bool CBKImage::ExtractObject(AbstractFileRecord *fr)
{
	bool bRet = true;

	//если /*плохой или удалённый или*/ ссылка - ничего не делаем
	if (fr->nAttr & FR_ATTR_LINK)
	{
		return true;
	}

	if (fr->nAttr & FR_ATTR_DIR)
	{
		//если директория, надо в неё зайти и получить список всех записей.
		AbstractFileRecord efr = *fr; //запись для выхода из директории
		m_pFloppyImage->ChangeDir(fr);
		AbstractFileRecord cur_fr;
		wstring tmpStorePath = m_strStorePath;
		wstring strDirName = fr->strName;
		SetSafeDir(strDirName);
		SetStorePath(m_strStorePath + strDirName);

		if (_tmkdir(m_strStorePath.c_str()))
		{
			AfxGetMainWnd()->SendMessage(WM_SEND_ERRORNUM, WPARAM(0), LPARAM(IMAGE_ERROR_CANNOT_CREATE_DIR));
			//bRet = false;
		}

		bool bEnd = m_pFloppyImage->GetStartFileName(&cur_fr);

		if (bEnd)
		{
			do
			{
				if (cur_fr.strName != L"..")
				{
					bRet = ExtractObject(&cur_fr);

					if (!bRet)
					{
						break;
					}
				}
			}
			while (bEnd = m_pFloppyImage->GetNextFileName(&cur_fr));
		}
		//тут надо выйти из директории.
		efr.nDirNum = fr->nDirBelong;
		efr.nRecType = RECORD_TYPE_UP;
		efr.strName = L"..";
		m_pFloppyImage->ChangeDir(&efr);
		m_strStorePath = tmpStorePath;
	}
	else
	{
		//если файл
		bRet = ExtractFile(fr);
	}

	return bRet;
}

//процедура извлечения файла
bool CBKImage::ExtractFile(AbstractFileRecord *fr)
{
	static wstring strBinExt = L".bin";
	IMAGE_ERROR_NUMS nErrorNumber = IMAGE_ERROR_OK;
	bool bRet = true;
	uint16_t BinHeader[2];

	//если /*плохой или удалённый или*/ директория - ничего не делаем
	if (fr->nAttr & FR_ATTR_DIR)
	{
		return bRet;
	}

	AnalyseFileStruct AFS;
	AFS.nAddr = fr->nAddress;
	AFS.nLen = fr->nSize;
	AFS.strName = fr->strName;
	SetSafeName(AFS.strName);
	m_pFloppyImage->OnExtract(fr, AFS.strName);

	if (m_bCheckUseBinStatus)
	{
		size_t p = AFS.strName.rfind(L'.'); //посмотрим, есть ли расширение

		if (p == wstring::npos) //если расширения нет
		{
			AFS.strName += strBinExt; //то просто добавим.
		}
		else //если расширение есть
		{
			wstring ext = AFS.strName.substr(p);
			strToLower(&ext); //сделаем мелкие буквы

			if (ext != strBinExt) //если расширение уже и так бин, то не добавляем
			{
				AFS.strName += strBinExt; //а если какое-то другое - добавим.
			}
		}
	}

	if ((AFS.file = _tfopen((m_strStorePath + AFS.strName).c_str(), _T("w+b"))) != nullptr)
	{
		int nLen = fr->nSize;
		auto *pBuffer = new uint8_t[m_pFloppyImage->EvenSizeByBlock(nLen)];

		if (pBuffer)
		{
			bRet = m_pFloppyImage->ReadFile(fr, pBuffer);

			if (bRet)
			{
				if (m_bCheckUseBinStatus)
				{
					//стандартный заголовок бин файла - два слова.
					//с адресом всё ясно, а вот длина больших файлов урезается
					BinHeader[0] = uint16_t(fr->nAddress);
					BinHeader[1] = uint16_t(nLen);
					fwrite(BinHeader, 1, sizeof(BinHeader), AFS.file);
				}

				size_t ret = fwrite(pBuffer, 1, fr->nSize, AFS.file);
				fflush(AFS.file);

				if (m_bCheckLogExtractStatus)
				{
					bRet = AnalyseExportFile(&AFS);

					if (!bRet)
					{
						nErrorNumber = IMAGE_ERROR_CANNOT_READ_FILE;
					}
				}
			}
			else
			{
				nErrorNumber = m_pFloppyImage->GetErrorNumber();
			}

			delete[] pBuffer;
		}
		else
		{
			nErrorNumber = IMAGE_ERROR_NOT_ENOUGHT_MEMORY;
			bRet = false;
		}

		fclose(AFS.file);
	}
	else
	{
		nErrorNumber = IMAGE_ERROR_FILE_CANNOT_CREATE;
		bRet = false;
	}

	if (!bRet)
	{
		AfxGetMainWnd()->SendMessage(WM_SEND_ERRORNUM, WPARAM(0), LPARAM(nErrorNumber));
	}

	return bRet;
}

/*
анализ экспортируемого файла
вход:
    m_bCheckUseBinStatus - флаг, что добавляем как бин
поля в структуре AnalyseFileStruct:
    file - указатель на открытый ранее файл
    strName - имя файла
    nAddr - адрес загрузки
    nLen - длина файла
чтобы не писать отдельные функции для каждой ОС, анализировать будем уже извлечённый файл.
выход: true - успешно
       false - ошибка
*/
bool CBKImage::AnalyseExportFile(AnalyseFileStruct *a)
{
	bool bRet = true;
	IMAGE_ERROR_NUMS nErrorNumber = IMAGE_ERROR_OK;
	int nStartAddr = a->nAddr; //в идеале вот так, адрес запуска == адресу загрузки
	struct autorun
	{
		WORD nWord770;
		WORD nWord772;
		WORD nWord774;
	};
	autorun sAuto = { 0, 0, 0 };

	if (0300 <= a->nAddr && a->nAddr < 01000)  //если есть блок автозапуска, или что-то похожее
	{
		if ((a->nAddr <= 0770) && (a->nAddr + a->nLen >= 0776)) //если файл перекрывает область, откуда брать адрес запуска
			//ведь это может быть просто кусок с адресом загрузки 0400 и длиной 0200
		{
			int nOffset = 0770 - a->nAddr; //адрес запуска будем брать из ячейки 0774 (хотя возможно надо из другой)

			//если содержимое ячеек 770, 772 и 774 равно между собой
			if (a->nAddr & 1)
			{
				nOffset++;    //на всякий случай, если адрес загрузки нечётный, скорректируем
			}

			if (m_bCheckUseBinStatus)
			{
				nOffset += 4;
			}

			if (0 == fseek(a->file, nOffset, SEEK_SET))
			{
				if (sizeof(sAuto) == fread(&sAuto, 1, sizeof(sAuto), a->file))
				{
					if ((sAuto.nWord770 == sAuto.nWord772) && (sAuto.nWord770 == sAuto.nWord774))
					{
						nStartAddr = int(sAuto.nWord774);
					}
				}
				else
				{
					bRet = false;
				}
			}
			else
			{
				bRet = false;
			}
		}
	}

	if (a->nAddr < 0100)
	{
		nStartAddr = 0;
	}

	FILE *f = _wfopen((m_strStorePath + L"extractlog.txt").c_str(), L"at");

	if (f)
	{
		fwprintf(f, L"%-20s\tload:%06o\tlen:%06o\tstart:%06o\n", a->strName.c_str(), a->nAddr, a->nLen, nStartAddr);
		fclose(f);
	}

	return bRet;
}



//возвращаем коды состояния и ошибок вызывающей функции. Она должна заниматься обработкой
//разных возникающих ситуаций
//сюда принимаем структуру sendfiledata и делаем из неё AbstractFileRecord
//bExistDir - флаг, когда мы пытаемся создать уже существующую директорию, - игнорировать ошибку
ADDOP_RESULT CBKImage::AddObject(struct sendfiledata &f_file, bool bExistDir)
{
	ADDOP_RESULT ret;

	if (m_pFloppyImage == nullptr)
	{
		//файл образа не открыт
		ret.bFatal = true;
		ret.nError = ADD_ERROR_IMAGE_NOT_OPEN;
		return ret;
	}

	AbstractFileRecord AFR;
	TCHAR pName[_MAX_FNAME] = { 0 };
	TCHAR pExt[_MAX_EXT] = { 0 };
	_tsplitpath_s(f_file.findFile.name, nullptr, 0, nullptr, 0, pName, _MAX_FNAME, pExt, _MAX_EXT);
	AFR.strName = wstring(pName) + wstring(pExt);

	if (f_file.findFile.attrib & _A_RDONLY)
	{
		AFR.nAttr |= FR_ATTR_READONLY;
	}

	if (f_file.findFile.attrib & _A_HIDDEN)
	{
		AFR.nAttr |= FR_ATTR_HIDDEN;
	}

	if (f_file.findFile.attrib & _A_SYSTEM)
	{
		AFR.nAttr |= FR_ATTR_PROTECTED;
	}

	AFR.timeCreation = f_file.findFile.time_create;

	//мы не можем сформировать данные:
	//nDirBelong - потому, что это внутренние данные образа
	//nBlkSize - потому, что это данные, зависящие от формата образа
	//nStartBlock - потому, что эти данные ещё неизвестны.

	if (f_file.findFile.attrib & _A_SUBDIR)
	{
		AFR.nAttr |= FR_ATTR_DIR;
		AFR.nRecType = RECORD_TYPE_DIR;

		if (m_pFloppyImage->CreateDir(&AFR) || bExistDir)
		{
			if (!m_pFloppyImage->ChangeDir(&AFR))
			{
				//ошибка изменения директории:
				//IMAGE_ERROR_FS_NOT_SUPPORT_DIRS
				//IMAGE_ERROR_IS_NOT_DIR
				ret.nImageErrorNumber = m_pFloppyImage->GetErrorNumber();
				//обрабатывать теперь
				ret.nError = ADD_ERROR_IMGERR;

				switch (ret.nImageErrorNumber)
				{
					case IMAGE_ERROR_FS_NOT_SUPPORT_DIRS:
						ret.bFatal = false;
						//ret.nError = ADD_ERROR_OK; //игнорируем данные ошибки
						//ret.nImageErrorNumber = IMAGE_ERROR_OK;
						break;

					case IMAGE_ERROR_IS_NOT_DIR:
					default:
						ret.bFatal = true;
						break;
				}
			}
		}
		else
		{
			//ошибка создания директории:
			//IMAGE_ERROR_FS_NOT_SUPPORT_DIRS - два варианта: игнорировать, остановиться
			//IMAGE_ERROR_CANNOT_WRITE_FILE
			//IMAGE_ERROR_FS_DIR_EXIST - такая директория уже существует, два варианта: игнорировать, остановиться
			ret.nImageErrorNumber = m_pFloppyImage->GetErrorNumber();
			//обрабатывать теперь
			ret.nError = ADD_ERROR_IMGERR;

			switch (ret.nImageErrorNumber)
			{
				case IMAGE_ERROR_FS_NOT_SUPPORT_DIRS:
				case IMAGE_ERROR_FS_DIR_EXIST:
					ret.bFatal = false;
					//ret.nError = ADD_ERROR_OK; //игнорируем данные ошибки
					//ret.nImageErrorNumber = IMAGE_ERROR_OK;
					break;

				default:
					ret.bFatal = true;
					break;
			}
		}
	}
	else
	{
		AFR.nRecType = RECORD_TYPE_FILE;
		AFR.nSize = f_file.findFile.size;

		if (f_file.findFile.size > m_pFloppyImage->GetImageFreeSpace())
		{
			//файл слишком большой
			ret.bFatal = true;
			ret.nError = ADD_ERROR_FILE_TOO_LARGE;
		}
		else
		{
			auto *pBuffer = new uint8_t[m_pFloppyImage->EvenSizeByBlock(f_file.findFile.size)];

			if (pBuffer)
			{
				memset(pBuffer, 0, f_file.findFile.size);
				AnalyseFileStruct AFS;

				if ((AFS.file = _tfopen(f_file.strName.c_str(), _T("rb"))) != nullptr)
				{
					AFS.strName = wstring(pName);
					AFS.strExt = wstring(pExt);
					AFS.nOffset = 0;
					AFS.nAddr = 01000;
					AFS.nLen = f_file.findFile.size;

					if (AnalyseImportFile(&AFS))
					{
						AFR.nAddress = AFS.nAddr;
						AFR.nSize = AFS.nLen;
						AFR.strName = AFS.strName + AFS.strExt;
					}

					fread(pBuffer, 1, AFR.nSize, AFS.file);
					fclose(AFS.file);

					if (!m_pFloppyImage->WriteFile(&AFR, pBuffer))
					{
						//ошибка записи файла:
						//IMAGE_ERROR_CANNOT_WRITE_FILE
						//IMAGE_ERROR_FS_DISK_FULL
						//IMAGE_ERROR_FS_FILE_EXIST - Файл существует. два варианта: остановиться, удалить старый и перезаписать новый файл
						//IMAGE_ERROR_FS_STRUCT_ERR
						//IMAGE_ERROR_FS_CAT_FULL
						//и прочие ошибки позиционирования и записи
						ret.nImageErrorNumber = m_pFloppyImage->GetErrorNumber();
						//обрабатывать теперь
						ret.nError = ADD_ERROR_IMGERR;

						if (ret.nImageErrorNumber == IMAGE_ERROR_FS_FILE_EXIST)
						{
							ret.bFatal = false;
						}
						else
						{
							ret.bFatal = true;
						}
					}
				}

				delete[] pBuffer;
			}
			else
			{
				//недостаточно памяти
				ret.bFatal = true;
				ret.nError = ADD_ERROR_IMGERR;
				ret.nImageErrorNumber = IMAGE_ERROR_NOT_ENOUGHT_MEMORY;
			}
		}
	}

	ret.afr = AFR;
	return ret;
}
//нужна функция выхода из директории, когда добавляется новая директория.
void CBKImage::OutFromDirObject(AbstractFileRecord *fr)
{
	fr->strName = L"..";
	fr->nRecType = RECORD_TYPE_UP;
	fr->nDirNum = fr->nDirBelong;
	m_pFloppyImage->ChangeDir(fr);
}


ADDOP_RESULT CBKImage::DeleteObject(AbstractFileRecord *fr, bool bForce)
{
	ADDOP_RESULT ret;

	if (m_pFloppyImage == nullptr)
	{
		//файл образа не открыт
		ret.bFatal = true;
		ret.nError = ADD_ERROR_IMAGE_NOT_OPEN;
		return ret;
	}

	if (fr->nAttr & FR_ATTR_DIR)
	{
		bool bRes = (bForce) ? DeleteRecursive(fr) : m_pFloppyImage->DeleteDir(fr);

		if (!bRes)
		{
			ret.nError = ADD_ERROR_IMGERR;
			ret.nImageErrorNumber = m_pFloppyImage->GetErrorNumber();
			ret.bFatal = (ret.nImageErrorNumber == IMAGE_ERROR_FS_DIR_NOT_EMPTY) ? false : true;
			ret.afr = *fr;
		}
	}
	else
	{
		if (!m_pFloppyImage->DeleteFile(fr, bForce))
		{
			ret.nError = ADD_ERROR_IMGERR;
			ret.nImageErrorNumber = m_pFloppyImage->GetErrorNumber();
			ret.bFatal = (ret.nImageErrorNumber == IMAGE_ERROR_FS_FILE_PROTECTED) ? false : true;
			ret.afr = *fr;
		}
	}

	return ret;
}

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

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

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

		//если действительно бин
		if (a->nOffset)
		{
			//да, бин однако
			a->nAddr = mBinHeader[0];
			a->nLen = mBinHeader[1];
			//тут можно ещё попробовать убрать расширение .bin
			wstring ext = a->strExt;
			strToLower(&ext);

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

				if (t != 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();
				}

				trim(&a->strName);
			}

			bRet = true;
		}
	}

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


