/*
sys_con.c - stdout and log
Copyright (C) 2007 Uncle Mike

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.
*/

#include "common.h"
#if XASH_WIN32
#define STDOUT_FILENO 1
#include <io.h>
#elif XASH_ANDROID
#include <android/log.h>
#endif
#include <string.h>
#include <errno.h>

#if !XASH_WIN32 && !XASH_MOBILE_PLATFORM
// #define XASH_COLORIZE_CONSOLE
// use with caution, running engine in Qt Creator may cause a freeze in read() call
// I was never encountered this bug anywhere else, so still enable by default
// #define XASH_USE_SELECT 1
#endif

#if XASH_USE_SELECT
// non-blocking console input
#include <sys/select.h>
#endif

typedef struct {
	char		title[64];
	qboolean		log_active;
	char		log_path[MAX_SYSPATH];
	FILE		*logfile;
	int 		logfileno;
} LogData;

static LogData s_ld;

char *Sys_Input( void )
{
#if XASH_USE_SELECT
	{
		fd_set rfds;
		static char line[1024];
		static int len;
		struct timeval tv;
		tv.tv_sec = 0;
		tv.tv_usec = 0;
		FD_ZERO( &rfds );
		FD_SET( 0, &rfds); // stdin
		while( select( 1, &rfds, NULL, NULL, &tv ) > 0 )
		{
			if( read( 0, &line[len], 1 ) != 1 )
				break;
			if( line[len] == '\n' || len > 1022 )
			{
				line[ ++len ] = 0;
				len = 0;
				return line;
			}
			len++;
			tv.tv_sec = 0;
			tv.tv_usec = 0;
		}
	}
#endif
#if XASH_WIN32
	return Wcon_Input();
#endif
	return NULL;
}

void Sys_DestroyConsole( void )
{
	// last text message into console or log
	Con_Reportf( "Sys_DestroyConsole: Exiting!\n" );
#if XASH_WIN32
	Wcon_DestroyConsole();
#endif
}

/*
===============================================================================

SYSTEM LOG

===============================================================================
*/
int Sys_LogFileNo( void )
{
	return s_ld.logfileno;
}

static void Sys_FlushStdout( void )
{
	// never printing anything to stdout on mobiles
#if !XASH_MOBILE_PLATFORM
	fflush( stdout );
#endif
}

static void Sys_FlushLogfile( void )
{
	if( s_ld.logfile )
		fflush( s_ld.logfile );
}

void Sys_InitLog( void )
{
	const char	*mode;

	if( Sys_CheckParm( "-log" ) && host.allow_console != 0 )
	{
		s_ld.log_active = true;
		Q_strncpy( s_ld.log_path, "engine.log", sizeof( s_ld.log_path ));
	}

	if( host.change_game && host.type != HOST_DEDICATED )
		mode = "a";
	else mode = "w";

	// create log if needed
	if( s_ld.log_active )
	{
		s_ld.logfile = fopen( s_ld.log_path, mode );

		if ( !s_ld.logfile )
		{
			Con_Reportf( S_ERROR  "Sys_InitLog: can't create log file %s: %s\n", s_ld.log_path, strerror( errno ) );
			return;
		}

		s_ld.logfileno = fileno( s_ld.logfile );

		fprintf( s_ld.logfile, "=================================================================================\n" );
		fprintf( s_ld.logfile, "\t%s (build %i) started at %s\n", s_ld.title, Q_buildnum(), Q_timestamp( TIME_FULL ) );
		fprintf( s_ld.logfile, "=================================================================================\n" );
	}
}

void Sys_CloseLog( void )
{
	char	event_name[64];

	// continue logged
	switch( host.status )
	{
	case HOST_CRASHED:
		Q_strncpy( event_name, "crashed", sizeof( event_name ));
		break;
	case HOST_ERR_FATAL:
		Q_strncpy( event_name, "stopped with error", sizeof( event_name ));
		break;
	default:
		if( !host.change_game ) Q_strncpy( event_name, "stopped", sizeof( event_name ));
		else Q_strncpy( event_name, host.finalmsg, sizeof( event_name ));
		break;
	}

	Sys_FlushStdout(); // flush to stdout to ensure all data was written

	if( s_ld.logfile )
	{
		fprintf( s_ld.logfile, "\n");
		fprintf( s_ld.logfile, "=================================================================================");
		if( host.change_game ) fprintf( s_ld.logfile, "\n\t%s (build %i) %s\n", s_ld.title, Q_buildnum(), event_name );
		else fprintf( s_ld.logfile, "\n\t%s (build %i) %s at %s\n", s_ld.title, Q_buildnum(), event_name, Q_timestamp( TIME_FULL ));
		fprintf( s_ld.logfile, "=================================================================================\n");

		fclose( s_ld.logfile );
		s_ld.logfile = NULL;
	}
}

