#include "consoleapp.h"
#include <windows.h>
#include <stdexcept>
#include <string>
#include <io.h>
#include <fcntl.h>
#include <iostream>

static void RedirectHandle( DWORD handleName, FILE * where, FILE ** bkp, const std::string& name, const char * mode )
{
	intptr_t lStdHdl = (intptr_t) GetStdHandle(handleName);
	if (lStdHdl == (intptr_t)INVALID_HANDLE_VALUE)
		throw std::runtime_error(std::string("Unable to fetch console handle ") + name);
	int hConHandle = _open_osfhandle(lStdHdl, _O_TEXT);
	if (-1 == hConHandle)
		throw std::runtime_error(std::string("Unable to open osf handle for console ") + name);
	try
	{
		FILE * fp = _fdopen( hConHandle, mode );
		if (!fp)
			throw std::runtime_error(std::string("Unable to open file descriptor for console ") + name);
		*bkp = fp;
		std::swap(*fp, *where);
		setvbuf( stdin, NULL, _IONBF, 0 );
	}
	catch (...)
	{
		_close(hConHandle);
		throw;
	}
}

static void UnRedirectHandle( FILE * where, FILE ** bkp )
{
	if (*bkp)
	{
		std::swap( *where, **bkp );
		fclose( *bkp );
		*bkp = NULL;
	}
}

CConsoleApp::CConsoleApp()
{
	m_mustFreeConsole = false;
	m_backupIn = m_backupOut = m_backupErr = NULL;

	try
	{
		m_mustFreeConsole = AllocConsole() != FALSE;
		RedirectHandle( STD_OUTPUT_HANDLE, stdout, &m_backupOut, "stdout", "w" );
		RedirectHandle( STD_INPUT_HANDLE, stdin, &m_backupIn, "stdin", "r" );
		RedirectHandle( STD_OUTPUT_HANDLE, stderr, &m_backupErr, "stderr", "w" );
		std::ios::sync_with_stdio();
	}
	catch (...)
	{
		Cleanup();
		throw;
	}
}

CConsoleApp::~CConsoleApp()
{
	Cleanup();
}

void CConsoleApp::Cleanup()
{
	if (m_mustFreeConsole)
		FreeConsole();
	UnRedirectHandle( stderr, &m_backupErr );
	UnRedirectHandle( stdin, &m_backupIn );
	UnRedirectHandle( stdout, &m_backupOut );
	std::ios::sync_with_stdio();
}
