/* * * * * * * * * * * * * * * * 
 * 	 ______     __  __     ______     __  __     ______     ______     __  __    
 *	/\  __ \   /\ \/\ \   /\  == \   /\ \_\ \   /\  __ \   /\  ___\   /\ \/ /    
 *	\ \ \/\ \  \ \ \_\ \  \ \  __<   \ \  __ \  \ \  __ \  \ \ \____  \ \  _"-.  
 *	 \ \_____\  \ \_____\  \ \_____\  \ \_\ \_\  \ \_\ \_\  \ \_____\  \ \_\ \_\ 
 *	  \/_____/   \/_____/   \/_____/   \/_/\/_/   \/_/\/_/   \/_____/   \/_/\/_/ 
 *
 *  - Version : 3L
 *  - By Oubliette
 * * * * * * * * * * * * * * * */

IOubHack = OH_MENU_INTERFACE;
OH_MENU_INTERFACE = nil; // No real need to nil the global, but what the heck, why not..

local OH = {};

/*
	 #                                                 ##        #####                                            
	 #        ####   ####   ####  # #    #  ####      #  #      #     #  ####  #    #  ####   ####  #      ###### 
	 #       #    # #    # #    # # ##   # #    #      ##       #       #    # ##   # #      #    # #      #      
	 #       #    # #      #      # # #  # #          ###       #       #    # # #  #  ####  #    # #      #####  
	 #       #    # #  ### #  ### # #  # # #  ###    #   # #    #       #    # #  # #      # #    # #      #      
	 #       #    # #    # #    # # #   ## #    #    #    #     #     # #    # #   ## #    # #    # #      #      
	 #######  ####   ####   ####  # #    #  ####      ###  #     #####   ####  #    #  ####   ####  ###### ###### 
*/                                                                                                              

OH.Logging = {};

// Throws a error - dark red, priority = high
function OH.Logging.ThrowError( t )

	OH.Logging.Print( OH.Get( "Logging", "log_errors" ), Color( 255, 0, 0, 255 ), "[E] ", t );

end 

// Throws a warning - yellow, priority = medium
function OH.Logging.ThrowWarning( t )

	OH.Logging.Print( OH.Get( "Logging", "log_warnings" ), Color( 255, 0, 255, 255 ), "[W] ", t );

end

// Throws a severe error causing OH to shutdown in 30 seconds, dark red, priority = highest
function OH.Logging.ThrowSevere( t  )

	OH.Logging.Print( OH.Get( "Logging", "log_severes" ), Color( 255, 0, 0, 255 ), "[S] ", t );

end

// Logs text to a file
function OH.Logging.LogToFile( t )

	IOubHack.LogToFile( t .. '\n' );

end 