static void Sys_PrintColorized( const char *logtime, const char *msg )
{
	char colored[4096];
	int len = 0;

	while( *msg && ( len < 4090 ) )
	{
		static char q3ToAnsi[ 8 ] =
		{
			'0', // COLOR_BLACK
			'1', // COLOR_RED
			'2', // COLOR_GREEN
			'3', // COLOR_YELLOW
			'4', // COLOR_BLUE
			'6', // COLOR_CYAN
			'5', // COLOR_MAGENTA
			0 // COLOR_WHITE
		};

		if( IsColorString( msg ) )
		{
			int color;

			msg++;
			color = q3ToAnsi[ *msg++ % 8 ];
			colored[len++] = '\033';
			colored[len++] = '[';
			if( color )
			{
				colored[len++] = '3';
				colored[len++] = color;
			}
			else
				colored[len++] = '0';
			colored[len++] = 'm';
		}
		else
			colored[len++] = *msg++;
	}
	colored[len] = 0;

	printf( "\033[34m%s\033[0m%s\033[0m", logtime, colored );
}

static void Sys_PrintFile( int fd, const char *logtime, const char *msg )
{
	const char *p = msg;

	write( fd, logtime, Q_strlen( logtime ) );

	while( p && *p )
	{
		p = Q_strchr( msg, '^' );

		if( p == NULL )
		{
			write( fd, msg, Q_strlen( msg ));
			break;
		}
		else if( IsColorString( p ))
		{
			if( p != msg )
			{
				write( fd, msg, p - msg );
			}
			msg = p + 2;
		}
		else
		{
			write( fd, msg, p - msg + 1 );
			msg = p + 1;
		}
	}
}

static void Sys_PrintStdout( const char *logtime, const char *msg )
{
#if XASH_MOBILE_PLATFORM
	static char buf[MAX_PRINT_MSG];

	// strip color codes
	COM_StripColors( msg, buf );

	// platform-specific output
#if XASH_ANDROID && !XASH_DEDICATED
	__android_log_print( ANDROID_LOG_DEBUG, "Xash", "%s", buf );
#endif // XASH_ANDROID && !XASH_DEDICATED

#if TARGET_OS_IOS
	void IOS_Log( const char * );
	IOS_Log( buf );
#endif // TARGET_OS_IOS
#else // XASH_MOBILE_PLATFORM
	Sys_PrintFile( STDOUT_FILENO, logtime, msg );
#endif
}

void Sys_PrintLog( const char *pMsg )
{
	time_t crt_time;
	const struct tm	*crt_tm;
	char logtime[32] = "";
	static char lastchar;

	time( &crt_time );
	crt_tm = localtime( &crt_time );

	if( !lastchar || lastchar == '\n')
		strftime( logtime, sizeof( logtime ), "[%H:%M:%S] ", crt_tm ); //short time

	// spew to stdout
#ifdef XASH_COLORIZE_CONSOLE
	Sys_PrintColorized( logtime, pMsg );
#elif !XASH_WIN32 // Wcon already does the job
	Sys_PrintStdout( logtime, pMsg );
#endif
	Sys_FlushStdout();

	if( !s_ld.logfile )
		return;

	if( !lastchar || lastchar == '\n')
		strftime( logtime, sizeof( logtime ), "[%Y:%m:%d|%H:%M:%S] ", crt_tm ); //full time
	
	// save last char to detect when line was not ended
	lastchar = pMsg[Q_strlen( pMsg ) - 1];

	Sys_PrintFile( s_ld.logfileno, logtime, pMsg );
	Sys_FlushLogfile();
}

/*
=============================================================================

CONSOLE PRINT

=============================================================================
*/
/*
=============
Con_Printf

=============
*/
void GAME_EXPORT Con_Printf( const char *szFmt, ... )
{
	static char	buffer[MAX_PRINT_MSG];
	va_list		args;

	if( !host.allow_console )
		return;

	va_start( args, szFmt );
	Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
	va_end( args );

	Sys_Print( buffer );
}

/*
=============
Con_DPrintf

=============
*/
void GAME_EXPORT Con_DPrintf( const char *szFmt, ... )
{
	static char	buffer[MAX_PRINT_MSG];
	va_list		args;

	if( host_developer.value < DEV_NORMAL )
		return;

	va_start( args, szFmt );
	Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
	va_end( args );

	if( buffer[0] == '0' && buffer[1] == '\n' && buffer[2] == '\0' )
		return; // hlrally spam

	Sys_Print( buffer );
}

/*
=============
Con_Reportf

=============
*/
void Con_Reportf( const char *szFmt, ... )
{
	static char	buffer[MAX_PRINT_MSG];
	va_list		args;

	if( host_developer.value < DEV_EXTENDED )
		return;

	va_start( args, szFmt );
	Q_vsnprintf( buffer, sizeof( buffer ), szFmt, args );
	va_end( args );

	Sys_Print( buffer );
}


#if XASH_MESSAGEBOX == MSGBOX_STDERR
void Platform_MessageBox( const char *title, const char *message, qboolean parentMainWindow )
{
	fprintf( stderr,
		 "======================================\n"
		 "%s: %s\n"
		 "======================================\n", title, message );
}
#endif