///////////////////////////////////////////////////////////////////////////////
//  CFile.cpp

#include <algorithm>
#include <string>
#include <vector>
#include "../../Specific/Debug/LMsg.h"
#include "CFile.h"
#include "CFilePath.h"

namespace filesystem {

CFile::CFile(void) :
m_hFile(nullptr),
m_dwPos(0),
m_dwSize(0)
{
}

CFile::~CFile()
{
	Close();
}

bool CFile::Create(const std::string& FileName)
{
	Close();

	const std::string RealFileName = TransRealFileName(FileName);

#ifdef WIN32
	if (fopen_s(&m_hFile, RealFileName.c_str(), "wb") != 0) return false;
#else
	m_hFile = fopen(RealFileName.c_str(), "wb");
#endif

	if (!m_hFile) return false;

	m_dwPos = 0;
	m_dwSize = 0xffffffff;

	return true;
}

bool CFile::Open(const std::string& FileName)
{
	Close();

	const std::string RealFileName = TransRealFileName(FileName);

#ifdef WIN32
	if (fopen_s(&m_hFile, RealFileName.c_str(), "rb") != 0) return false;
	if (!m_hFile) return false;
#else
	std::string TransFileName = FileName;
	std::replace(TransFileName.begin(), TransFileName.end(), '\\', '/');

	m_hFile = fopen(TransFileName.c_str(), "rb");
	if (!m_hFile) {
		return false;
	}
#endif

	if (fseek(m_hFile, 0, SEEK_END) != 0) return false;

	auto FileSize = ftell(m_hFile);

	if (fseek(m_hFile, 0, SEEK_SET) != 0) return false;

	m_dwPos = 0;

	m_dwSize = (static_cast<unsigned int>(FileSize) & 0xffffffff);

	return true;
}

bool CFile::Close(void)
{
	if (!m_hFile) return true;

	if (fclose(m_hFile) != 0) return false;

	m_hFile = nullptr;

	return true;
}

bool CFile::StepWritePos(int nSize)
{
	std::vector<unsigned char> vecbyData;
	vecbyData.resize(nSize);
	std::fill(vecbyData.begin(), vecbyData.end(), static_cast<unsigned char>(0));

	return Write(&vecbyData[0], nSize);
}

bool CFile::WritePosDWORD(int nPos, unsigned int dwData)
{
	unsigned int dwPrevPos = GetPos();

	if (!SetPos(static_cast<unsigned int>(nPos))) return false;
	if (!Write(&dwData, sizeof(unsigned int))) return false;

	if (!SetPos(dwPrevPos)) return false;

	return true;
}

bool CFile::Write(const void* pvData, int nSize)
{
	if (!m_hFile) return false;

	if (fwrite(pvData, sizeof(unsigned char), nSize, m_hFile) != static_cast<size_t>(nSize)) return false;

	m_dwPos += nSize;

	return true;
}

bool CFile::Read(void* pvData, int nSize)
{
	if (!m_hFile) return false;

	if (m_dwPos + nSize > m_dwSize) {
		return false;
	}

	if (fread(pvData, sizeof(unsigned char), nSize, m_hFile) != static_cast<size_t>(nSize)) return false;

	m_dwPos += nSize;

	return true;
}

bool CFile::SetPos(unsigned int dwPos)
{
	if (!m_hFile) return false;

	if (dwPos >= m_dwSize) return false;

	if (fseek(m_hFile, dwPos, SEEK_SET) != 0) return false;

	m_dwPos = dwPos;

	return true;
}

bool CFile::WriteString(const char* pszText, bool bWriteNul)
{
	int nLength = (int)strlen(pszText);
	if (nLength > 0) {
		if (!Write(pszText, nLength)) return false;
	}

	if (bWriteNul) {
		if (!WriteByte(0)) return false;
	}

	return true;
}

bool CFile::ReadString(std::string& szText)
{
	szText.clear();

	if (m_dwPos >= m_dwSize) return false;

	const unsigned int dwStartPos = m_dwPos;
	unsigned int dwReadSize = 0;

	std::vector<char> veccText;

	for (;;) {
		if (m_dwPos >= m_dwSize) {
			veccText.push_back('\0');
			szText = &veccText[0];
			return true;
		}

		std::vector<char> veccBuf((std::min)(128U, m_dwSize - m_dwPos));

		if (!Read(&veccBuf[0], (int)veccBuf.size())) return false;

		int nIndex;
		for (nIndex = 0; nIndex < (int)veccBuf.size(); nIndex++) {
			if (veccBuf[nIndex] == '\0') {
				// I—¹
				if (!SetPos(dwStartPos + dwReadSize + static_cast<unsigned int>(nIndex) + 1)) return false;

				veccText.push_back('\0');
				szText = &veccText[0];

				return true;
			}
			else {
				veccText.push_back(veccBuf[nIndex]);
			}
		}

		dwReadSize += static_cast<unsigned int>(veccBuf.size());
	}
}

bool CFile::ReadImage(std::vector<unsigned char>& Data, const std::string& FileName)
{
	CFile File;
	if (!File.Open(FileName.c_str())) return false;

	Data.resize(File.GetSize());

	if (!Data.empty()) {
		if (!File.Read(&Data[0], static_cast<int>(Data.size()))) return false;
	}

	if (!File.Close()) return false;

	return true;
}

bool CFile::ReadTextImage(std::string& Text, const std::string& FileName)
{
	std::vector<unsigned char> Data;
	if (!ReadImage(Data, FileName)) return false;
	if (Data.empty()) return true;

	Data.push_back('\0');

	Text = reinterpret_cast<const char*>(&Data[0]);

	return true;
}

bool CFile::WriteImage(const std::vector<unsigned char>& Data, const std::string& FileName)
{
	CFile File;
	if (!File.Create(FileName.c_str())) return false;

	if (!Data.empty()) {
		if (!File.Write(&Data[0], static_cast<int>(Data.size()))) return false;
	}

	if (!File.Close()) return false;

	return true;
}

bool CFile::WriteTextImage(const std::string& Text, const std::string& FileName)
{
	std::vector<unsigned char> Data(Text.length());

	if (!Text.empty()) memcpy(&Data[0], Text.c_str(), Text.length());

	return WriteImage(Data, FileName);
}

//
std::string CFile::GetAppUserFolderPath()
{
	filesystem::CFilePath UserFolderPath;
	UserFolderPath.SetPersonalFolderPath();
	UserFolderPath.AddFolderName("MocuMocuVRM");

	return UserFolderPath.GetString();
}

std::string CFile::TransRealFileName(const std::string& FileName)
{
	if (FileName.substr(0, 9) == "UserData\\") {
		return GetAppUserFolderPath() + "\\" + FileName;
	}
	else if (FileName.substr(0, 8) == "Setting\\") {
		return GetAppUserFolderPath() + "\\" + FileName;
	}

	return FileName;
}

}

