#pragma once
#include <fstream>
#include <sstream>
#include <iostream>

namespace DXFW
{
/// <summary>ファイルの読込書込モード</summary>
enum class FileMode
{
	Read,///読込のみ
	Write,///書込のみ
	Add,///書込時、末尾に追加
	None,///開かれていない
};

/// <summary>説明</summary>
///	<example>
///	[サンプルコード:未完成]<BR>
/// サンプル説明<BR>
///	<code>
///	int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow )<BR>
///	{<BR>
///		if( !Dx::Initialise() ) return -1;<BR>
///		Dx::End();<BR>
///		return 0;<BR>
/// }<BR>
/// </code>	
/// </example>
class File
{
private:
	std::ifstream fst;
	bool canRead;
	bool canWrite;
	bool canAdd;
	bool isBinary;
	std::string fileName;

public:
	
	///<summary>ファイルを開く</summary>
	///ファイル名とモードを指定して、テキストファイルかバイナリファイルを開く。
	File(const char* ファイル名 , FileMode 読み書きモード , bool バイナリファイル = false )
	{
		File::Open( ファイル名 , 読み書きモード , バイナリファイル );
	}
	
	///<summary>ファイルを開く</summary>
	///コンストラクタと同じ
	bool Open(const char* ファイル名 , FileMode 読み書きモード , bool バイナリファイル = false )
	{
		std::string mode;
		this->fileName = ファイル名;

		switch(読み書きモード)
		{
			case FileMode::Read:
				if( isBinary )	fst.open( ファイル名 , std::ios::in | std::ios::binary );
				else			fst.open( ファイル名 , std::ios::in );
				canRead = true;
				canWrite = false;
				canAdd = false;
			break;
			case FileMode::Write:
				if( isBinary )	fst.open( ファイル名 , std::ios::out | std::ios::binary );
				else			fst.open( ファイル名 , std::ios::out );
				canWrite = true;
				canRead = false;
				canAdd = false;
			break;
			case FileMode::Add:
				if( isBinary )	fst.open( ファイル名 , std::ios::out | std::ios::app | std::ios::binary );
				else			fst.open( ファイル名 , std::ios::out | std::ios::app );
				canWrite = true;
				canAdd = true;
				canRead = false;
			break;
		}

		this->isBinary = バイナリファイル;

		if ( fst.fail() )
		{
			canRead = false;
			canWrite = false;
			canAdd = false;
		}

		return true;
	}

	///<summary>カレントディレクトリの設定</summary>
	///実行ファイルの相対パスでカレントディレクトリを設定する
	static void SetDirectory( const char* ディレクトリ名 )
	{			
		char full [MAX_PATH+1];
		char drive[MAX_PATH+1];
		char dir  [MAX_PATH+1];
		char fname[MAX_PATH+1];
		char ext  [MAX_PATH+1];			

		GetModuleFileName( NULL , full , MAX_PATH );

		_splitpath_s( full ,
						drive , sizeof(drive),
						dir ,sizeof(dir),
						fname ,sizeof(fname),
						ext , sizeof(ext)); 

		std::string path = drive;
		path += dir;
		path += ディレクトリ名;

		SetCurrentDirectory( path.c_str() );
	}

	///<summary>ファイルを閉じる</summary>
	bool Close()
	{
		fst.close();
		canRead = false;
		canWrite = false;
		canAdd = false;
	}
			
	/// <summary>ファイルモードを取得</summary>
	FileMode GetFileMode()
	{
		if( this->canAdd )  return FileMode::Add;
		if( this->canWrite) return FileMode::Write;
		if( this->canRead ) return FileMode::Read;
		return FileMode::None;
	}

	///<summary>ファイル名を取得</summary>
	const char* GetFileName()
	{				
		return this->fileName.c_str();
	}

	///<summary>データを読み込む</summary>
	///FileMode::Readの場合成功。
	///読込先変数にデータを読み込む。
	template< class T>
	bool Read(T &読込先変数 )
	{
		if( canRead ) fst << 読込先変数;
		return canRead;
	}
			
	///<summary>データを書き込む</summary>
	///FileModeがWriteかAddの場合成功。
	///書込元変数をファイルに書き込む。
	template< class T>
	bool Write(T &書込元変数)
	{
		if( canWrite ) fst >> 書込元変数;
		return canWrite;
	}
			
	///<summary>データの読み書き</summary>
	///FileModeがReadの場合読込、WriteかAddの場合書込を行う。
	template< class T>
	bool ReadWrite(T &buf )
	{
		if( canRead )
		{
			fst >> buf;
			return true;
		}else if( canWrite ){
			fst << buf;
			return true;
		}		
		return false;
	}
	
	///<summary>ファイルから一行読込</summary>
	///FileModeがReadの場合成功。
	///ファイルから一行読込、引数に代入
	bool GetLine(std::string &str)
	{
		if( canRead ) getline( fst , str );
		return canRead;
	}
			
	///<summary>CSVファイルの一行読込</summary>
	///FileModeがReadの場合成功。
	///ファイルから一行読込、カンマ区切りで引数に代入
	bool GetLineCSV(std::vector<std::string> &strSet)
	{
		if( canRead )
		{
			std::string str;
			std::string buf;

			getline( fst , str );
			std::istringstream iss( str );

			while( getline( iss , buf , ',' ) )
			{
				strSet.push_back( buf );
			}
		}
		return canRead;
	}
	
	///<summary>ファイルの終端判定</summary>
	///ファイルが終端に達したらtrueを返す
	bool CheckEOF()
	{
		return ( fst.eof() );
	}

};
}