#include <iostream>
#include <fstream>
#include <stdio.h>

namespace fs
{
    class tempfile
	{
	public:
		// tempfile constructor.
		explicit tempfile(char const * const _file_name, std::ios_base::openmode _mode = std::ios_base::binary, char const * const _temp_dir = 0 , char const * const _prefix = "tfx") :
			m_file_name( _file_name ),
			m_temp_name( tempnam(_temp_dir, _prefix) ),
			m_temp_file( m_temp_name.c_str(), std::ios_base::out | _mode )
		{
		}

		// check to see if the temp file is open and ready for writing.
		bool is_open() const
		{
			return m_temp_file.is_open();
		}

		// boolean operator returns true if the temporary file is ready for writing.
		operator bool() const
		{
			return is_open();
		}

		// get the temporary file stream.
		std::ofstream & temp()
		{
			return m_temp_file;
		}

		// closes the temp file but doesn't commit the changes.
		void close( bool erase_temp = false )
		{
			// close the temp file.
			if ( m_temp_file )
				m_temp_file.close();

			// kill the temp file.
			if ( erase_temp )
				cleanup();
		}

		// erase the temporary file.
		void cleanup()
		{
			remove( m_temp_name.c_str() );
		}

		// replaces the final file with the temporary. note: every
		// attempt is made to preserve the original file.
		bool commit( bool keep_backup = true )
		{
			if ( is_open() ) {
				// close the temp file to move the contents.
				close( false );

				// add backup strategies to handle backup.  three strategy ideas: 
				//      1. delete - delete the original file.
				//      2. rename - rename the original file (ie, keep backup).
				//      3. archive - store the file in source control or database or offsite.
				// for now, rename the old file.
				std::string oldfilename = m_file_name;
				oldfilename += std::string(".bck");
				remove( oldfilename.c_str() );
				rename( m_file_name.c_str(), oldfilename.c_str() );

				// rename it to the new file.
				if ( rename(m_temp_name.c_str(), m_file_name.c_str()) == 0 )
				{
					if (!keep_backup) 
						remove( oldfilename.c_str() );

					return true;
				}

				// if temp rename fails, restore the original file.
				remove( m_file_name.c_str() );
				rename( oldfilename.c_str(), m_file_name.c_str() );
			}

			// if you get here, attempt to close & remove the temporary file.
			close( true );

			return false;
		}

		// close the file assuming we will explicitly commit the results. 
		~tempfile()
		{
			close( true );
		}

	private:
		std::string			m_file_name;
		std::string         m_temp_name;
		std::ofstream		m_temp_file;

	private:
		tempfile(); // disabled
	};
}

int main()
{
    fs::tempfile test("test.txt");

	if ( test ) {
		test.temp() << "nothing to see here...\n";

		if ( test.commit() )
			std::cout << "Success!" << std::endl;
		else
			std::cout << "Failure :( " << std::endl;
	}

	return 0;
}


