#include "stdafx.h"
#include "BKFloppyImage_Prototype.h"

#pragma warning(disable:4996)

CBKFloppyImage_Prototype::CBKFloppyImage_Prototype(const PARSE_RESULT &image)
	: m_pKoi8tbl(koi8tbl10)
	, m_bMakeAdd(false)
	, m_bMakeDel(false)
	, m_bMakeRename(false)
	, m_bChangeAddr(false)
	, m_sParseImgResult(image)
	, m_nLastErrorNumber(IMAGE_ERROR::OK_NOERRORS)
	, m_bFileROMode(false)
	, m_nCatPos(0)
	, m_nSeekOffset(0)
	, m_nBlockSize(BLOCK_SIZE)
{
	ReopenFloppyImage();
}


CBKFloppyImage_Prototype::~CBKFloppyImage_Prototype()
{
	CloseFloppyImage();
}

bool CBKFloppyImage_Prototype::OpenFloppyImage()
{
	m_nLastErrorNumber = IMAGE_ERROR::OK_NOERRORS;
	CloseFloppyImage();

	if (m_pFoppyImgFile.Open(m_sParseImgResult.strName.c_str(), true))
	{
		m_bFileROMode = false;
		return true;
	}
	else if (m_pFoppyImgFile.Open(m_sParseImgResult.strName.c_str(), false))
	{
		m_bFileROMode = true;
		return true;
	}

	m_nLastErrorNumber = IMAGE_ERROR::IMAGE_CANNOT_OPEN;
	return false;
}

void CBKFloppyImage_Prototype::CloseFloppyImage()
{
	m_pFoppyImgFile.Close();
}

bool CBKFloppyImage_Prototype::ReopenFloppyImage()
{
	m_sDiskCat.bHasDir = false;
	m_sDiskCat.bTrueDir = false;
	m_sDiskCat.nMaxDirNum = 255;
	OnReopenFloppyImage();
	m_sDiskCat.nFreeRecs = 0;
	m_sDiskCat.nFreeBlocks = 0;
	m_sDiskCat.nCurrDirNum = 0;
	m_sDiskCat.vecDir.clear();
	return OpenFloppyImage();
}

const std::wstring CBKFloppyImage_Prototype::GetImageInfo()
{
	// никак от CString не избавиться. всё равно компиляторо и платформозависимый код в этом месте
	auto ptr = CString(MAKEINTRESOURCE(IDS_INFO_FREE_BLK)); // вот так получаем строковый ресурс из ресурсов
	std::wstring strf(ptr);
	register auto freeblocks = static_cast<UINT>(m_sDiskCat.nFreeBlocks);
	register UINT bytes = freeblocks * m_nBlockSize;
	register auto totalblocks = static_cast<UINT>(m_sDiskCat.nTotalBlocks);
	register UINT totalbytes = totalblocks * m_nBlockSize;
	register auto freerecs = static_cast<UINT>(m_sDiskCat.nFreeRecs);
	wchar_t strb[256];
	memset(strb, 0, 256);
	_snwprintf(strb, 255, strf.c_str(), freerecs, tblStrRec[GetWordEndIdx(freerecs)].c_str(),
	           static_cast<UINT>(m_sDiskCat.nTotalRecs),
	           freeblocks, tblStrBlk[GetWordEndIdx(freeblocks)].c_str(),
	           bytes, tblStrBlk[GetWordEndIdx(bytes)].c_str(),
	           totalblocks, totalbytes);
	return std::wstring(strb);
}

const size_t CBKFloppyImage_Prototype::GetImageFreeSpace()
{
	return static_cast<size_t>(m_sDiskCat.nFreeBlocks) * m_nBlockSize;
}

// создаём фейковую запись о выходе из каталога.
void CBKFloppyImage_Prototype::MakeFakeOutDirRecord()
{
	// если у нас настоящие директории, то формировать запись .. не нужно.
	// но для корневой директории при этом, всё равно нужно.
	if (!m_sDiskCat.bTrueDir || m_sDiskCat.nCurrDirNum == 0)
	{
		AbstractFileRecord afr;
		afr.strName = L"..";
		afr.nAttr = FR_ATTR::DIR;
		afr.nRecType = AbstractRecordType::UP;
		afr.nDirBelong = m_sDiskCat.nCurrDirNum;
		afr.nStartBlock = m_sParseImgResult.nBaseOffset;

		if (m_sDiskCat.vecDir.empty())
		{
			afr.nDirNum = -1;
		}
		else
		{
			afr.nDirNum = m_sDiskCat.vecDir.back();
		}

		m_sDiskCat.vecFC.push_back(afr);
	}
}


