/*
  FLUIDS v.1 - SPH Fluid Simulator for CPU and GPU
  Copyright (C) 2009. Rama Hoetzlein, http://www.rchoetzlein.com

  ZLib license
  This software is provided 'as-is', without any express or implied
  warranty.  In no event will the authors be held liable for any damages
  arising from the use of this software.

  Permission is granted to anyone to use this software for any purpose,
  including commercial applications, and to alter it and redistribute it
  freely, subject to the following restrictions:

  1. The origin of this software must not be misrepresented; you must not
     claim that you wrote the original software. If you use this software
     in a product, an acknowledgment in the product documentation would be
     appreciated but is not required.
  2. Altered source versions must be plainly marked as such, and must not be
     misrepresented as being the original software.
  3. This notice may not be removed or altered from any source distribution.
*/



//#define MEMORY_DEBUG						// Overloads the new and delete operators

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#include "mdebug.h"

#ifdef _MSC_VER
	#include <windows.h>
	#include <io.h>
	#include <fcntl.h>	
	#include <conio.h>
#endif

CDebug debug;
CError error;

//------------------------------------------------- DEBUG CLASS
CDebug::CDebug ()
{
	m_OutName = "debug.txt";	
	m_bStarted = false;
	m_bToFile = true;
	m_bToSysbox = true;
	m_bToCons = false;	
	m_OutFile = NULL;	
	m_OutCons = NULL;
	Start ();
}

CDebug::~CDebug (void)
{	
	Stop ();	
}

void CDebug::Start ()
{
	if ( m_bStarted ) Stop ();
	
	if ( m_bToFile ) {
		char fn[200];

#ifdef _MSC_VER
		strcpy_s ( fn, 200, m_OutName.c_str () );
		fopen_s ( &m_OutFile, fn, "w+t" );
#else 
        strncpy ( fn, m_OutName.c_str(), 200 );
        m_OutFile = fopen( fn, "w+" );
#endif 

		if ( m_OutFile == 0x0 ) {
			exit ( EXIT_FAILURE );		// error: Cannot create debug file
		}
		m_bStarted = true;
		Print ( "debug", "CDebug started to file.\n");
	}		
	m_bStarted = true;	
}

void CDebug::Exit ( int code )
{
	if ( !m_bToSysbox ) {
		#ifdef _MSC_VER
			_getch();
		#endif
	}
	exit ( code );	
}

void CDebug::Stop ()
{
	if ( m_bStarted ) {
		if ( m_bToFile ) {
			Print ( "debug", "CDebug stopped.");
			fclose ( m_OutFile );			
		}
	}
	m_bStarted = false;
}

void CDebug::SendToFile ( char* fname )
{
	if (m_bStarted) Stop ();
	m_bToFile = true;
	m_OutName = fname;
}

void CDebug::SendToConsole ( bool tf )
{
	m_bToCons = tf;
	if ( tf ) {
		#ifdef _MSC_VER
			AllocConsole ();
			long lStdHandle = (long) GetStdHandle( STD_OUTPUT_HANDLE );
			int hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
			m_OutCons = _fdopen( hConHandle, "w" );
		#endif
	}
}

void CDebug::SendToSysbox ( bool tf )
{
	m_bToSysbox = tf;
}

void CDebug::Print ( std::string subsys, std::string msg )
{
	if ( m_bStarted ) {
		if ( m_bToFile ) {
			fprintf ( m_OutFile, "%s: %s\n", subsys.c_str(), msg.c_str() );
			fflush ( m_OutFile);
		}
		if ( m_bToCons ) {
			#ifdef _MSC_VER
				fprintf ( m_OutCons, "%s: %s\n", subsys.c_str(), msg.c_str() );
				fflush ( m_OutCons );
			#else
				printf ( "%s: %s\n", subsys.c_str(), msg.c_str() );
			#endif
		}				
	}
}

void CDebug::Print (char* msg)
{
	if ( m_bStarted ) {
		if ( m_bToFile ) {
			fprintf ( m_OutFile, "%s", msg );
			fflush ( m_OutFile );
		}
		if ( m_bToCons ) {
			#ifdef _MSC_VER
				fprintf ( m_OutCons, "%s", msg );
				fflush ( m_OutCons );
			#else
				printf ( "%s", msg );
			#endif
		}				
	}	
}

void CDebug::Print ( std::string msg )
{
	if ( m_bStarted ) {
		if ( m_bToFile ) {
			fprintf ( m_OutFile, "%s", msg.c_str() );
			fflush ( m_OutFile );
		}
		if ( m_bToCons ) {
			#ifdef _MSC_VER
				fprintf ( m_OutCons, "%s", msg.c_str() );
				fflush ( m_OutCons );
			#else
				printf ( "%s", msg.c_str() );
			#endif
		}				
	}	
}

