// console history handling functions

#include "q_shared.h"
#include "qcommon.h"

static      qboolean historyLoaded = qfalse;

#define     COMMAND_HISTORY 32

static field_t historyEditLines[COMMAND_HISTORY];

static int nextHistoryLine; // the last line in the history buffer, not masked
static int historyLine;     // the line being displayed from history buffer
                                 // will be <= nextHistoryLine

#define     MAX_CONSOLE_SAVE_BUFFER ( COMMAND_HISTORY * (MAX_EDIT_LINE + 13) )

static void Con_LoadHistory( void );
static void Con_SaveHistory( void );


/*
================
Con_ResetHistory
================
*/
void Con_ResetHistory( void )
{
	historyLoaded = qfalse;
	nextHistoryLine = 0;
	historyLine = 0;
}


/*
================
Con_SaveField
================
*/
void Con_SaveField( const field_t *field )
{
	const field_t *h;

	if ( !field || field->buffer[0] == '\0' )
		return;

	if ( historyLoaded == qfalse ) {
		historyLoaded = qtrue;
		Con_LoadHistory();
	}

	// try to avoid inserting duplicates
	if ( nextHistoryLine > 0 ) {
		h = &historyEditLines[(nextHistoryLine-1) % COMMAND_HISTORY];
		if ( field->cursor == h->cursor && field->scroll == h->scroll && !strcmp( field->buffer, h->buffer ) ) {
			historyLine = nextHistoryLine;
			return;
		}
	}

	historyEditLines[nextHistoryLine % COMMAND_HISTORY] = *field;
	nextHistoryLine++;
	historyLine = nextHistoryLine;

	Con_SaveHistory();
}


/*
================
Con_HistoryGetPrev

returns qtrue if previously returned edit field needs to be updated
================
*/
qboolean Con_HistoryGetPrev( field_t *field )
{
	qboolean bresult;

	if ( historyLoaded == qfalse ) {
		historyLoaded = qtrue;
		Con_LoadHistory();
	}

	if ( nextHistoryLine - historyLine < COMMAND_HISTORY && historyLine > 0 ) {
		bresult = qtrue;
		historyLine--;
	} else {
		bresult = qfalse;
	}

	*field = historyEditLines[ historyLine % COMMAND_HISTORY ];

	return bresult;
}


/*
================
Con_HistoryGetNext

returns qtrue if previously returned edit field needs to be updated
================
*/
qboolean Con_HistoryGetNext( field_t *field )
{
	qboolean bresult;

	if ( historyLoaded == qfalse ) {
		historyLoaded = qtrue;
		Con_LoadHistory();
	}

	historyLine++;

	if ( historyLine >= nextHistoryLine ) {
		if ( historyLine == nextHistoryLine )
			bresult = qtrue;
		else
			bresult = qfalse;
		historyLine = nextHistoryLine;
		Field_Clear( field );
		return bresult;
	}

	*field = historyEditLines[ historyLine % COMMAND_HISTORY ];

	return qtrue;
}


