#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_sParseImgResult(image)
	, m_nLastErrorNumber(IMAGE_ERROR::OK_NOERRORS)
	, m_bFileROMode(false)
	, m_nCatPos(0)
	, m_nSeekOffset(0)
	, m_nBlockSize(BLOCK_SIZE)
{
	ResetFloppyImage();
}


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::ResetFloppyImage()
{
	m_sDiskCat.nFreeRecs = 0;
	m_sDiskCat.nFreeBlocks = 0;

	switch (m_sParseImgResult.imageOSType)
	{
		case IMAGE_TYPE::ERROR_NOIMAGE:
		case IMAGE_TYPE::UNKNOWN:
		case IMAGE_TYPE::MIKRODOS:
		case IMAGE_TYPE::NCDOS:
		case IMAGE_TYPE::RT11:
			m_sDiskCat.bHasDir = false;
			break;

		default: m_sDiskCat.bHasDir = true;
	}

	m_sDiskCat.nCurrDirNum = 0;
	m_sDiskCat.vecDir.clear();
	return OpenFloppyImage();
}

const std::wstring CBKFloppyImage_Prototype::GetImageInfo()
{
	CString str, strf;
	strf.LoadString(IDS_INFO_FREE_BLK);
	unsigned int bytes = unsigned int(m_sDiskCat.nFreeBlocks) * m_nBlockSize;
	unsigned int totalbytes = unsigned int(m_sDiskCat.nTotalBlocks) * m_nBlockSize;
	str.Format(strf, unsigned int(m_sDiskCat.nFreeRecs), tblStrRec[GetWordEndIdx(unsigned int(m_sDiskCat.nFreeRecs))].c_str(),
	           unsigned int(m_sDiskCat.nTotalRecs),
	           unsigned int(m_sDiskCat.nFreeBlocks), tblStrBlk[GetWordEndIdx(unsigned int(m_sDiskCat.nFreeBlocks))].c_str(),
	           bytes, tblStrBlk[GetWordEndIdx(bytes)].c_str(),
	           unsigned int(m_sDiskCat.nTotalBlocks), totalbytes);
	return std::wstring(str.GetString());
}

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

//создаём фейковую запись о выходе из каталога.
void CBKFloppyImage_Prototype::MakeFakeOutDirRecord()
{
	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::SeekToBlock(size_t nBlockNumber)
{
	long nOffs = m_sParseImgResult.nBaseOffset + (long)nBlockNumber * m_nBlockSize;
	m_nSeekOffset = nOffs / BLOCK_SIZE;
	return true;
}

bool CBKFloppyImage_Prototype::ReadData(void *ptr, size_t length)
{
	auto nBlkLen = int(EvenSizeByBlock(length)); //размер проги выровняем по границе сектора

	if (m_pFoppyImgFile.ReadLBA(ptr, m_nSeekOffset, nBlkLen / BLOCK_SIZE))
	{
		m_nSeekOffset += nBlkLen / BLOCK_SIZE;
		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 = int(EvenSizeByBlock(length)); //размер проги выровняем по границе сектора

	if (m_pFoppyImgFile.WriteLBA(ptr, m_nSeekOffset, nBlkLen / BLOCK_SIZE))
	{
		m_nSeekOffset += nBlkLen / BLOCK_SIZE;
		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_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;
	m_sDiskCat.nMaxDirNum = 0;

	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; //инициализация - именно поэтому надо сперва эту функцию вызывать, а потом другую.
	//если это не корень, надо вывести ..
	//в корне тоже будем выводить .. - оно будет вызывать закрытие текущего образа.
	pFR->clear();
	pFR->strName = L"..";
	pFR->nAttr = FR_ATTR_DIR;
	pFR->nRecType = AbstractRecordType::UP;
	pFR->nDirBelong = 0;
	pFR->nStartBlock = m_sParseImgResult.nBaseOffset;
	pFR->nDirNum = m_sDiskCat.nCurrDirNum;
	return true;
}

bool CBKFloppyImage_Prototype::GetNextFileName(AbstractFileRecord *pFR)
{
	pFR->clear();
	auto size = static_cast<int>(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;
}