void CDebug::PrintF ( std::string substr, char* format, ... )
{
	// Note: This is the >only< way to do this. There is no general way to
	// pass on all the arguments from one ellipsis function to another.
	// The function vfprintf was specially designed to allow this.

	if ( m_bStarted ) {
		if ( m_bToFile ) {
			va_list argptr;
			va_start (argptr, format);
			fprintf ( m_OutFile, "%s: ", substr.c_str() );
 			vfprintf ( m_OutFile, format, argptr);
			va_end (argptr);
			fflush ( m_OutFile );
		}
		if ( m_bToCons ) {
			#ifdef _MSC_VER
				va_list argptr;
				va_start (argptr, format);
				fprintf ( m_OutCons, "%s: ", substr.c_str() );
 				vfprintf ( m_OutCons, format, argptr);			
				va_end (argptr);			
				fflush ( m_OutCons );
			#else
				va_list argptr;
				va_start (argptr, format);
				printf ( "%s: ", substr.c_str() );
 				vprintf ( format, argptr);			
				va_end (argptr);			
			#endif
		}	
	}
}


void CDebug::Printf ( char* format, ... )
{
	// Note: This is the >only< way to do this. There is no general way to
	// pass on all the arguments from one ellipsis function to another.
	// The function vfprintf was specially designed to allow this.

	if ( m_bStarted ) {
		if ( m_bToFile ) {
			va_list argptr;
			va_start (argptr, format);			
 			vfprintf ( m_OutFile, format, argptr);
			va_end (argptr);
			fflush ( m_OutFile );
		}
		if ( m_bToCons ) {
			#ifdef _MSC_VER
				va_list argptr;
				va_start (argptr, format);				
 				vfprintf ( m_OutCons, format, argptr);			
				va_end (argptr);			
				fflush ( m_OutCons );
			#else
				va_list argptr;
				va_start (argptr, format);				
 				vprintf ( format, argptr);			
				va_end (argptr);			
			#endif
		}	
	}
}

void CDebug::PrintErr ( std::string errid, std::string subsys, std::string msg, std::string sysbox )
{
	if ( m_bStarted ) {
		if ( m_bToFile ) {
			fprintf ( m_OutFile, "%s: ERROR: %s\n", subsys.c_str(), msg.c_str() );
			fflush ( m_OutFile );
		}
		if ( m_bToCons ) {
			#ifdef _MSC_VER
				fprintf ( m_OutCons, "%s: ERROR: %s\n", subsys.c_str(), msg.c_str() );
				fflush ( m_OutCons );
			#else
				printf ( "%s: ERROR[%s] %s\n", subsys.c_str(), errid.c_str(), msg.c_str() );
			#endif				
		}
		if ( m_bToSysbox ) {			
			char disp[4000];
			char caption[200];
			
			#ifdef _MSC_VER
				// Message boxes for Windows 
				strcpy_s ( caption, 200, "Mint - Error" );
				strcpy_s ( disp, 4000, sysbox.c_str()); 					
				#include <windows.h>
				int hr = MessageBoxA ( 0x0, disp, caption, MB_OK);			
			#else 
				strncpy ( caption, m_ErrorSubsys.c_str(), 200 );
				strncpy ( disp, msg.c_str(), 4000 ); 		
			#endif
		}
	}
}


//-------------------------------------------------------------------- Error Class Code 
// 
// This software is released under the LGPL Open Source Liscense. 
// See the documentation included with this source code for terms of modification, 
// distribution and re-release. 
//
// Original Copyright (C) 2002 Rama C. Hoetzlein, GameX R4
// 

CError::CError ()
{
	m_Errors.clear ();
	m_ErrorID = "";
	m_ErrorSubsys = "";
	m_ErrorMsg = "";
	m_ErrorFunc = "";
	m_ErrorFix = "";
	m_ErrorExtra = "";
}

void CError::Start ()
{
	Start ( "" );
}

void CError::Start ( char* fname )
{
	if ( fname != 0x0 && strlen(fname) > 0 ) {
		// Read error message file. NOT YET IMPLEMENTED
	} else {
		debug.Print ( "error", "No error file loaded." );
	}
	debug.Print ( "error", "Error handler started." );
	m_bStarted = true;
}

