// NCachedFile.cpp: CNCachedFile クラスのインプリメンテーション
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "NCachedFile.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// 構築/消滅
//////////////////////////////////////////////////////////////////////

CNCachedFile::CNCachedFile()
	: CNFile()
{
	m_bIsWritable = FALSE;

	m_pBuff = NULL;
	m_llDataPos = 0;
	m_llDataSize = 0;
	m_dwBuffSize = 0;
	m_llCurPos = 0;
}

CNCachedFile::~CNCachedFile()
{
	Close();
	CNFile::~CNFile();
}

const BOOL CNCachedFile::Open(LPCTSTR lpszName, BYTE bFlags, const DWORD dwBuffSize)
{
	if((bFlags & CNFile::CNF_WRITE) && !(bFlags & CNFile::CNF_READ)){
		// ライトキャッシュ有効
		m_bIsWritable = TRUE;
		}
	else if(!(bFlags & CNFile::CNF_WRITE) && (bFlags & CNFile::CNF_READ) && !(bFlags & CNFile::CNF_NEW)){
		// リードキャッシュ有効
		m_bIsWritable = FALSE;
		}
	else{
		// フラグの組み合わせが非対応
		SetLastError(0x00000057);	// 「パラメータが正しくありません。」
		return FALSE;
		}

	// ファイルオープン
	if(!CNFile::Open(lpszName, bFlags))return FALSE;

	// バッファ確保
	if(!m_bIsWritable){
		// 読み込みバッファ
		m_dwBuffSize = (dwBuffSize <= GetSize())? dwBuffSize : (DWORD)GetSize();
		}

	if(m_pBuff)delete [] m_pBuff;
	m_pBuff = new BYTE [m_dwBuffSize];
	if(!m_pBuff){
		Close();
		SetLastError(0x0000000E);	// 「この操作を完了するのに十分な記憶領域がありません。」
		return FALSE;
		}

	// バッファ初期化
	m_llDataPos = 0;
	m_llDataSize = 0;
	m_llCurPos = 0;

	SetLastError(0x00000000);	// 「操作は正常に終了しました。」

	return TRUE;
}

void CNCachedFile::Close(void)
{
	// 未書き込みデータフラッシュ
	Flush();

	CNFile::Close();

	if(m_pBuff){
		delete [] m_pBuff;
		m_pBuff = NULL;
		}
}

const BOOL CNCachedFile::Read(BYTE *pBuff, const DWORD dwLen)
{
	// エラー処理
	if(m_bIsWritable){
		SetLastError(0x00000001);	// 「不正な関数です。」
		return FALSE;
		}

	if(!dwLen){
		SetLastError(0x00000057);	// 「パラメータが正しくありません。」
		return FALSE;
		}

	if((m_llCurPos + dwLen) > GetSize()){
		SetLastError(0x00000026);	// 「ファイルの終わりに達しました。」
		return FALSE;
		}

	SetLastError(0x00000000);		// 「操作は正常に終了しました。」

	// キャッシュ判定
	if(m_llDataSize > 0){
		if((m_llCurPos >= m_llDataPos) && ((m_llCurPos + dwLen) <= (m_llDataPos + m_llDataSize))){
			// 1
			CopyMemory(pBuff, m_pBuff + (m_llCurPos - m_llDataPos), dwLen);
			m_llCurPos = m_llCurPos + dwLen;
			}
		else if((m_llCurPos < m_llDataPos) && ((m_llCurPos + dwLen - 1) >= m_llDataPos) && ((m_llCurPos + dwLen) <= (m_llDataPos + m_llDataSize))){
			// 2
			if(!CNFile::Read(pBuff, (DWORD)(m_llDataPos - m_llCurPos), m_llCurPos))return FALSE;
			CopyMemory(pBuff + (m_llDataPos - m_llCurPos), m_pBuff, dwLen - (DWORD)(m_llDataPos - m_llCurPos));
			m_llCurPos = m_llCurPos + dwLen;
			}
		else if((m_llCurPos >= m_llDataPos) && (m_llCurPos <= (m_llDataPos + m_llDataSize - 1)) && ((m_llCurPos + dwLen) > (m_llDataPos + m_llDataSize))){
			// 3
			CopyMemory(pBuff, m_pBuff + (m_llCurPos - m_llDataPos), (DWORD)(m_llDataPos + m_llDataSize - m_llCurPos));
			if((dwLen - (m_llDataPos + m_llDataSize - m_llCurPos)) <= m_dwBuffSize){
				if(!CNFile::Read(m_pBuff, m_dwBuffSize, m_llDataPos + m_llDataSize))return FALSE;
				CopyMemory(pBuff + (m_llDataPos + m_llDataSize - m_llCurPos), m_pBuff, dwLen - (DWORD)(m_llDataPos + m_llDataSize - m_llCurPos));
				m_llDataPos = m_llDataPos + m_llDataSize;
				}
			else{
				if(!CNFile::Read(pBuff, dwLen - (DWORD)(m_llDataPos + m_llDataSize - m_llCurPos), m_llDataPos + m_llDataSize))return FALSE;
				}

			m_llCurPos = m_llCurPos + dwLen;
			}
		else if((m_llCurPos < m_llDataPos) && ((m_llCurPos + dwLen) > (m_llDataPos + m_llDataSize))){
			// 4
			if(!CNFile::Read(pBuff, (DWORD)(m_llDataPos - m_llCurPos), m_llCurPos))return FALSE;
			CopyMemory(pBuff + (m_llDataPos - m_llCurPos), m_pBuff, (DWORD)m_llDataSize);
			if((dwLen - (m_llDataPos + m_llDataSize - m_llCurPos)) <= m_dwBuffSize){
				if(!CNFile::Read(m_pBuff, m_dwBuffSize, m_llDataPos + m_llDataSize))return FALSE;
				CopyMemory(pBuff + (m_llDataPos + m_llDataSize - m_llCurPos), m_pBuff, (DWORD)(m_llCurPos + dwLen - m_llDataPos - m_llDataSize));
				m_llDataPos = m_llDataPos + m_llDataSize;
				}
			else{
				if(!CNFile::Read(pBuff, (DWORD)(m_llCurPos + dwLen - m_llDataPos - m_llDataSize), m_llDataPos + m_llDataSize))return FALSE;
				}

			m_llCurPos = m_llCurPos + dwLen;
			}
		else{
			// 5
			if(dwLen < m_dwBuffSize){			
				if(!CNFile::Read(m_pBuff, m_dwBuffSize, m_llCurPos))return FALSE;
				CopyMemory(pBuff, m_pBuff, dwLen);
				m_llDataPos = m_llCurPos;
				}
			else{
				if(!CNFile::Read(pBuff, dwLen, m_llCurPos))return FALSE;
				}

			m_llCurPos = m_llCurPos + dwLen;
			}
		}
	else{
		if(dwLen < m_dwBuffSize){			
			if(!CNFile::Read(m_pBuff, m_dwBuffSize, m_llCurPos))return FALSE;
			CopyMemory(pBuff, m_pBuff, dwLen);
			m_llDataPos = m_llCurPos;
			m_llDataSize = m_dwBuffSize;
			}
		else{
			if(!CNFile::Read(pBuff, dwLen, m_llCurPos))return FALSE;
			}

		m_llCurPos = m_llCurPos + dwLen;
		}

	return TRUE;
}

