//	UTF8File.h - A class which can be used to load from/save to a UTF-8 text
//				 file.
//	----------------------------------------------------------------------------
//	This file is part of 'NiallsAVLib', base code for any kind of audiovisual
//	apps.
//	Copyright (C) 2012  Niall Moody
//
//	This program is free software: you can redistribute it and/or modify
//	it under the terms of the GNU General Public License as published by
//	the Free Software Foundation, either version 3 of the License, or
//	(at your option) any later version.
//
//	This program is distributed in the hope that it will be useful,
//	but WITHOUT ANY WARRANTY; without even the implied warranty of
//	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//	GNU General Public License for more details.
//
//	You should have received a copy of the GNU General Public License
//	along with this program.  If not, see <http://www.gnu.org/licenses/>.
//	----------------------------------------------------------------------------

#ifndef UTF8FILE_H_
#define UTF8FILE_H_

#include "FilePath.h"

#include <fstream>
#include <string>

#ifndef WIN32
typedef std::codecvt<wchar_t, char, std::mbstate_t> W2CConvert;
typedef std::codecvt<char, wchar_t, std::mbstate_t> C2WConvert;
#endif

///	A class which can be used to load from/save to a UTF-8 text file.
/*!
	TODO: Test on posix systems. It looks like posix is set to use UTF-8
	natively with chars, not wchar_ts like Windows. Will need to test to
	see how this affects the code here.

	Note: According to wikipedia you don't use BOMs with UTF-8, but I found
	files I wrote without BOMs were seen as ANSII by other apps, so this
	class reads and writes them.
 */
class UTF8File
{
  public:
	///	Constructor.
	/*!
		\param path The path of the file to load from/save to.
	 */
	UTF8File(const FilePath& path);
	///	Default Constructor.
	UTF8File();
	///	Destructor.
	~UTF8File();

	///	Sets the file path.
	void setFilePath(const FilePath& path);

	///	Attempts to read the entire file into a wstring.
	/*!
		\param text The string to read the file into.
		\param includeLineEndings If true, line endings ('\n') will be included
		in the returned string.
	 */
	void readFile(std::wstring& text, bool includeLineEndings = true);
	///	Attempts to read a single line from the file into a wstring.
	/*!
		\param text will be empty if we've reached the eof.

		\return True if we've reached the eof.
	 */
	bool readLine(std::wstring& text);

	///	Writes text to the file.
	void writeText(const std::wstring& text);
	///	Writes a wstring to the file.
	friend UTF8File& operator<<(UTF8File& out, const std::wstring& text);
	///	Returns true if the file could not be opened.
	bool getFail() const {return failStream;};

	///	Returns true if the file exists.
	bool exists() const {return filePath.exists();};

	///	To be used with e.g. command line args.
	static void charToWstring(const std::string& in, std::wstring& out);
	///	Used e.g. when loading shaders.
	static void wstringToChar(const std::wstring& in, std::string& out);
private:
	///	Helper method. Opens or creates the file.
	void initStream(bool read);
	///	Helper method. Tests if the file's parent dir exists, creates it if it doesn't.
	void checkParentDir();

	///	Singleton used to ensure we set the locale to UTF-8 on OSX.
	class LocaleSingleton
	{
	  public:
		///	Sets the locale.
		static void setLocale();
	  private:
		///	Constructor.
		LocaleSingleton();
	};

	///	The file we're writing to/reading from.
	FilePath filePath;

	///	The stream we use to write & read.
	std::fstream stream;

	///	For debugging, this keeps track of how many lines we've read.
	int linesRead;

	///	True if we've already tried to open this stream and failed.
	bool failStream;
};

#endif