void CError::OutputMessage ()
{
	// Create sysbox message
	std::string box_msg;
	box_msg = "Subsystem: " + m_ErrorSubsys + "\n\n";
	box_msg += "Error: " + m_ErrorMsg + "\n";		
	if ( m_ErrorExtra.length() > 0)		box_msg += "Info: " + m_ErrorExtra + "\n";
	if ( m_ErrorFix.length() > 0)		box_msg += "\nFix: " + m_ErrorFix + "\n";	
	if ( m_ErrorID.length() > 0 )		box_msg += "Error ID: " + m_ErrorID + "\n";
	if ( m_ErrorFunc.length() > 0 )		box_msg += "Function: " + m_ErrorFunc + "\n";			
			
	// Error output to debug file 
	debug.PrintErr ( m_ErrorID, m_ErrorSubsys, m_ErrorMsg, box_msg );	
}

void CError::Exit ( int code )
{
	debug.Exit ( code );
}

void CError::Print ( char* msg)
{
	// User-level error (no additional info)
	m_ErrorID = "";
	m_ErrorSubsys = "undef";
	m_ErrorMsg = msg;
	m_ErrorFunc = "";
	m_ErrorFix = "";
	m_ErrorExtra = "";	
	OutputMessage ();
}

void CError::Print ( std::string msg )
{
	// User-level error (no additional info)
	m_ErrorID = "";
	m_ErrorSubsys = "undef";
	m_ErrorMsg = msg;
	m_ErrorFunc = "";
	m_ErrorFix = "";
	m_ErrorExtra = "";	
	OutputMessage ();
}

void CError::Print ( std::string subsys, std::string msg ){
	// Unregistered error
	m_ErrorID = "";
	m_ErrorSubsys = subsys;
	m_ErrorMsg = msg;
	m_ErrorFunc = "";
	m_ErrorFix = "";
	m_ErrorExtra = "";
	OutputMessage ();
}

void CError::PrintF ( std::string subsys, char* msg, ... )
{
	char buf[2000];
	va_list argptr;
	m_ErrorID = "";
	m_ErrorSubsys = subsys;
	va_start(argptr, msg);
    #ifdef _MSC_VER
 		vsprintf_s (buf, 2000, msg, argptr);
    #else
		vsnprintf(buf, 2000, msg, argptr);
    #endif
	va_end (argptr);	
	m_ErrorMsg = buf;
	m_ErrorFunc = "";
	m_ErrorFix = "";
	m_ErrorExtra = "";
	OutputMessage ();
}


void CError::PrintErr ( std::string err )
{
	// Registered error - NOT YET IMPLEMENTED
	// CErrorMsg* msg = m_
}

void CError::PrintErrDX ( std::string err, int result )
{
//
// #ifdef BUILD_DX
// m_ErrorExtra = DXGetErrorString9 ( result );
// #endif
	OutputMessage ();
	m_ErrorExtra = "";
}

void CError::PrintErrGL ( std::string err, int result )
{
	OutputMessage ();
	m_ErrorExtra = "";
}

void CError::PrintErrW ( std::string err,  int result )
{

	OutputMessage ();
	m_ErrorExtra = "";
}


