/*
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"
#ifdef __ANDROID__
#include <android/log.h>
#endif

#if !defined( _WIN32 ) && !defined( 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
#endif

#ifdef 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 )
{
#ifdef 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
#ifdef _WIN32
	return Wcon_Input();
#endif
	return NULL;
}

void Sys_DestroyConsole( void )
{
	// last text message into console or log
	MsgDev( D_NOTE, "Sys_DestroyConsole: Exiting!\n" );
#ifdef _WIN32
	Wcon_DestroyConsole();
#endif
}

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

SYSTEM LOG

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

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 ) MsgDev( D_ERROR, "Sys_InitLog: can't create log file %s\n", s_ld.log_path );

		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;
	}

	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;
	}
}

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 );
#ifdef __ANDROID__
	__android_log_print( ANDROID_LOG_DEBUG, "Xash", "%s", pMsg );
#endif

#if TARGET_OS_IOS
	void IOS_Log(const char*);
	IOS_Log(pMsg);
#endif


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

#ifdef XASH_COLORIZE_CONSOLE
	{
		char colored[4096];
		const char *msg = pMsg;
		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 );

	}
#else
#if !defined __ANDROID__ || defined XASH_DEDICATED
	printf( "%s %s", logtime, pMsg );
	fflush( stdout );
#endif
#endif

	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[strlen(pMsg)-1];

	fprintf( s_ld.logfile, "%s %s", logtime, pMsg );
	fflush( s_ld.logfile );
}

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

CONSOLE PRINT

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

=============
*/
void 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 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 );
}