bool CBKFloppyImage_Prototype::AppendDirNum(uint8_t nNum)
{
	bool bRet = false;

	if (m_sDiskCat.arDirNums[nNum] == 0)
	{
		m_sDiskCat.arDirNums[nNum] = 1;
		bRet = true;
	}

	// иначе - такой номер уже есть
	return bRet;
}

uint8_t CBKFloppyImage_Prototype::AssignNewDirNum()
{
	for (uint8_t i = 1; i < m_sDiskCat.nMaxDirNum; ++i)
	{
		if (m_sDiskCat.arDirNums[i] == 0)
		{
			m_sDiskCat.arDirNums[i] = 1;
			return i;
		}
	}

	return 0;
}

bool CBKFloppyImage_Prototype::SeekToBlock(size_t nBlockNumber)
{
	unsigned long nOffs = m_sParseImgResult.nBaseOffset + static_cast<unsigned long>(nBlockNumber) * m_nBlockSize;
	m_nSeekOffset = nOffs / BLOCK_SIZE;
	return true;
}

bool CBKFloppyImage_Prototype::ReadData(void *ptr, size_t length)
{
	// размер проги выровняем по границе сектора и сделаем размер в блоках
	auto nBlkLen = static_cast<UINT>(EvenSizeByBlock(length) / BLOCK_SIZE);

	if (m_pFoppyImgFile.ReadLBA(ptr, m_nSeekOffset, nBlkLen))
	{
		m_nSeekOffset += nBlkLen;
		return true;
	}

	m_nLastErrorNumber = IMAGE_ERROR::IMAGE_CANNOT_READ;
	return false;
}

bool CBKFloppyImage_Prototype::SeektoBlkReadData(size_t nBlockNumber, void *ptr, size_t length)
{
	if (SeekToBlock(nBlockNumber))
	{
		return ReadData(ptr, length);
	}

	return false;
}

bool CBKFloppyImage_Prototype::WriteData(void *ptr, size_t length)
{
	// размер проги выровняем по границе сектора и сделаем размер в блоках
	auto nBlkLen = static_cast<UINT>(EvenSizeByBlock(length) / BLOCK_SIZE);

	if (m_pFoppyImgFile.WriteLBA(ptr, m_nSeekOffset, nBlkLen))
	{
		m_nSeekOffset += nBlkLen;
		return true;
	}

	m_nLastErrorNumber = IMAGE_ERROR::IMAGE_CANNOT_WRITE;
	return false;
}

bool CBKFloppyImage_Prototype::SeektoBlkWriteData(size_t nBlockNumber, void *ptr, size_t length)
{
	if (SeekToBlock(nBlockNumber))
	{
		return WriteData(ptr, length);
	}

	return false;
}

void CBKFloppyImage_Prototype::EnableGUIControls()
{
	// извлекать всегда можно
	AfxGetMainWnd()->SendMessage(WM_SEND_ENABLE_BUTTON2, WPARAM(IDC_BUTTON_EXTRACT), LPARAM(TRUE));
	// просматривать всегда можно
	AfxGetMainWnd()->SendMessage(WM_SEND_ENABLE_BUTTON2, WPARAM(IDC_BUTTON_VIEW), LPARAM(TRUE));
	AfxGetMainWnd()->SendMessage(WM_SEND_ENABLE_BUTTON2, WPARAM(IDC_BUTTON_VIEW_AS_SPR), LPARAM(TRUE));
	// адрес менять не всегда можно
	AfxGetMainWnd()->SendMessage(WM_SEND_ENABLE_BUTTON2, WPARAM(ID_CONTEXT_CHGADDR), LPARAM(m_bChangeAddr));
	// остальные функции - по мере реализации.
	AfxGetMainWnd()->SendMessage(WM_SEND_ENABLE_BUTTON2, WPARAM(IDC_BUTTON_ADD),    LPARAM(m_bMakeAdd));
	AfxGetMainWnd()->SendMessage(WM_SEND_ENABLE_BUTTON2, WPARAM(IDC_BUTTON_DELETE), LPARAM(m_bMakeDel));
	AfxGetMainWnd()->SendMessage(WM_SEND_ENABLE_BUTTON2, WPARAM(IDC_BUTTON_RENAME), LPARAM(m_bMakeRename));
}

