
include "string", "re", "math";


function ConUtil_MatchRank( word, comp )
{
	comp = re_replace( comp, "#[^a-zA-Z-]#", "" );
	rcomp = string_explode( comp, "" );
	rcomp = "#" $ string_implode( rcomp, ".*?" ) $ "#i";
	if( re_match( word, rcomp ) )
	{
		length_rank = 1 / word.length;
		return 1 + length_rank;
	}
	return 0;
}

function ConUtil_ArgUnpack( params, argc )
{
	params = array_filter( string_explode( params, " " ) );
	params.resize( argc );
	return params;
}


global Console =
{
	CR = 13,
	BS = 8,
	SC_PREVIOUS = 1,
	SC_NEXT = 2,
	SC_FINISH = 3,
};

function Console.create()
{
	data =
	{
		commands = {},
		buffer = [],
		autocomplete = [],
		history = [],
		history_index = 0,
		history_limit = 50,
	};
	con = class( data, Console );
	con.add_commands
	({
		"list" = function()use(con){ con.list_commands(); },
	});
	return con;
}

function Console.add_commands( cmds )
{
	this.commands = get_merged( this.commands, cmds );
}

function Console.list_commands()
{
	foreach( cmd ,: this.commands )
	{
		Console.print( cmd );
	}
}

function Console.print( text )
{
	println( text );
}

function Console.commit()
{
	if( this.history_index )
		cmd = this.history[ this.history.size + this.history_index ];
	else
	{
		cmd = string_utf8_encode( this.buffer );
		if( this.autocomplete.size )
			cmd = this.ac_apply();
	}
	
	if( !cmd.length )
		return;
	
	while( this.history.size > this.history_limit )
		this.history.shift();
	if( this.history.size == 0 || this.history.last != cmd )
		this.history.push( string_trim( cmd ) );
	
	split = string_explode( cmd, " " );
	action = split.shift();
	params = string_implode( split, " " );
	if( isset( this.commands, action ) )
	{
		Console.print( "> " $ cmd );
		this.commands[ action ]( params, this, cmd );
	}
	else
	{
		Console.print( "Command '"$action$"' was not found" );
	}
	
	this.buffer.clear();
	this.autocomplete.clear();
	this.history_index = 0;
}

function Console.ac_apply( nosp )
{
	id = this.history_index;
	if( id < 0 )
	{
		cmd = this.history[ this.history.size + id ];
		this.buffer = string_utf8_decode( cmd );
		this.history_index = 0;
		this.ac_update( true );
		return cmd;
	}
	
	if( id < 1 )
		id = 1;
	
	cmd = string_utf8_encode( this.buffer );
	if( this.autocomplete.size == 0 )
		return cmd;
	
	cmd = string_trim( cmd, " ", STRING_TRIM_LEFT );
	split = string_explode( cmd, " " );
	split[ 0 ] = this.autocomplete[ id - 1 ];
	cmd = string_implode( split, " " );
	
	this.buffer = string_utf8_decode( if( nosp, cmd, cmd $ " " ) );
	
	this.history_index = 0;
	this.ac_update();
	return cmd;
}

function Console.ac_update( restart )
{
	if( restart )
		this.autocomplete = get_keys( this.commands );
	
	cmd = string_explode( string_trim( string_utf8_encode( this.buffer ) ), " " )[0];
	ranking = [];
	foreach( acid, item : this.autocomplete )
	{
		rank = ConUtil_MatchRank( item, cmd );
		if( !rank )
			this.autocomplete[ acid ] = null;
		ranking.push( rank );
	}
//	printvar(this.autocomplete,ranking);
	this.autocomplete.sort_mapped( ranking, true );
	
	this.autocomplete = array_filter( this.autocomplete );
}

function Console.char_event( code )
{
	if( code == 0 ) ; // invalid code, ignore
	else if( code == Console.BS )
	{
		if( this.history_index != 0 )
			this.ac_apply( true );
		if( this.buffer.size > 0 )
			this.buffer.pop();
		if( this.buffer.size == 0 )
			this.autocomplete.clear();
		else
			this.ac_update( true );
	}
	else if( code == Console.CR )
	{
		this.ac_apply();
		this.commit();
	}
	else
	{
		if( this.history_index != 0 )
			this.ac_apply( true );
		this.buffer.push( code );
		this.ac_update( this.buffer.size == 1 );
	}
}

function Console.special_event( which )
{
	if( which == Console.SC_PREVIOUS )
	{
		this.history_index--;
		if( -this.history_index > this.history.size )
			this.history_index = -this.history.size;
		if( !this.history_index )
			this.autocomplete.clear();
	}
	else if( which == Console.SC_NEXT )
	{
		this.history_index++;
		if( this.history_index > this.autocomplete.size )
			this.history_index = this.autocomplete.size;
		if( !this.history_index )
			this.ac_update( true );
	}
	else if( which == Console.SC_FINISH )
	{
		if( this.autocomplete.size )
			this.ac_apply();
	}
}


function ss_draw_console( con, font, lineheight )
{
	// pick command to display
	if( con.history_index < 0 )
		com2d = con.history[ con.history.size + con.history_index ];
	else if( con.history_index > 0 )
		com2d = con.autocomplete[ con.history_index - 1 ];
	else
		com2d = string_utf8_encode( con.buffer );
	
	// render command
	draw({ preset = "tile", position = [4,5], scale = [9999,lineheight], color = [0,0,0,0.5] });
	draw_text_line( "> " $ com2d $ "_", font, 4, 4, [0.9,0.9,0.9] );
	
	// render autocomplete
	maxcount = 10;
	if( con.autocomplete.size < maxcount )
		maxcount = con.autocomplete.size;
	for( i = 0; i < maxcount; ++i )
	{
		draw({ preset = "tile", position = [36,5 + lineheight * ( i + 1 )], scale = [100,lineheight], color = [0,0,0,0.5] });
		draw_text_line( "- " $ con.autocomplete[ i ], font, 36, 4 + lineheight * ( i + 1 ), [0.5,0.5,0.5] );
	}
}