// Prints to the OubHack console, color, text, color, text, etc ...
function OH.Logging.Print( log, ... )

	local arg = { ... };

	arg[ #arg + 1 ] = '\n';

	OH.Logging.PrintPure( log, Color( 0, 0, 0, 255 ), OH.Logging.GetTimeStamp(), Color( 90, 90, 90 ), unpack( arg ) );

end 

// Print function used by console redirection
function OH.Logging.RedirectPrint( log, ... )

	local arg = { ... };

	arg[ #arg + 1 ] = '\n';

	OH.Logging.PrintPure( log, Color( 0, 0, 0, 255 ), OH.Logging.GetTimeStamp(), Color( 0, 215, 0, 255 ), "[CRedirect] ", Color( 90, 90, 90 ), unpack( arg ) );

end 

// Returns the time stamp
function OH.Logging.GetTimeStamp()

	// Apparently %r crashes .. so this'll have to do
	return "[" .. string.Explode( " ", os.date() )[ 2 ] .. "] ";

end 

// Prints to the console without time stamps or new lines, color, text, color, text, etc ...
function OH.Logging.PrintPure( log, ... )

	local arg = { ... };

	if ( log ) then

		local text = "";

		for _, t in pairs( arg ) do

			if ( isstring( t ) ) then text = text .. t; end

		end 
		OH.Logging.LogToFile( text:sub( 1, text:len() - 1 ), true );

	end
	OH.GUI.ConsolePrint( ... );

end 

OH.Commands = {};

// Generates a random string of a specified length, for timers
function OH.RandomString( len )

	str = "";

	for i = 0, len do 

		str = str .. string.char( math.random(97,122) );

	end

	return str;

end


// Nice function to test if a number is equal to another within a certain range, used with Lerps
function OH.Within( x, y, threshold )

	return math.abs( x - y ) <= threshold;

end

// Usesd to draw invisible panels, if debugging mode is on outlines are drawn
function OH.DrawInvisible( x, y, w, h )

	if ( false ) then 

		surface.SetDrawColor( Color( 0, 0, 0, 255 ) );
		surface.DrawOutlinedRect( x, y, w, h );

		surface.SetDrawColor( Color( 0, 0, 0, 255 ) );
		surface.DrawOutlinedRect( x + 3, y + 3, w - 6, h - 6 );

	end

end

OH.FormatFunctions = {
	["%rand10"] = function() return OH.RandomString( 10 ); end,
	["%rand25"] = function() return OH.RandomString( 25 ); end,
	["%rand100"] = function() return OH.RandomString( 100 ); end,
	["%rand255"] = function() return OH.RandomString( 255 ); end
}

// Formats a string's wildcards
function OH.FormatWildcards( str )

	for needle, replace in pairs( OH.FormatFunctions ) do 

		str = string.Replace( str, needle, replace() );

	end

	return str;

end


// Loads a config safely returning true if it was loaded and false if it wasn't
function OH.SafeLoadConfig( name )

	local struct = util.KeyValuesToTable( IOubHack.RawRead( "OubHack/configs/" .. name .. ".conf" ) );
	local newStruct = {};


	// Convert regions to their original character case

	for region, data in pairs( struct ) do 

		if ( region == "aimbot" ) then region = "Aimbot"; end
		if ( region == "triggerbot" ) then region = "Triggerbot"; end
		if ( region == "lasersights" ) then region = "LaserSights"; end
		if ( region == "lasereyes" ) then region = "LaserEyes"; end
		if ( region == "esp" ) then region = "ESP"; end
		if ( region == "wallhack" ) then region = "Wallhack"; end
		if ( region == "utilities" ) then region = "Utilities"; end
		if ( region == "antiac" ) then region = "AntiAC"; end
		if ( region == "logging" ) then region = "Logging"; end
		if ( region == "console" ) then region = "Console"; end
		if ( region == "gui" ) then region = "GUI"; end
		if ( region == "binds" ) then region = "Binds"; end
		if ( region == "radar" ) then region = "Radar"; end

		newStruct[ region ] = data;


	end

	OH.ConfigurationStructure = OH.FromKeySafe( newStruct );

	return true;

end

/*
	 #####                                                                               #####                                                        
	#     #  ####  #    # ###### #  ####  #    # #####    ##   ##### #  ####  #    #    #     # ##### #####  #    #  ####  #    # ##### #####  ###### 
	#       #    # ##   # #      # #    # #    # #    #  #  #    #   # #    # ##   #    #         #   #    # #    # #    # #    #   #   #    # #      
	#       #    # # #  # #####  # #      #    # #    # #    #   #   # #    # # #  #     #####    #   #    # #    # #      #    #   #   #    # #####  
	#       #    # #  # # #      # #  ### #    # #####  ######   #   # #    # #  # #          #   #   #####  #    # #      #    #   #   #####  #      
	#     # #    # #   ## #      # #    # #    # #   #  #    #   #   # #    # #   ##    #     #   #   #   #  #    # #    # #    #   #   #   #  #      
	 #####   ####  #    # #      #  ####   ####  #    # #    #   #   #  ####  #    #     #####    #   #    #  ####   ####   ####    #   #    # ###### 
*/

OH.Keys = {
	
	{ "A", KEY_A, "." }, { "B", KEY_B, "." }, { "C", KEY_C, "." }, { "D", KEY_D, "." }, { "E", KEY_E, "." }, { "F", KEY_F, "." },
	{ "G", KEY_G, "." }, { "H", KEY_H, "." }, { "I", KEY_I, "." }, { "J", KEY_J, "." }, { "K", KEY_K, "." }, { "L", KEY_L, "." },
	{ "M", KEY_M, "." }, { "N", KEY_N, "." }, { "O", KEY_O, "." }, { "P", KEY_P, "." }, { "Q", KEY_Q, "." }, { "R", KEY_R, "." },
	{ "S", KEY_S, "." }, { "T", KEY_T, "." }, { "U", KEY_U, "." }, { "V", KEY_V, "." }, { "W", KEY_W, "." }, { "X", KEY_X, "." },
	{ "Y", KEY_Y, "." }, { "Z", KEY_Z, "." }, { "1", KEY_1, "." }, { "2", KEY_2, "." }, { "3", KEY_3, "." }, { "4", KEY_4, "." },
	{ "5", KEY_5, "." }, { "6", KEY_6, "." }, { "7", KEY_7, "." }, { "8", KEY_8, "." }, { "9", KEY_9, "." }, { "0", KEY_0, "." }, 

	{ ",", KEY_COMMA, "." }, { ".", KEY_PERIOD, "." }, { "/", KEY_SLASH, "." }, { ";", KEY_SEMICOLON, "." },
	{ "'", KEY_APOSTROPHE, "." }, { "\\", KEY_BACKSLASH, "." }, { "[", KEY_LBRACKET, "." }, { "]", KEY_RBRACKET, "." },
	{ "rclick", MOUSE_RIGHT, "." }, { "lclick", MOUSE_LEFT, "." }, { "rctrl", KEY_RCONTROL, "." },
	{ "lctrl", KEY_LCONTROL, "." }, { "ralt", KEY_LALT, "." }, { "lalt", KEY_RALT, "." },

	{ "lshift", KEY_LSHIFT, "." }, { "rshift", KEY_RSHIFT, "." }, { "enter", KEY_ENTER, "." },
	{ "up", KEY_UP, "." }, { "down", KEY_DOWN, "." }, { "left", KEY_LEFT, "." }, { "right", KEY_RIGHT, "." },
	{ "tab", KEY_TAB, "." }, { "backspace", KEY_BACKSPACE, "." }, { "equal", KEY_EQUAL, "." }, { "minus", KEY_MINUS, "." },
	{ "F1", KEY_F1, "." }, { "F2", KEY_F2, "." }, { "F3", KEY_F3, "." }, { "F4", KEY_F4, "." }, { "F5", KEY_F5, "." }, { "F6", KEY_F6, "." },

	{ "F7", KEY_7, "." }, { "F8", KEY_F8, "." }, { "F9", KEY_F9, "." }, { "F10", KEY_F10, "." }, { "F11", KEY_F11, "." }, { "F12", KEY_F12, "." },
	{ "scrlock", KEY_SCROLLLOCK, "." }, { "space", KEY_SPACE, "." }, { "insert", KEY_INSERT, "." }, { "del", KEY_DELETE, "." }
	
};

//  

OH.ConfigurationStructure = {
	
	[ "Aimbot" ] = {

		friends = {},

		enabled = true,

		trace_key = IN_ATTACK,
		trace_bone = "ValveBiped.Bip01_Head1",
		trace_fraction = 0.95,

		target_players = true,
		target_bots = true,
		target_steam_friends = true,
		target_vehicles = false,
		target_locking = false, 
		target_admins = false,
		target_super_admins = false,
		target_ownteam = true,
		target_traitors_as_traitor = false,
		target_innocents_as_innocent = false,
		target_detectives_as_innocent = false,

		antisnap_enabled = false,
		antisnap_speed = 0.90,

		check_ammo = true,
		bad_weapons = { ["gmod_tool"] = true },

		prediction_delta = 0.90,

		sa_enabled = true,
		sa_smooth = true,

		offset = 0

	},
	[ "Triggerbot" ] = {

		FIRE_WHEN_VISISBLE = 0x01,
		FIRE_WHEN_HOVERED = 0x02,

		enabled = false,

		mode = 0x01,

		delay = 0,

	},
	[ "LaserSights" ] = {

		enabled = true,

		draw_all = false,
		draw_visible = true,
		draw_target = true,
		draw_idle = true,

		length = 0,
		width = 10,

		all_colour = Color( 255, 255, 0, 255 ),
		visible_colour = Color( 0, 0, 255, 255 ),
		target_colour = Color( 255, 0, 0, 255 ),
		idle_colour = Color( 0, 255, 0, 255 ),

		check_ab_compat = true


	},
	[ "LaserEyes" ] = {

		ignorez = false,
		colour = Color( 255, 0, 0, 255 ),
		length = 500

	},
	[ "ESP" ] = {

		enabled = true,

		check_ab_compat = false,

		draw_mat = "wireframe", // Wireframe or solidbg
		draw_always = false,

		ntarget_colour = Color( 255, 255, 0, 255 ),
		friend_colour = Color( 0, 255, 0, 255 ),
		target_colour = Color( 255, 0, 0, 255 ),
		non_ent_colour = Color( 0, 0, 255, 255 ),

		max_distance = 0,

	},
	[ "Wallhack" ] = {

		reg = 0x05,

		enabled = true,

		check_ab_compat = false,

		width = 1,

		ntarget_colour = Color( 255, 255, 0, 255 ),
		friend_colour = Color( 0, 255, 0, 255 ),
		target_colour = Color( 255, 0, 0, 255 ),
		non_ent_colour = Color( 0, 0, 255, 255 ),

		text_font = "default",
		text_colour = Color( 255, 255, 255, 255 ),
		padding_amount = 10,

		max_distance = 0,

	},
	[ "Utilities" ] = {

		reformat_logs = true,
		blocked_files = {},

		dump_files = true,
		dump_runstring = true,
		dump_luacmd = true,

		block_runstring = false,
		block_luacmd = false

	},
	[ "AntiAC" ] = {

		reg = 0x07,

	},
	[ "Logging" ] = {

		log_errors = true,
		log_console = true,
		log_warnings = true,
		log_severes = true,

		lnet_messages = {},
		net_logall = false,

	},
	[ "Console" ] = {

		history_length = 100

	},
	[ "GUI" ] = {

		debug = false,

	},
	[ "Radar" ] = {

		enabled = true,

		size = 200,

		pixrat = 15,

		draw_outlines = true,
		draw_names = true,
		draw_cross = true

	},
	[ "Binds" ] = {

		key_down = table.Copy( OH.Keys ),
		key_up = table.Copy( OH.Keys ),
		key_down_think = table.Copy( OH.Keys ),
		key_up_think = table.Copy( OH.Keys )

	}

};
OH.DefaultConfigurationStructure = OH.ConfigurationStructure;


// Load our default config, save the default to file otherwise.
/*if ( IOubHack.FileExists( "OubHack/configs/default.conf" ) ) then 

	OH.SafeLoadConfig( "default" );

else 

	IOubHack.RawWrite( "OubHack/configs/default.conf", util.TableToKeyValues( OH.KeySafe( OH.ConfigurationStructure ) ), "w" );

end*/

// Nice way of retrieving values from the configuration structure 
function OH.Get( region, name )

	if ( !name ) then return OH.ConfigurationStructure[ region ]; end

	return OH.ConfigurationStructure[ region ][ name ];

end

// Nice way of setting values from the configuration structure 
function OH.Set( region, name, value )

	OH.ConfigurationStructure[ region ][ name ] = value;
	IOubHack.RawWrite( "OubHack/configs/" .. OH.CurrentConfig .. ".conf", util.TableToKeyValues( OH.KeySafe( OH.ConfigurationStructure ) ), "w" );

end

function OH.MatchConfigCase( str )

	for name, config in pairs( OH.ConfigurationStructure ) do 

		if ( name:lower() == str:lower() ) then return name; end

	end

	return str;

end

/*
	#     #  #####  #     # ###    #######                                                 
	#     # #     # #     #  #     #       #      ###### #    # ###### #    # #####  ####  
	#     # #       #     #  #     #       #      #      ##  ## #      ##   #   #   #      
	#     # #  #### #     #  #     #####   #      #####  # ## # #####  # #  #   #    ####  
	 #   #  #     # #     #  #     #       #      #      #    # #      #  # #   #        # 
	  # #   #     # #     #  #     #       #      #      #    # #      #   ##   #   #    # 
	   #     #####   #####  ###    ####### ###### ###### #    # ###### #    #   #    ###
*/

OH.VGUI = {};
OH.VGUI.ElementFactory = {};

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

// Element registration and creation
function OH.VGUI.RegisterElement( name, mtable, base )

	if ( !name || !mtable ) then return false; end

	mtable.Base = base or "Panel";
	OH.VGUI.ElementFactory[ name ] = mtable;

	return OH.VGUI.ElementFactory[ name ];

end

function OH.VGUI.CreateElement( name, parent )

	if ( OH.VGUI.ElementFactory[ name ] ) then 

		local tab = OH.VGUI.ElementFactory[ name ];
		local panel = OH.VGUI.CreateElement( tab.Base, parent );

		table.Merge( panel:GetTable(), tab );

		panel.ClassName, panel.BaseClass = name, OH.VGUI.ElementFactory[ tab.Base ];

		if ( panel.Init ) then 

			local st, err = pcall( panel.Init, panel );
			if ( !st ) then OH.LogginThrowError( "Panel " .. name .. " failed to initialize! - " .. err ); return nil; end

		end

		panel:Prepare(); return panel;

	end 

	return vgui.Create( name, parent );

end

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

local panel = {};

function panel:Init()

	self.colour = Color( 0, 0, 0, 0 );
	self.callback = function() end;
	self.isFaded = true;

end 

function panel:GetColour()

	return self.colour;

end 

function panel:Paint()

	OH.DrawInvisible( 0, 0, self:GetWide(), self:GetTall() );

end

function panel:ColorChanged()

	// Override

end

function panel:Think()

	if ( !self.targetPosX ) then self.targetPosX = self.x; end
	if ( !self.targetPosY ) then self.targetPosY = self.y; end

	// Colour 

	if ( self.isFaded && !OH.Within( self.colour.a, 0, 2 ) ) then 

		self.colour.a = Lerp( 0.70, self.colour.a, 0 );
		panel:ColorChanged( self.colour.a );

	elseif ( !self.isFaded && !OH.Within( self.colour.a, 255, 2 ) ) then 

		self.colour.a = Lerp( 0.70, self.colour.a, 255 );
		panel:ColorChanged( self.colour.a );

	end 

	// Position 

	if ( !OH.Within( self.x, self.targetPosX, 3 ) || !OH.Within( self.y, self.targetPosY, 3 ) ) then 

		self.x = Lerp( 0.70, self.x, self.targetPosX );
		self.y = Lerp( 0.70, self.y, self.targetPosY );

		self:SetPos( self.x, self.y );

		self.canCallCB = true;

	else 

		if ( self.canCallCB ) then self.callback(); end
		self.canCallCB = false;

	end 

end 

function panel:SetCallback( func )

	self.callback = func;

end

function panel:GoTo( x, y )

	self.targetPosX, self.targetPosY = x, y;

end 

function panel:FadeIn()

	self.isFaded = false;

end 

function panel:FadeOut() 

	self.isFaded = true;

end

OH.VGUI.RegisterElement( "OHSlidePanel", panel, "EditablePanel" );

/*
	 #####                                                        #     #                         ###                                                        
	#     # #####    ##   #####  #    # #  ####    ##   #         #     #  ####  ###### #####      #  #    # ##### ###### #####  ######   ##    ####  ###### 
	#       #    #  #  #  #    # #    # # #    #  #  #  #         #     # #      #      #    #     #  ##   #   #   #      #    # #       #  #  #    # #      
	#  #### #    # #    # #    # ###### # #      #    # #         #     #  ####  #####  #    #     #  # #  #   #   #####  #    # #####  #    # #      #####  
	#     # #####  ###### #####  #    # # #      ###### #         #     #      # #      #####      #  #  # #   #   #      #####  #      ###### #      #      
	#     # #   #  #    # #      #    # # #    # #    # #         #     # #    # #      #   #      #  #   ##   #   #      #   #  #      #    # #    # #      
	 #####  #    # #    # #      #    # #  ####  #    # ######     #####   ####  ###### #    #    ### #    #   #   ###### #    # #      #    #  ####  ######
*/

OH.GUI = {};
OH.GUI.ConsoleHistory = {};
OH.GUI.CurrentConsoleHistoryIndex = 1;

function OH.GUI.AppendConsoleHistory( com )

	OH.GUI.ConsoleHistory[ #OH.GUI.ConsoleHistory + 1 ] = com;

	if ( #OH.GUI.ConsoleHistory >= OH.Get( "Console", "history_length" ) ) then table.remove( 1, OH.GUI.ConsoleHistory ); end

end

function OH.GUI.ConsoleOnTextChanged( panel )

	local text = panel:GetValue();
	local sug = "";

	if ( OH.GUI.Console && OH.GUI.Console.SuggestionsList ) then OH.GUI.Console.SuggestionsList:Clear(); end
	OH.GUI.Console.SuggestionsList:SetVisible( false );

	for name, data in pairs( OH.Commands ) do 

		if ( text == "" ) then break; end 

		// Only allow incomplete command names
		if ( name:sub( 1, text:len() ):lower() == text:lower() && text:lower() != name:lower() ) then 

			if ( sug == "" ) then sug = name; end

			if ( OH.GUI.Console && OH.GUI.Console.SuggestionsList ) then 

				OH.GUI.Console.SuggestionsList:AddLine( name );
				OH.GUI.Console.SuggestionsList:SetVisible( true );

			end

		end

	end 

	if ( OH.GUI.Console && OH.GUI.Console.SuggestionOverlay && sug != "" ) then 

		OH.GUI.Console.SuggestionOverlay:SetText( OH.MatchCase( sug, text ) );
		OH.GUI.Console.SuggestionsList:SetVisible( true ); 

	elseif ( OH.Commands[ string.Explode( " ", text )[ 1 ] ] ) then 

		local command = string.Explode( " ", text )[ 1 ];

		local parsed = false;

		for _, data in pairs( OH.ParseCommands( text ) ) do 

			if ( data.name == command ) then 

				parsed = data.args;
				break;

			end

		end

		if ( parsed ) then 

			local sug, sugs =  OH.AutoCompleteCommand( string.Explode( " ", text )[ 1 ], command, parsed, text );

			if ( sugs && #sugs <= 0 ) then 

				OH.GUI.Console.SuggestionsList:SetVisible( false );

			elseif ( sugs ) then

				OH.GUI.Console.SuggestionsList:SetVisible( true );
				for _, data in pairs( sugs ) do 

					OH.GUI.Console.SuggestionsList:AddLine( data.display );
					OH.GUI.Console.SuggestionsList.Overrides = OH.GUI.Console.SuggestionsList.Overrides or {};
					OH.GUI.Console.SuggestionsList.Overrides[ data.display ] = data.text;

				end

			end


			OH.GUI.Console.SuggestionOverlay:SetText( sug );

		else // Should never happen

			OH.GUI.ConsoleErrorFlash();
			OH.LogginPrint( OH.Get( "Logging", "log_errors" ) , Color( 225, 0, 0, 255 ), "Internal command auto-complete parse error!" );

		end

	else 

		OH.GUI.Console.SuggestionsList:SetVisible( false );
		OH.GUI.Console.SuggestionOverlay:SetText("");

	end

end

function OH.GUI.ConsoleOnKey( panel, key )

	if ( key == KEY_UP || key == KEY_DOWN ) then 

		local inc = ( key == KEY_UP and -1 or 1 );
		local val = OH.GUI.CurrentConsoleHistoryIndex;

		if ( OH.GUI.ConsoleHistory[ val + inc ] ) then

			OH.GUI.CurrentConsoleHistoryIndex = val + inc; 

			panel:SetText( OH.GUI.ConsoleHistory[ OH.GUI.CurrentConsoleHistoryIndex ] );
			panel:SetCaretPos( panel:GetValue():len() );

		else 

			OH.GUI.ConsoleErrorFlash();

		end

	elseif ( key == KEY_ESCAPE ) then 

		OH.GUI.ConsoleClose();

	elseif ( key == KEY_ENTER ) then 

		OH.GUI.ConsoleOnCommand( panel );

	elseif ( key == KEY_TAB ) then 

		if ( OH.GUI.Console.SuggestionOverlay && OH.GUI.Console.SuggestionOverlay:GetText() != "" ) then 

			panel:SetText( OH.GUI.Console.SuggestionOverlay:GetText() );
			OH.GUI.ConsoleOnTextChanged( panel );

		end

		// For some reason pressing tab loses focus, let's give it back to us here
		timer.Simple( 0, function() 

			panel:RequestFocus();
			panel:SetCaretPos( isstring( panel:GetValue() ) and panel:GetValue():len() or 1 );

		end);

	end 

end

function OH.GUI.ConsoleOnCommand( panel )

	local com = panel:GetValue();
	
	OH.GUI.AppendConsoleHistory( com );
	OH.GUI.CurrentConsoleHistoryIndex = #OH.GUI.ConsoleHistory + 1;

	timer.Simple( 0, function() panel:RequestFocus(); panel:SetText(""); end );

	OH.ExecuteCommands( OH.ParseCommands( com ) );

end

function OH.GUI.ConsoleCreate()

	// Console back panel, never "visible" - there's no drawing done here
	OH.GUI.Console = OH.VGUI.CreateElement( "DPanel" )

	OH.GUI.Console:SetPos( ScrW() * 0.50, 0 );
	OH.GUI.Console:SetSize( ScrW() * 0.50, ScrH() );

	OH.GUI.Console:SetVisible( false );
	OH.GUI.Console.Paint = function( panel ) OH.DrawInvisible( 0, 0, panel:GetWide(), panel:GetTall() ) end;

	// Console parent, used as a nice way to achieve the slide effect
	OH.GUI.Console.SlidePanel = OH.VGUI.CreateElement( "OHSlidePanel" );
	OH.GUI.Console.SlidePanel:SetSize( OH.GUI.Console:GetWide() - 200, OH.GUI.Console:GetTall() - 200 );
	OH.GUI.Console.SlidePanel:SetVisible( false );

	// The back of the rich text panel
	OH.GUI.Console.RichTextBack = OH.VGUI.CreateElement( "DPanel", OH.GUI.Console.SlidePanel );

	OH.GUI.Console.RichTextBack:SetPos( 100 + 4, 1 );
	OH.GUI.Console.RichTextBack:SetSize( OH.GUI.Console.SlidePanel:GetWide() - 100, OH.GUI.Console.SlidePanel:GetTall() - 100 - 30 - 2 );

	// Flash panel

	OH.GUI.Console.FlashPanel = OH.VGUI.CreateElement( "DPanel", OH.GUI.Console.SlidePanel )

	OH.GUI.Console.FlashPanel:SetPos( 100 + 4, 1 );
	OH.GUI.Console.FlashPanel:SetSize( OH.GUI.Console.RichTextBack:GetSize() );

	OH.GUI.Console.FlashPanel.Paint = function( panel )

		if ( !panel.fCol ) then panel.fCol = Color( 0, 0, 0, 0 ); end 

		if ( panel.fCol.a > 0 ) then panel.fCol.a = math.Approach( panel.fCol.a, 0, 5 ); end 

		surface.SetDrawColor( panel.fCol );
		surface.DrawRect( 0, 0, panel:GetSize() );

	end

	// Console text handler
	OH.GUI.Console.RichText = OH.VGUI.CreateElement( "RichText", OH.GUI.Console.SlidePanel );

	OH.GUI.Console.RichText:SetPos( 100 + 4, 1 );
	OH.GUI.Console.RichText:SetSize( OH.GUI.Console.RichTextBack:GetSize() );

	OH.GUI.Console.RichText:SetVerticalScrollbarEnabled( true );

	// Text entry
	OH.GUI.Console.TextEntry = OH.VGUI.CreateElement( "DTextEntry", OH.GUI.Console.SlidePanel );

	OH.GUI.Console.TextEntry:SetPos( 100 + 4 , 1 + OH.GUI.Console.RichText:GetTall() + 2 );
	OH.GUI.Console.TextEntry:SetSize( OH.GUI.Console.RichText:GetWide() - 8, 30 );

	OH.GUI.Console.TextEntry:SetEnterAllowed( true );
	OH.GUI.Console.TextEntry:SetEditable( true );

	OH.GUI.Console.TextEntry.OnTextChanged = OH.GUI.ConsoleOnTextChanged;
	OH.GUI.Console.TextEntry.OnKeyCodeTyped = OH.GUI.ConsoleOnKey;
	OH.GUI.Console.TextEntry:SetFont( "default" )

	// Suggestion overlay
	OH.GUI.Console.SuggestionOverlay = OH.VGUI.CreateElement( "DLabel", OH.GUI.Console.TextEntry );

	OH.GUI.Console.SuggestionOverlay:SetPos( 3, 0 );
	OH.GUI.Console.SuggestionOverlay:SetSize( OH.GUI.Console.TextEntry:GetSize() );

	OH.GUI.Console.SuggestionOverlay:SetTextColor( Color( 130, 130, 130, 150 ) );
	OH.GUI.Console.SuggestionOverlay:SetText( "" );

	OH.GUI.Console.SuggestionOverlay:SetFont( "default" );

	// Suggestions list
	OH.GUI.Console.SuggestionsList = OH.VGUI.CreateElement( "DListView", OH.GUI.Console.SlidePanel );

	OH.GUI.Console.SuggestionsList:SetPos( 0, 1 );
	OH.GUI.Console.SuggestionsList:SetSize( 100, OH.GUI.Console.SlidePanel:GetTall() - 100 );
	OH.GUI.Console.SuggestionsList:AddColumn( "Name" );

	OH.GUI.Console.SuggestionsList:SetMultiSelect( false );
	OH.GUI.Console.SuggestionsList:SetVisible( false );

	OH.GUI.Console.SuggestionsList.OnRowSelected = function( panel, line )

		if ( OH.GUI.Console && OH.GUI.Console.SuggestionOverlay ) then 

			local text = panel:GetLine( line ):GetValue( 1 );

			if ( panel.Overrides && panel.Overrides[ text ] ) then text = panel.Overrides[ text ]; end

			OH.GUI.Console.SuggestionOverlay:SetText( text ); 

		end

	end

	OH.GUI.Console.SuggestionsList.DoDoubleClick = function( panel, line )

		if ( OH.GUI.Console && OH.GUI.Console.TextEntry ) then 

			local text = panel:GetLine( line ):GetValue( 1 );

			if ( panel.Overrides && panel.Overrides[ text ] ) then text = panel.Overrides[ text ]; end

			OH.GUI.Console.TextEntry:SetText( text ); 
			OH.GUI.Console.TextEntry:OnTextChanged();
			
		end

	end

end 

function OH.GUI.ConsoleErrorFlash()

	if ( !OH.GUI.Console || !OH.GUI.Console.FlashPanel ) then return; end

	OH.GUI.Console.FlashPanel.fCol = Color( 255, 0, 0, 255 );

end

function OH.GUI.ConsolePrint( ... )

	local arg = { ... };

	if ( !arg[ 1 ] ) then return false; end

	if ( OH.GUI.Console && OH.GUI.Console.RichText && IsValid( OH.GUI.Console.RichText ) ) then 

		for _, t in pairs( arg ) do 

			if ( isstring( t ) ) then 

				OH.GUI.Console.RichText:AppendText( t );

			elseif ( istable( t ) ) then 

				OH.GUI.Console.RichText:InsertColorChange( t.r, t.g, t.b, t.a );

			end

		end

	else 

		timer.Simple( 0.1, function( ... ) OH.GUI.ConsolePrint( ... ) end );

	end


end

function OH.GUI.ConsoleOpen()

	if ( !IsValid( OH.GUI.Console ) ) then OH.GUI.ConsoleCreate(); end

	OH.GUI.Console:SetVisible( true );
	OH.GUI.Console.SlidePanel:SetVisible( true );

	OH.GUI.Console.SlidePanel:SetCallback( function() end );
	OH.GUI.Console.SlidePanel:MakePopup();

	OH.GUI.Console.SlidePanel:FadeIn();
	OH.GUI.Console.SlidePanel:SetPos( ScrW() - 1, 100 );
	OH.GUI.Console.SlidePanel:GoTo( ( ScrW() * 0.50 ) + 100.0, 100.0 );

	OH.GUI.Console.TextEntry:RequestFocus();

end

function OH.GUI.ConsoleClose()

	if ( !IsValid( OH.GUI.Console ) ) then OH.GUI.ConsoleCreate(); end

	OH.GUI.Console:SetVisible( true );

	OH.GUI.Console.SlidePanel:SetCallback( function() OH.GUI.Console:SetVisible( false ); OH.GUI.Console.SlidePanel:SetVisible( false ); end );

	OH.GUI.Console.SlidePanel:FadeOut();
	OH.GUI.Console.SlidePanel:GoTo( ScrW() - 1.0, 100.0 );

end

function OH.GUI.ConsoleToggle()

	if ( !IsValid( OH.GUI.Console ) ) then OH.GUI.ConsoleCreate(); end
	OH.GUI[ "Console" .. ( OH.GUI.Console:IsVisible() and "Close" or "Open" ) ]();
end 

//OH.GUI.ConsoleCreate(); OH.GUI.ConsoleToggle();

concommand.Add( "tits", OH.GUI.ConsoleToggle )

/*

	#     #      ##        #####   #####     ###                                                        
	#     #     #  #      #     # #     #     #  #    # ##### ###### #####  ######   ##    ####  ###### 
	#     #      ##       #       #           #  ##   #   #   #      #    # #       #  #  #    # #      
	#######     ###       #       #           #  # #  #   #   #####  #    # #####  #    # #      #####  
	#     #    #   # #    #       #           #  #  # #   #   #      #####  #      ###### #      #      
	#     #    #    #     #     # #     #     #  #   ##   #   #      #   #  #      #    # #    # #      
	#     #     ###  #     #####   #####     ### #    #   #   ###### #    # #      #    #  ####  ###### 

*/

OH.Commands = {};
OH.Redirects = {};

OH.Redirects[ "print" ] = print;
OH.Redirects[ "Msg" ] = Msg;
OH.Redirects[ "MsgN" ] = MsgN;
OH.Redirects[ "MsgC" ] = MsgC;

function OH.InitiateRedirects()

	_G.print = function( ... ) OH.Logging.RedirectPrint( OH.Get( "Logging", "log_console" ), ... ); end
	_G.Msg = function( ... ) OH.Logging.RedirectPrint( OH.Get( "Logging", "log_console" ), ... ); end
	_G.MsgN = function( ... ) OH.Logging.RedirectPrint( OH.Get( "Logging", "log_console" ), ... ); end
	_G.MsgC = function( ... ) OH.Logging.RedirectPrint( OH.Get( "Logging", "log_console" ), ... ); end

end

function OH.RemoveRedirects()

	_G.print = OH.Redirects[ "print" ];
	_G.Msg = OH.Redirects[ "Msg" ];
	_G.MsgN = OH.Redirects[ "MsgN" ];
	_G.MsgC = OH.Redirects[ "MsgC" ];

end

function OH.AddHook( name, func )

	OH.Hooks[ name ] = func;
	//_G.hook.Add( name, OH.RandomString( 200 ), func );

end

function OH.AddCmd( name, func, writeFunc )

	writeFunc = writeFunc or function() return ""; end

	OH.Commands[ name ] = { execute = func, onType = writeFunc };

end

function OH.ExecuteCommand( name, ... )

	OH.InitiateRedirects();
	timer.Simple( 0, OH.RemoveRedirects );

	if ( !OH.Commands[ name ] ) then return nil; end
	return OH.Commands[ name ].execute( ... );

end 

function OH.AutoCompleteCommand( name, ... )

	if ( !OH.Commands[ name ] ) then return ""; end 

	return OH.Commands[ name ].onType( ... );

end

function OH.ExecuteCommands( commands )

	for _, data in pairs( commands ) do 

		local targs = {};

		local name = data.name;
		local args = data.args;

		for _, arg in pairs( args ) do targs[ arg ] = true; end

		// Only run this part if it was typed into the console rather than a bind, as binds use this function too.
		if ( OH.GUI.Console && OH.GUI.Console:IsVisible() ) then OH.Logging.Print( OH.Get( "Logging", "log_console" ), "] " .. name ); end

		local result, err = OH.ExecuteCommand( name:lower(), args, targs );

		if ( OH.GUI.Console && OH.GUI.Console:IsVisible() ) then 

			if ( result == nil ) then 

				OH.Logging.Print( OH.Get( "Logging", "log_console" ), Color( 255, 0, 0, 225 ), "] No such command '" .. name .. "'!" );
				OH.GUI.ConsoleErrorFlash();

			elseif ( !result ) then

				OH.GUI.ConsoleErrorFlash();
				if ( err ) then 

					OH.Logging.Print( OH.Get( "Logging", "log_console" ), Color( 255, 0, 0, 225 ), "] ERROR: " .. err );

				else 

					OH.Logging.Print( OH.Get( "Logging", "log_console" ), Color( 255, 0, 0, 225 ), "] ERROR: Unknown error!" );

				end

			end

		end

	end

end

function OH.ParseCommands( com ) 

	// parse the command

	local isEncap = false;
	local commands = {};

	com = OH.FormatWildcards( com ) .. " ";

	local name, _name, arg = "", "", "";

	for i = 1, com:len() do 

		if ( com[ i ] == "\"" ) then 

			// Check if there's another double quote before encapsulating the input 
			if ( !string.find( com, "\"", i + 1 ) && !isEncap ) then continue; end
			isEncap = !isEncap;

		elseif ( com[ i ] == ";" && !isEncap ) then 

			// name wasn't "finished" 
			if ( name == "" ) then commands[ #commands + 1 ] = { name = _name, args = {} }; end

			// Insert current argument 
			if ( arg != "" ) then 

				commands[ #commands ].args[ #commands[ #commands ].args + 1 ] = arg;

				// Clear arg for next command
				arg = "";

			end

			// Establish a command name again
			name, _name = "", "";


		elseif ( com[ i ] == " " && !isEncap ) then 

			if ( name == "" ) then 

				name = _name; 
				commands[ #commands + 1 ] = { name = name, args = {} };

			else 

				commands[ #commands ].args[ #commands[ #commands ].args + 1 ] = arg;
				arg = "";

			end


		else 

			if ( name == "" ) then 

				_name = _name .. com[ i ];

			else 

				arg = arg .. com[ i ];

			end 

		end

	end

	return commands;

end