// виртуальная функция, для каждой ФС - своя реализация.
bool CBKFloppyImage_Prototype::ReadCurrentDir()
{
	m_nLastErrorNumber = IMAGE_ERROR::OK_NOERRORS;
	m_sDiskCat.vecFC.clear();
	m_sDiskCat.nTotalRecs = m_sDiskCat.nTotalBlocks = m_sDiskCat.nFreeRecs = m_sDiskCat.nFreeBlocks = -1;
	memset(&m_sDiskCat.arDirNums[0], 0, 256);

	if (m_pFoppyImgFile.IsFileOpen())
	{
		MakeFakeOutDirRecord();
		return true;
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR::IMAGE_NOT_OPEN;
		return false;
	}
}

bool CBKFloppyImage_Prototype::WriteCurrentDir()
{
	m_nLastErrorNumber = IMAGE_ERROR::OK_NOERRORS;

	if (m_pFoppyImgFile.IsFileOpen())
	{
		return true;
	}
	else
	{
		m_nLastErrorNumber = IMAGE_ERROR::IMAGE_NOT_OPEN;
		return false;
	}
}

bool CBKFloppyImage_Prototype::ChangeDir(AbstractFileRecord *pFR)
{
	ConvertAbstractToRealRecord(pFR);
	m_nLastErrorNumber = IMAGE_ERROR::OK_NOERRORS;

	if (pFR->nAttr & FR_ATTR::DIR)
	{
		switch (pFR->nRecType)
		{
			case AbstractRecordType::UP:
			{
				// выход из директории или логдиска
				m_sDiskCat.nCurrDirNum = pFR->nDirNum; // если тут будет -1, то надо выгрузить образ.

				if (!m_sDiskCat.vecDir.empty())
				{
					m_sDiskCat.vecDir.pop_back();
				}

				return true;
			}

			case AbstractRecordType::DIR:
			{
				// заход в директорию
				if (VerifyDir(pFR))
				{
					m_sDiskCat.vecDir.push_back(m_sDiskCat.nCurrDirNum);
					m_sDiskCat.nCurrDirNum = pFR->nDirNum;
					return true;
				}
				else
				{
					m_nLastErrorNumber = IMAGE_ERROR::FS_DIR_NOT_EXIST;
					return false;
				}
			}
		}
	}

	m_nLastErrorNumber = IMAGE_ERROR::FS_IS_NOT_DIR;
	return false;
}

bool CBKFloppyImage_Prototype::VerifyDir(AbstractFileRecord *pFR)
{
	m_nLastErrorNumber = IMAGE_ERROR::FS_NOT_SUPPORT_DIRS;
	return false;
}

bool CBKFloppyImage_Prototype::CreateDir(AbstractFileRecord *pFR)
{
	m_nLastErrorNumber = IMAGE_ERROR::FS_NOT_SUPPORT_DIRS;
	return false;
}

bool CBKFloppyImage_Prototype::DeleteDir(AbstractFileRecord *pFR)
{
	m_nLastErrorNumber = IMAGE_ERROR::FS_NOT_SUPPORT_DIRS;
	return false;
}

bool CBKFloppyImage_Prototype::GetStartFileName(AbstractFileRecord *pFR)
{
	m_vecPC.push_back(m_nCatPos); // сохраним предыдущую позицию
	m_nCatPos = 0; // инициализация - именно поэтому надо сперва эту функцию вызывать, а потом другую.
	return GetNextFileName(pFR);
}

bool CBKFloppyImage_Prototype::GetNextFileName(AbstractFileRecord *pFR)
{
	pFR->clear();
	auto size = static_cast<UINT>(m_sDiskCat.vecFC.size());

	// и выводим всё что найдём. но по одному.
	while (m_nCatPos < size)
	{
		if (m_sDiskCat.vecFC.at(m_nCatPos).nDirBelong == m_sDiskCat.nCurrDirNum)
		{
			*pFR = m_sDiskCat.vecFC.at(m_nCatPos);
			m_nCatPos++;
			return true;
		}

		m_nCatPos++;
	}

	if (!m_vecPC.empty())
	{
		m_nCatPos = m_vecPC.back();
		m_vecPC.pop_back();
	}

	return false;
}
