/*************************************************
*           BK Emulator v3.8 for Windows         *
*************************************************/

/*
File: TapeUnit.cpp
Version: 1.0.0
Written by: Yuriy Kalmykov <kalmykov@stoik.com>
    Copyright (c) 2002-2004 Yuriy Kalmykov

    BK Emulator is a program emulated hardware environment for running
code for BK 0010(01) in different configurations.

    This code may be used in compiled form in any way you desire.
This file or it's parts can't be redistributed without the authors
written consent, but can be modified for your private needs.
    Providing that this notice and the authors name and all copyright
notices remains intact.

    Please, an email me to know how you are using it and where. You can
ask me for any information about this below code or any attendant
knowledge.

    This file is provided "as is" with no expressed or implied warranty.
The author accepts no liability for any damage or loss of business that
this product may cause.
*/


// TapeUnit.cpp: implementation of the CTapeUnit class.
//


#include "stdafx.h"
#include "Globals.h"
#include "Tape.h"
#include "MSFManager.h"
#include "TapeUnit.h"

//////////////////////////////////////////////////////////////////////
// Construction/Destruction


CTapeUnit::CTapeUnit() :
	m_nType(TYPE_NONE),
	m_nAddress(UNKNOWN),
	m_nLength(UNKNOWN),
	m_nMSTime(UNKNOWN),
	m_nWaveLength(UNKNOWN),
	m_bCRC(false)
{
}

CTapeUnit::CTapeUnit(CTapeUnit &tapeUnit)
{
	m_nType = tapeUnit.m_nType;
	m_nAddress = tapeUnit.m_nAddress;
	m_nLength = tapeUnit.m_nLength;
	m_nMSTime = tapeUnit.m_nMSTime;
	m_nWaveLength = tapeUnit.m_nWaveLength;
	m_bCRC = tapeUnit.m_bCRC;
	m_strName = tapeUnit.m_strName;
	m_strPath = tapeUnit.m_strPath;
}

CTapeUnit::~CTapeUnit()
{
}


CTapeUnit &CTapeUnit::operator= (CTapeUnit &tapeUnit)
{
	m_nType = tapeUnit.m_nType;
	m_nAddress = tapeUnit.m_nAddress;
	m_nLength = tapeUnit.m_nLength;
	m_nMSTime = tapeUnit.m_nMSTime;
	m_nWaveLength = tapeUnit.m_nWaveLength;
	m_bCRC = tapeUnit.m_bCRC;
	m_strName = tapeUnit.m_strName;
	m_strPath = tapeUnit.m_strPath;
	return *this;
}


bool CTapeUnit::SetFile(CString &strPath)
{
	bool bRet = false;
	CFileStatus fs;

	//получим информацию о файле - таким образом проверяется существует он или нет
	if (CFile::GetStatus(strPath, fs)) //если true - то файл существует.
	{
		// теперь определим тип файла
		m_strName = _T("");
		m_bCRC = false;
		bRet = true;

		if (IsFileWave(strPath))
		{
			// File is PCM Wave
			m_nType = TYPE_WAV;
		}
		else if (IsFileMSF(strPath))
		{
			// File is MSF
			m_nType = TYPE_MSF;
		}
		else if (IsFileBin(strPath))
		{
			// File is BIN
			m_nType = TYPE_BIN;
			m_bCRC = true;
		}
		else
		{
			bRet = false;
		}

		if (bRet)
		{
			m_nAddress = UNKNOWN;
			m_nLength = UNKNOWN;
			m_nMSTime = UNKNOWN;
			m_nWaveLength = UNKNOWN;
			m_strPath = strPath;
		}
	}

	return bRet;
}


bool CTapeUnit::SetTmpFile(CString &strPath)
{
	// Check file status
	CFileStatus fs;

	if (!CFile::GetStatus(strPath, fs))
	{
		return false;
	}

	// All checks done. Define file type
	// File is TMP captured Wave
	m_nType = TYPE_TMP;
	m_nAddress = UNKNOWN;
	m_nLength = UNKNOWN;
	m_nMSTime = UNKNOWN;
	m_nWaveLength = UNKNOWN;
	m_bCRC = false;
	m_strName = _T("");
	m_strPath = strPath;
	return true;
}