const BOOL CNCachedFile::Read(BYTE *pBuff, const DWORD dwLen, const LONGLONG llPos)
{
	if(m_bIsWritable){
		SetLastError(0x00000001);	// 「不正な関数です。」
		return FALSE;
		}

	// ファイルシーク
	if(Seek(llPos)){
		return Read(pBuff, dwLen);
		}

	return FALSE;
}

const BOOL CNCachedFile::Write(const BYTE *pBuff, const DWORD dwLen)
{
	// ファイル書き込み
	if(!m_bIsWritable){
		SetLastError(0x00000001);	// 「不正な関数です。」
		return FALSE;
		}

	if(!dwLen){
		SetLastError(0x00000057);	// 「パラメータが正しくありません。」
		return FALSE;
		}

	// バッファリング判定
	if((m_dwBuffSize - m_llDataSize) <= dwLen){
		// バッファ不足
		if(!Flush())return FALSE;
		}

	// バッファリング
	if((m_dwBuffSize - m_llDataSize) >= dwLen){
		CopyMemory(m_pBuff + m_llDataSize, pBuff, dwLen);
		m_llDataSize += dwLen;
		}
	else{
		if(!CNFile::Write(pBuff, dwLen, m_llCurPos))return FALSE;
		}

	// ファイルポジション更新
	m_llCurPos += dwLen;

	SetLastError(0x00000000);	// 「操作は正常に終了しました。」

	return TRUE;
}

const BOOL CNCachedFile::Write(const BYTE *pBuff, const DWORD dwLen, const LONGLONG llPos)
{
	// ファイル書き込み
	if(!m_bIsWritable){
		SetLastError(0x00000001);	// 「不正な関数です。」
		return FALSE;
		}

	// ファイルシーク
	if(Seek(llPos)){
		return Write(pBuff, dwLen);
		}

	return FALSE;
}

const BOOL CNCachedFile::Seek(const LONGLONG llPos)
{
	// シーク前にフラッシュ
	if(!Flush())return FALSE;

	// シーク
	if(!CNFile::Seek(llPos))return FALSE;

	// ファイルポジション更新
	m_llCurPos = llPos;
	m_llDataPos = m_llCurPos;

	return TRUE;
}

const BOOL CNCachedFile::Flush(void)
{
	if(!m_bIsWritable || !m_llDataSize)return TRUE;

	// バッファ先頭位置に書き込み
	if(!CNFile::Write(m_pBuff, (DWORD)m_llDataSize, m_llDataPos))return FALSE;

	// ファイルポジションを復帰
	if(!CNFile::Seek(m_llCurPos))return FALSE;

	// バッファサイズクリア
	m_llDataSize = 0;
	m_llDataPos = m_llCurPos;

	return TRUE;
}