/*
================
Con_LoadHistory
================
*/
static void Con_LoadHistory( void )
{
	char consoleSaveBuffer[ MAX_CONSOLE_SAVE_BUFFER ];
	int  consoleSaveBufferSize;
	const char *token, *text_p;
	int i, numChars, numLines = 0;
	field_t *edit;
	fileHandle_t f;

	for ( i = 0 ; i < COMMAND_HISTORY ; i++ ) {
		Field_Clear( &historyEditLines[i] );
	}

	consoleSaveBufferSize = FS_Home_FOpenFileRead( CONSOLE_HISTORY_FILE, &f );
	if ( f == FS_INVALID_HANDLE )
	{
		Com_Printf( "Couldn't read %s.\n", CONSOLE_HISTORY_FILE );
		return;
	}

	if ( consoleSaveBufferSize < MAX_CONSOLE_SAVE_BUFFER &&
			FS_Read( consoleSaveBuffer, consoleSaveBufferSize, f ) == consoleSaveBufferSize )
	{
		consoleSaveBuffer[ consoleSaveBufferSize ] = '\0';
		text_p = consoleSaveBuffer;

		for( i = COMMAND_HISTORY - 1; i >= 0; i-- )
		{
			if ( !*( token = COM_Parse( &text_p ) ) )
				break;

			edit = &historyEditLines[ i ];

			edit->cursor = atoi( token );

			if ( !*( token = COM_Parse( &text_p ) ) )
				break;

			edit->scroll = atoi( token );

			if( !*( token = COM_Parse( &text_p ) ) )
				break;

			numChars = atoi( token );
			text_p++;
			if ( numChars > ( consoleSaveBufferSize - ( text_p - consoleSaveBuffer ) ) || numChars >= sizeof( edit->buffer ) )
			{
				Com_DPrintf( S_COLOR_YELLOW "WARNING: probable corrupt history\n" );
				break;
			}

			if ( edit->cursor > sizeof( edit->buffer ) - 1 )
				edit->cursor = sizeof( edit->buffer ) - 1;
			else if ( edit->cursor < 0 )
				edit->cursor = 0;

			if ( edit->scroll > edit->cursor )
				edit->scroll = edit->cursor;
			else if ( edit->scroll < 0 )
				edit->scroll = 0;

			Com_Memcpy( edit->buffer, text_p, numChars );
			edit->buffer[ numChars ] = '\0';
			text_p += numChars;

			numLines++;
		}

		memmove( &historyEditLines[ 0 ], &historyEditLines[ i + 1 ],
				numLines * sizeof( field_t ) );
		for( i = numLines; i < COMMAND_HISTORY; i++ )
			Field_Clear( &historyEditLines[ i ] );

		historyLine = nextHistoryLine = numLines;
	}
	else
		Com_Printf( "Couldn't read %s.\n", CONSOLE_HISTORY_FILE );

	FS_FCloseFile( f );
}


/*
================
Con_SaveHistory
================
*/
static void Con_SaveHistory( void )
{
	char            consoleSaveBuffer[ MAX_CONSOLE_SAVE_BUFFER ];
	int             consoleSaveBufferSize;
	int             i;
	int             lineLength, saveBufferLength, additionalLength;
	fileHandle_t    f;

	consoleSaveBuffer[ 0 ] = '\0';

	i = ( nextHistoryLine - 1 + COMMAND_HISTORY ) % COMMAND_HISTORY;
	do
	{
		if( historyEditLines[ i ].buffer[ 0 ] )
		{
			lineLength = strlen( historyEditLines[ i ].buffer );
			saveBufferLength = strlen( consoleSaveBuffer );

			//ICK
			additionalLength = lineLength + 13; // strlen( "999 999 999  " )

			if( saveBufferLength + additionalLength < MAX_CONSOLE_SAVE_BUFFER )
			{
				Q_strcat( consoleSaveBuffer, MAX_CONSOLE_SAVE_BUFFER,
						va( "%d %d %d %s ",
						historyEditLines[ i ].cursor,
						historyEditLines[ i ].scroll,
						lineLength,
						historyEditLines[ i ].buffer ) );
			}
			else
				break;
		}
		i = ( i - 1 + COMMAND_HISTORY ) % COMMAND_HISTORY;
	}
	while( i != ( nextHistoryLine - 1 + COMMAND_HISTORY ) % COMMAND_HISTORY );

	consoleSaveBufferSize = strlen( consoleSaveBuffer );

	f = FS_FOpenFileWrite( CONSOLE_HISTORY_FILE );
	if( f == FS_INVALID_HANDLE )
	{
		Com_Printf( "Couldn't write %s.\n", CONSOLE_HISTORY_FILE );
		return;
	}

	if( FS_Write( consoleSaveBuffer, consoleSaveBufferSize, f ) < consoleSaveBufferSize )
		Com_Printf( "Couldn't write %s.\n", CONSOLE_HISTORY_FILE );

	FS_FCloseFile( f );
}
