//	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.
	/*!
		\param text The text to write.
		\param flushAfter If true, this will flush the output stream after
		writing the text to it. This means the contents of the stream will
		be written to disk straight away, rather than when the UTF8File is
		deleted. If you're doing a lot of writeText() calls, setting
		flushAfter to true will probably slow things down a lot.
		\param noDebug If this is true, the method will not output any debug
		messages on failure. This is to prevent infinite loops from
		DebugStatement().
	 */
	void writeText(const std::wstring& text,
				   bool flushAfter = false,
				   bool noDebug = false);
	///	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.
	/*!
		\param in The wstring to convert.
		\param out The string to convert in to.
		\param noDebug If this is true, the method will not output any debug
		messages on failure. This is to prevent infinite loops from
		DebugStatement().
	 */
	static void wstringToChar(const std::wstring& in,
							  std::string& out,
							  bool noDebug = false);
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

