/*
Copyright (C) 2003-2006 Andrey Nazarov

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 2
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.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

*/

//
// prompt.c
//

#include "qcommon.h"
#include "q_field.h"
#include "prompt.h"

static char		*matches[MAX_MATCHES];
static char		*sortedMatches[MAX_MATCHES];
static int		numMatches;
static int		numCommands;
static int		numCvars;
static int		numAliases;

static cvar_t	*com_completion_mode;

/*
====================
Prompt_FreeMatches
====================
*/
static void Prompt_FreeMatches( void ) {
	int i;

	// free them
	for( i = 0; i < numMatches; i++ ) {
		Z_Free( matches[i] );
	}

	numMatches = 0;
	numCommands = 0;
	numCvars = 0;
	numAliases = 0;

}

static void Prompt_FooGenerator( const char *partial, xgenerator_t generator ) {
	const char *match;

    match = (*generator)( partial, 0 );
    while( match ) {
        matches[numMatches++] = Z_CopyString( match );
		if( numMatches == MAX_MATCHES ) {
			(*generator)( partial, 2 );
			return;
		}
        match = (*generator)( partial, 1 );
    }
}

static void Prompt_GenerateMatches( const char *partial ) {
	Prompt_FooGenerator( partial, Cmd_CommandGenerator );
    numCommands = numMatches;
    
	if( numMatches != MAX_MATCHES ) {
		Prompt_FooGenerator( partial, Cvar_Generator );
		numCvars = numMatches - numCommands;

		if( numMatches != MAX_MATCHES ) {
			Prompt_FooGenerator( partial, Cmd_AliasGenerator );
			numAliases = numMatches - numCvars - numCommands;
		}
	}
}

static void Prompt_ShowMatches( commandPrompt_t *prompt, char **matches, int start, int end ) {
    int count = end - start;
    int numCols, numLines;
    int i, j, k, max, len, total;
    int colwidths[6];
    char *match;

    for( numCols = 6; numCols > 0; numCols-- ) {
        numLines = ( count + numCols - 1 ) / numCols;
        total = 0;
        for( i = 0; i < numCols; i++ ) {
            k = start + numLines * i;
			if( k >= end ) {
				break;
			}
            max = -9999;
            j = k;
            while( j - k < numLines && j < end ) {
                len = strlen( matches[j++] );
                if( max < len ) {
                    max = len;
                }
            }
            colwidths[i] = max;
            total += max + 2;
        }
        if( total < prompt->widthInChars ) {
            break;
        }
    }

    for( i = 0; i < numLines; i++ ) {
        for( j = 0; j < numCols; j++ ) {
            k = start + j * numLines + i;
            if( k >= end ) {
                break;
            }
            match = matches[k];
            prompt->Printf( "%s", match );
            len = colwidths[j] - strlen( match );
            for( k = 0; k < len + 2; k++ ) {
				prompt->Printf( " " );
            }
        }
        prompt->Printf( "\n" );
    }
    
}

static void Prompt_ShowIndividualMatches( commandPrompt_t *prompt ) {
	int offset = 0;

	if( numCommands ) {
		qsort( matches + offset, numCommands,
				sizeof( matches[0] ), SortStrcmp );

		prompt->Printf( "\n" S_COLOR_YELLOW "%i possible command%s:\n",
			numCommands, ( numCommands % 10 ) != 1 ? "s" : "" );

        Prompt_ShowMatches( prompt, matches, offset, offset + numCommands );
		offset += numCommands;
	}

	if( numCvars ) {
		qsort( matches + offset, numCvars,
				sizeof( matches[0] ), SortStrcmp );

		prompt->Printf( "\n" S_COLOR_YELLOW "%i possible variable%s:\n",
				numCvars, ( numCvars % 10 ) != 1 ? "s" : "" );

        Prompt_ShowMatches( prompt, matches, offset, offset + numCvars );
		offset += numCvars;
	}

	if( numAliases ) {
		qsort( matches + offset, numAliases,
				sizeof( matches[0] ), SortStrcmp );

		prompt->Printf( "\n" S_COLOR_YELLOW "%i possible alias%s:\n",
				numAliases, ( numAliases % 10 ) != 1 ? "es" : "" );

        Prompt_ShowMatches( prompt, matches, offset, offset + numAliases );
		offset += numAliases;
	}
}