bool CTapeUnit::IsFileWave(CString &strPath)
{
	CFile waveFile;

	if (!waveFile.Open(strPath, CFile::modeRead))
	{
		return false;
	}

	WaveHeader waveHeader;

	if (waveFile.Read(&waveHeader, sizeof(WaveHeader)) != sizeof(WaveHeader))
	{
		return false;
	}

	if (waveHeader.riffTag != RIFF_TAG || waveHeader.fmtTag != FMT_TAG)
	{
		return false;
	}

	WAVEFORMATEX wfx;

	if (waveFile.Read(&wfx, waveHeader.fmtSize) != waveHeader.fmtSize)
	{
		return false;
	}

	wfx.cbSize = 0;
//  if (wfx.nAvgBytesPerSec != 44100 || wfx.nBlockAlign != 1
//          || wfx.nChannels != 1 || wfx.nSamplesPerSec != 44100
//          || wfx.wBitsPerSample != 8 || wfx.wFormatTag != WAVE_FORMAT_PCM)
//  {
//      return false;
//  }
	DataHeader dataHeader;

	for (;;)
	{
		if (waveFile.Read(&dataHeader, sizeof(DataHeader)) != sizeof(DataHeader))
		{
			return false;
		}

		if (dataHeader.dataTag == DATA_TAG)
		{
			break;
		}

		waveFile.Seek(dataHeader.dataSize, SEEK_CUR);
	}

	return true;
}


bool CTapeUnit::IsFileMSF(CString &strPath)
{
	CMSFManager msf;

	if (!msf.CheckFile(strPath))
	{
		return false;
	}

	if (!msf.OpenFile(strPath, true))
	{
		return false;
	}

	MSF_BLOCK_INFO bi;

	if (!msf.FindBlock(MSF_BLOCKTYPE_WAVE, &bi))
	{
		return false;
	}

	return true;
}


bool CTapeUnit::IsFileBin(CString &strPath)
{
	CFileStatus fs;

	if (!CFile::GetStatus(strPath, fs))
	{
		return false;
	}

	CFile binFile;

	if (!binFile.Open(strPath, CFile::modeRead))
	{
		return false;
	}

	WORD length;

	//читаем первое слово - это адрес, его надо пропустить
	if (binFile.Read(&length, sizeof(length)) != sizeof(length))
	{
		return false;
	}

	//читаем второе слово - это длина
	if (binFile.Read(&length, sizeof(length)) != sizeof(length))
	{
		return false;
	}

	if (length != fs.m_size - 4) //если не простой бин
	{
		if (length != fs.m_size - 22) //и не сложный
		{
			return false;
		}
	}

	return true;
}


bool CTapeUnit::RetrieveInfo()
{
	bool bRet = false;
	auto *pTape = new CTape;

	if (pTape)
	{
		TAPE_FILE_INFO tfi;

		switch (m_nType)
		{
			case TYPE_BIN:
				// Open binary file
				bRet = pTape->LoadBinFile(m_strPath, &tfi);
				break;

			case TYPE_WAV:
				// Open wave file
				bRet = pTape->LoadWaveFile(m_strPath);
				break;

			case TYPE_MSF:
				// Open MSF file
				bRet = pTape->LoadMSFFile(m_strPath);
				break;

			case TYPE_TMP:
				// Open TMP file
				bRet = pTape->LoadTmpFile(m_strPath);
				break;
		}

		if (bRet)
		{
			pTape->GetWaveFile(&tfi, false);

			if (tfi.marker2 == -1) //файл повреждён как-то
			{
				bRet = false; //у файла нет конца
			}
			else
			{
				// да - получим полную информацию о нём
				m_nAddress = tfi.address;
				m_nLength = tfi.length;
				m_nWaveLength = pTape->GetWaveLength();
				m_nMSTime = (int)(m_nWaveLength / (double(g_Config.m_nSoundSampleRate) / 1000.0));
				m_bCRC = pTape->CalcCRC(&tfi, true);
				m_strName = BKToUNICODE(tfi.name, 16);
				m_strName.Trim();
			}
		}

		delete pTape;
	}

	return bRet;
}