/*
void CError::GetErrorMessage ( ErrorDef::Errors err )
{
	switch (err) {
	//----------------------------------------------------- MATRIX Errors
	case ErrorDef::MatrixIsNull:
		m_strDescription = "Matrix data is null\n";
		m_strFunction = "Matrix::op()";
		m_strFix = "";
		break;
	case ErrorDef::ColOutOfBounds:
		m_strDescription = "Column index out of bounds\n";
		m_strFunction = "Matrix::op()";
		m_strFix = "";
		break;
	case ErrorDef::RowOutOfBounds:
		m_strDescription = "Row index out of bounds\n";
		m_strFunction = "Matrix::op()";
		m_strFix = "";
		break;
	//----------------------------------------------------- WinDX Errors
	case ErrorDef::DXInitFail:		
		m_strDescription = "Initialization of DirectX failed.";
		m_strFunction = "GameX::InitDirect3D";
		m_strFix = "Reinstall DirectX 9.0. Confirm DirectX hardware support.";
		break;
	case ErrorDef::DXCannotGetMode:		
		m_strDescription = "Cannot get current display mode.";
		m_strFunction = "GameX::SysGetCurrentMode";
		m_strFix = "";
		break;
	case ErrorDef::DXCannotSetMode:		
		m_strDescription = "Cannot select display mode.";
		m_strFunction = "GameX::SysSelectMode";
		m_strFix = "Try requesting a different display mode and options.";
		break;
	case ErrorDef::WinCannotCreateClass:		
		m_strDescription = "Cannot create Windows class.";
		m_strFunction = "GameX::SysCreateWindowsClass";
		m_strFix = "Close other applications. Restart system.";
		break;
	case ErrorDef::WinCannotCreateWindow:		
		m_strDescription = "Cannot create Window.";
		m_strFunction = "GameX::SysCreateWindow";
		m_strFix = "Try requesting a different display mode and options.\nClose other applications. Restart system. Possibly unsupported display hardware.";
		break;
	case ErrorDef::DXCannotCreateDevice:	
		m_strDescription = "Cannot create DirectX Device.";
		m_strFunction = "GameX::SysCreateDevice3D";
		m_strFix = "Try requesting a different display mode and options.";
		break;
	case ErrorDef::DXCannotCreateDepthStencils:		
		m_strDescription = "Cannot create DirectX Depth Stencils.";
		m_strFunction = "GameX::SysCreateDepthStencils";
		m_strFix = "Try requesting a different display mode and options.";
		break;
	//---------------------------------------------------------------- File Errors
	case ErrorDef::CannotAppendAndRead:		
		m_strDescription = "Cannot open a file in both APPEND and READ mode.";
		m_strFunction = "File::Open";
		m_strFix = "Use either APPEND, READ or WRITE when opening file.";
		break;
	case ErrorDef::CannotAppendAndWrite:		
		m_strDescription = "Cannot open a file in both APPEND and WRITE mode.";
		m_strFunction = "File::Open";
		m_strFix = "Use either APPEND, READ or WRITE when opening file.";
		break;
	case ErrorDef::FileNotFound:		
		m_strDescription = "File not found.";
		m_strFunction = "File::Open";
		m_strFix = "Use AUTOCREATE if you would like to create a new file.";
		break;
	case ErrorDef::LeaveEofOnlyOnAppend:		
		m_strDescription = "Warning:LEAVEEOF flag can only be used in APPEND mode.";
		m_strFunction = "File::Open";
		m_strFix = "Remove the LEAVEEOF flag or open file in APPEND mode.";
		break;
	case ErrorDef::NoModeSpecified:		
		m_strDescription = "No open mode specified.";
		m_strFunction = "File::Open";
		m_strFix = "You must specify READ, WRITE or APPEND to open a file.";
		break;
	case ErrorDef::NoNotOpenCmd:		
		m_strDescription = "NOTOPEN cannot be used as an open file flag.";
		m_strFunction = "File::Open";
		m_strFix = "Remove the NOTOPEN flag.";
		break;
	case ErrorDef::NoSeqAndRandom:		
		m_strDescription = "Cannot open file in both SEQUENTIAL and RANDOM access modes.";
		m_strFunction = "File::Open";
		m_strFix = "Choose either SEQUENTIAL or RANDOM mode to open file in.";
		break;		
	case ErrorDef::CannotCreateFont:	
		m_strDescription = "Cannot create default font.";
		m_strFunction = "RenderDX::Initialize";
		m_strFix = "DirectX fonts are not supported for some reason.";
		break;		
	case ErrorDef::CannotCreateSprite:		
		m_strDescription = "Cannot create default sprite.";
		m_strFunction = "RenderDX::Initialize";
		m_strFix = "DirectX sprites are not supported for some reason.";
		break;		
	case ErrorDef::CannotAddImage:		
		m_strDescription = "Cannot create image on video memory.";
		m_strFunction = "RenderDX::AddImage";
		m_strFix = "DirectX was unable to create hardware texture for the image.";
		break;		
	case ErrorDef::CannotUpdateImage:
		m_strDescription = "Cannot update image data on video memory.";
		m_strFunction = "RenderDX::UpdateImage";
		m_strFix = "DirectX was unable to modify hardware texture for the image.";
		break;		
	case ErrorDef::CannotOpenFile:
		#if defined(BUILD_VSNET)
			m_strDescription = strerror( errno );
		#else	
			m_strDescription = "";		// should replace with Mac general error code
		#endif
		m_strFunction = "File::Open";
		m_strFix = "";
		break;
	case ErrorDef::ImageLoadError:
		m_strDescription = "Unable to load image.";
		m_strFunction = "ImageX::Load";
		m_strFix = "";
		break;
	default:
		char msg[500];
		sprintf (msg, "%d", (int) err );
		m_strDescription = "Undefined error: ";
		m_strDescription += msg;
		m_strSubsystem = "Undefined error.";
		m_strFix = "Error must be given a description and fix message.";
		break;
	};
}
*/



#ifdef MEMORY_DEBUG

	// User-defined operator new.
	void *operator new( size_t stSize ) 
	{
		void* pvMem = malloc( stSize );
		debug.Printf ( "NEW %p (%d bytes)\n", pvMem, stSize );
		return pvMem;
	}

	// User-defined operator delete.
	void operator delete( void *pvMem )
	{
		debug.Printf ( "DELETE %p\n", pvMem );
		free ( pvMem );	
	}
#endif