/*
====================
Prompt_CompleteCommand
====================
*/
void Prompt_CompleteCommand( commandPrompt_t *prompt, qboolean backslash ) {
	inputField_t *inputLine = &prompt->inputLine;
	char *text, *partial, *prev;
	int i, argc, pos, currentArg, size, length;
	char *first, *last;
	xgenerator_t generator;

	text = inputLine->text;
	size = sizeof( inputLine->text );
	pos = inputLine->cursorPos;
	if( backslash ) {
		if( inputLine->text[0] != '\\' && inputLine->text[0] != '/' ) {
			memmove( inputLine->text + 1, inputLine->text, sizeof( inputLine->text ) - 1 );
			inputLine->text[0] = '\\';
		}
		text++; size--; pos--;
	}
	
	Cmd_TokenizeString( text, qfalse );

	argc = Cmd_Argc();

	currentArg = Cmd_FindArgForOffset( pos );
	i = strlen( text );
	if( i != 0 && text[ i - 1 ] == ' ' ) {
		if( currentArg == argc - 1 ) {
			currentArg++;
		}
	}
	partial = Cmd_Argv( currentArg );
	if( *partial == ';' ) {
		currentArg++;
		partial = Cmd_Argv( currentArg );
	}

    generator = NULL;
	prev = Cmd_Argv( currentArg - 1 );
	if( currentArg && *prev != ';' ) {
		generator = Cmd_FindGenerator( prev );
		if( generator ) {
			Prompt_FooGenerator( partial, generator );
		}
	} else {
		Prompt_GenerateMatches( partial );
	}

	if( !numMatches ) {
		inputLine->cursorPos = strlen( inputLine->text );
		return; /* nothing found */
	}

	pos = Cmd_ArgOffset( currentArg );
	text += pos;
	size -= pos;
	
	if( numMatches == 1 ) {
		/* we have finished completion! */
		*text = 0;
		Q_strcat( inputLine->text, sizeof( inputLine->text ), matches[0] );
		Q_strcat( inputLine->text, sizeof( inputLine->text ), " " );
		Q_strcat( inputLine->text, sizeof( inputLine->text ), Cmd_RawArgsFrom( currentArg + 1 ) );
		inputLine->cursorPos = strlen( inputLine->text );
		inputLine->selectStart = inputLine->cursorPos;
		inputLine->selectEnd = inputLine->cursorPos;
		Prompt_FreeMatches();
		return;
	}

	/* sort matches alphabethically */
	for( i = 0; i < numMatches; i++ ) {
		sortedMatches[i] = matches[i];
	}
	qsort( sortedMatches, numMatches, sizeof( sortedMatches[0] ), SortStrcmp );

	/* copy matching part */
	first = sortedMatches[0];
	last = sortedMatches[ numMatches - 1 ];
	length = 0;
	do {
        if( *first != *last ) {
            break;
        }
		text[length++] = *first;
		if( length == size - 1 ) {
			break;
		}

		first++;
		last++;
	} while( *first );

	text[length] = 0;

	inputLine->cursorPos = strlen( inputLine->text );
	inputLine->selectStart = inputLine->cursorPos;
	inputLine->selectEnd = inputLine->cursorPos;

	if( currentArg + 1 < argc ) {
		Q_strcat( inputLine->text, sizeof( inputLine->text ), " " );
		Q_strcat( inputLine->text, sizeof( inputLine->text ), Cmd_RawArgsFrom( currentArg + 1 ) );
	}

	prompt->Printf( "]\\%s\n", Cmd_ArgsFrom( 0 ) );
	if( generator ) {
		goto multicoloumn;
	}
    
	switch( com_completion_mode->integer ) {
	case 0:
		/* print in solid list */
		for( i = 0 ; i < numMatches; i++ ) {
			prompt->Printf( "%s\n", sortedMatches[i] ); 
		}
		break;
	case 1:
    multicoloumn:
		/* print in multiple coloumns */
        Prompt_ShowMatches( prompt, sortedMatches, 0, numMatches );
		break;
	case 2:
	default:
		/* resort matches by type and print in multiple coloumns */
		Prompt_ShowIndividualMatches( prompt );
		break;
	}
	
	Prompt_FreeMatches();
}

/*
====================
Prompt_Action

User just pressed enter
====================
*/
char *Prompt_Action( commandPrompt_t *prompt ) {
	int i;
	
	if( !prompt->inputLine.text[0] ||
		( ( prompt->inputLine.text[0] == '/' ||
		    prompt->inputLine.text[0] == '\\' ) &&
		    prompt->inputLine.text[1] == 0 ) )
	{
		/* empty line, do nothing */
		IF_Clear( &prompt->inputLine );
		return NULL;
	}

	/* save current line in history */
	i = prompt->inputLineNum & HISTORY_MASK;
    
	prompt->inputLineNum++;
	prompt->historyLineNum = prompt->inputLineNum;	

	if( prompt->history[i] ) {
		Z_Free( prompt->history[i] );
	}
	
	prompt->history[i] = Z_CopyString( prompt->inputLine.text );
	
	IF_Clear( &prompt->inputLine );
	
	return prompt->history[i];
}

/*
====================
Prompt_HistoryUp
====================
*/
void Prompt_HistoryUp( commandPrompt_t *prompt ) {
    int i;

	if( prompt->historyLineNum == prompt->inputLineNum ) {
		/* save current line in history */
	    i = prompt->inputLineNum & HISTORY_MASK;
        if( prompt->history[i] ) {
            Z_Free( prompt->history[i] );
        }
        prompt->history[i] = Z_CopyString( prompt->inputLine.text );
	}

    if( prompt->inputLineNum - prompt->historyLineNum < HISTORY_SIZE &&
            prompt->historyLineNum > 0 ) 
    {
        prompt->historyLineNum--;
    }


    i = prompt->historyLineNum & HISTORY_MASK;
	IF_Replace( &prompt->inputLine, prompt->history[i] );
}

/*
====================
Prompt_HistoryDown
====================
*/
void Prompt_HistoryDown( commandPrompt_t *prompt ) {
    int i;

	if( prompt->historyLineNum == prompt->inputLineNum ) {
		return;
	}
    
    prompt->historyLineNum++;
    
    i = prompt->historyLineNum & HISTORY_MASK;
	IF_Replace( &prompt->inputLine, prompt->history[i] );
}

/*
====================
Prompt_Clear
====================
*/
void Prompt_Clear( commandPrompt_t *prompt ) {
	int i;
	
	for( i = 0; i < HISTORY_SIZE; i++ ) {
		if( prompt->history[i] ) {
			Z_Free( prompt->history[i] );
			prompt->history[i] = NULL;
		}
	}
	
	prompt->historyLineNum = 0;
	prompt->inputLineNum = 0;
	
	IF_Clear( &prompt->inputLine );
}

/*
====================
Prompt_Init
====================
*/
void Prompt_Init( void ) {
	com_completion_mode = Cvar_Get( "com_completion_mode", "1",
			CVAR_ARCHIVE );
}