bool CTapeUnit::SaveAs(CString &strPath, int type)
{
	g_Config.NormalizePath(strPath);
	CString strNewPath = strPath + m_strName;
	CString strName = g_Config.GetFileName(m_strName);
	CString strExt = g_Config.GetFileExt(m_strName);
	CString strTypeExt;
	CFileStatus fs;
	TAPE_FILE_INFO tfi;
	bool bRet = true;

	// Добавим расширение для нового типа
	switch (type)
	{
		case TYPE_BIN:
		{
			// Добавим расширение .bin
			strTypeExt.LoadString(IDS_FILEEXT_BINARY);
			strNewPath = strPath + strName;

			if (strExt.IsEmpty() || strExt.CollateNoCase(strTypeExt))
			{
				strNewPath += strTypeExt;
			}
		}
		break;

		case TYPE_WAV:
		{
			// Добавим расширение .wav
			strTypeExt.LoadString(IDS_FILEEXT_WAVE);
			strNewPath = strPath + strName + strTypeExt;
		}
		break;

		case TYPE_MSF:
		{
			// Добавим расширение .tap
			strTypeExt.LoadString(IDS_FILEEXT_TAPE);
			strNewPath = strPath + strName + strTypeExt;
		}
		break;
	}

	// Проверим, а не существует ли уже такой файл?
	if (CFile::GetStatus(strNewPath, fs))
	{
		return false; // если существует - выйдем с ошибкой
		//!!! а вдруг не надо? вдруг надо перезаписать?
		//TODO: мессагебокс с вопросом чё делать? и механизм перезаписи
	}

	auto *ptape = new CTape;

	if (ptape)
	{
		switch (m_nType)
		{
			case TYPE_BIN: // Если текущий тип файла - bin
				if (type == TYPE_BIN)
				{
					// если и новый тоже bin - просто копируем файл
					bRet = (CopyFile(m_strPath, strNewPath, FALSE) == TRUE);
				}
				else
				{
					// сконвертируем bin в wave
					bRet = ptape->LoadBinFile(m_strPath, &tfi);
				}

				break;

			case TYPE_WAV: // Если текущий тип файла - wav
				if (type == TYPE_WAV)
				{
					// если и новый тоже wav - просто копируем файл
					bRet = (CopyFile(m_strPath, strNewPath, FALSE) == TRUE);
				}
				else
				{
					//загрузим wav файл
					bRet = ptape->LoadWaveFile(m_strPath);
				}

				break;

			case TYPE_MSF: // Если текущий тип файла - tap
				if (type == TYPE_MSF)
				{
					// если и новый тоже tap - просто копируем файл
					bRet = (CopyFile(m_strPath, strNewPath, FALSE) == TRUE);
				}
				else
				{
					// сконвертируем tap в wave
					bRet = ptape->LoadMSFFile(m_strPath);
				}

				break;

			case TYPE_TMP: // если текущий файл - временный
				bRet = ptape->LoadTmpFile(m_strPath);
				break;

			default: // Какой-то неизвестный тип.
				ASSERT(FALSE);
				bRet = false;
		}

		if (bRet)
		{
			// конвертируем в заданный формат
			switch (type)
			{
				case TYPE_BIN:
					// сохраним загруженный/сконвертированный wav как bin
					bRet = ptape->GetWaveFile(&tfi, false);

					if (bRet)
					{
						bRet = ptape->SaveBinFile(strNewPath, &tfi);
					}

					break;

				case TYPE_WAV:
					// сохраним загруженный/сконвертированный wav
					bRet = ptape->SaveWaveFile(strNewPath);
					break;

				case TYPE_MSF:
					// сохраним загруженный/сконвертированный wav как tap
					bRet = ptape->SaveMSFFile(strNewPath);
					break;
			}
		}

		delete ptape;
	}

	return bRet;
}
