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

#include "main.h"

#include "oubhack.h"

#include "oubhack_iluaobject.h"

#include "checksum_md5.h"
#include "baseentity_shared.h"

#include "c_baseentity.h"

#include "vstdlib\random.h"

#include "dirent.h"

using namespace std;

// Needed declararions for NoSpread
ConVar	ai_shot_bias_min( "ai_shot_bias_min", "-1.0", FCVAR_REPLICATED );
ConVar	ai_shot_bias_max( "ai_shot_bias_max", "1.0", FCVAR_REPLICATED );

// Macros
#define LUA oubhack->GetLuaInterface( LUA_STATE_CLIENT )

#pragma comment (lib, "lua51.lib")

extern "C" {

#include "lua.h"
#include "lauxlib.h"

}

/* ===============================
		Name : LoadDefaultConfig
   =============================== */

// Loads OubHack/Configs/Default - Saves passed table if it doesn't exist in the reg
LUA_FUNCTION( FileExists ){

	LUA->CheckType( 1, GLua::TYPE_STRING );

	const char* ccPath = LUA->GetString( 1 );

	LUA->PushBool( oubhack->m_pFileIO->FileExists( ccPath ) );

	return 1;
	 
}

/* ===============================
		Name : LoadConfig
   =============================== */

LUA_FUNCTION( RawRead ){

	LUA->CheckType( 1, GLua::TYPE_STRING );

	const char* ccPath = LUA->GetString( 1 );

	LUA->PushString( oubhack->m_pFileIO->ReadFile( ccPath ).c_str() );

	return 1;

}

/* ===============================
		Name : RawWrite
   =============================== */

LUA_FUNCTION( RawWrite ){

	LUA->CheckType( 1, GLua::TYPE_STRING );
	LUA->CheckType( 2, GLua::TYPE_STRING );
	LUA->CheckType( 3, GLua::TYPE_STRING );

	const char* ccPath = LUA->GetString( 1 );
	const char* ccContents = LUA->GetString( 2 );
	const char* ccMode = LUA->GetString( 3 );

	FILE * file;

	if ( (file = fopen ( ccPath, ccMode ) ) != NULL )
	{

		fprintf( file, ccContents );

		fclose (file);

	}

	return 0;

}

/* ===============================
		Name : WriteDumpFile
   =============================== */

LUA_FUNCTION( WriteDumpFile ){

	LUA->CheckType( 1, GLua::TYPE_STRING );
	LUA->CheckType( 2, GLua::TYPE_STRING );

	const char* ccPath = LUA->GetString( 1 );
	const char* ccString = LUA->GetString( 2 );

	oubhack->m_pFileIO->FileDump( ccPath, ccString );

	return 0;

}

/* ===============================
		Name : CreateDir
   =============================== */

LUA_FUNCTION( CreateDir ){

	LUA->CheckType( 1, GLua::TYPE_STRING );

	oubhack->m_pFileIO->CreateDirectories( LUA->GetString( 1 ) );

	return 0;

}

/* ===============================
		Name : GetFiles
   =============================== */

LUA_FUNCTION( GetFiles ){

	LUA->CheckType( 1, GLua::TYPE_STRING );

	const char* ccPath = LUA->GetString( 1 );

	DIR *dir;
	struct dirent *ent;

	LUA->CreateTable();
	ILuaObject* obj = LUA->IGetObject();

	if ( (dir = opendir( ccPath )) != NULL ){

		while ( (ent = readdir( dir ) ) != NULL ){

			std::string sPath = ccPath;
			sPath += "/";
			sPath += ent->d_name;

			if (oubhack->m_pFileIO->FileExists(sPath.c_str())){

				obj->SetMember(ent->d_name, true);

			}

		}

		closedir( dir );

	}

	obj->Push();

	return 1;

}

/* ===============================
		Name : GetDirectories
   =============================== */

LUA_FUNCTION( GetDirectories ){

	LUA->CheckType( 1, GLua::TYPE_STRING );

	const char* ccPath = LUA->GetString( 1 );

	DIR *dir;
	struct dirent *ent;

	LUA->CreateTable();
	ILuaObject* obj = LUA->IGetObject();

	if ( (dir = opendir( ccPath )) != NULL ){

		while ( (ent = readdir( dir ) ) != NULL ){

			std::string sPath = ccPath;
			sPath += "/";
			sPath += ent->d_name;

			if (!oubhack->m_pFileIO->FileExists(sPath.c_str())){

				obj->SetMember(ent->d_name, true);

			}

		}

		closedir( dir );

	}

	obj->Push();

	return 1;

}

/* ===============================
		Name : SetViewAngles
   =============================== */

LUA_FUNCTION( SetViewAngles ){

	LUA->CheckType( 1, GLua::TYPE_USERCMD );
	LUA->CheckType( 2, GLua::TYPE_ANGLE );
	
	CUserCmd* cmd = (CUserCmd*)LUA->GetUserdata( 1 );
	
	cmd->viewangles = oubhack->m_pLuaLibrary->GetAngle( LUA, 2 );

	return 0;

}

/* ===============================
		Name : SetEyeAngles
   =============================== */

LUA_FUNCTION( SetEyeAngles ){

	LUA->CheckType( 1, GLua::TYPE_ANGLE );
	
	oubhack->Interfaces.pClient->SetViewAngles( oubhack->m_pLuaLibrary->GetAngle( LUA, 1 ) ); 

	return 0;

}

/* ===============================
		Name : RunConsoleCommand
   =============================== */

LUA_FUNCTION( RunConsoleCommand ){

	LUA->CheckType( 1, GLua::TYPE_STRING );

	oubhack->Interfaces.pClient->ClientCmd( LUA->GetString( 1 ) );

	return 0;

}

/* ===============================
		Name : GetCommandNumber
   =============================== */

LUA_FUNCTION( GetCommandNumber ){

	LUA->CheckType( 1, GLua::TYPE_USERCMD );
	
	CUserCmd* cmd = (CUserCmd*)LUA->GetUserdata( 1 );

	LUA->PushNumber( (float)cmd->command_number );

	return 1;

}

/* ===============================
		Name : GetSeed
   =============================== */

LUA_FUNCTION( GetSeed ){

	LUA->CheckType( 1, GLua::TYPE_NUMBER );
	
	unsigned int long seed = MD5_PseudoRandom( LUA->GetNumber( 1 ) ) & 0x7fffffff;

	LUA->PushNumber( (float)( seed & 255 ) );

	return 1;

}

/* ===============================
		Name : GetSpread
		Purpose: Fuck off
   =============================== */

LUA_FUNCTION( GetSpread ){

	LUA->CheckType(1, GLua::TYPE_NUMBER);
	LUA->CheckType(2, GLua::TYPE_VECTOR);
	LUA->CheckType(3, GLua::TYPE_VECTOR);

	RandomSeed( LUA->GetNumber( 1 ) );

	Vector vecAim = oubhack->m_pLuaLibrary->GetVector( LUA, 2 ); 

	CShotManipulator shot(vecAim);

	Vector vecSpread = oubhack->m_pLuaLibrary->GetVector( LUA, 3 ); 

	Vector vecResult = shot.ApplySpread(vecSpread);

	Vector *result = new Vector(vecResult);

	oubhack->m_pLuaLibrary->PushVector( LUA, result );

	return 1;

}

/* ===============================
		Name : SetCVar
   =============================== */

LUA_FUNCTION( SetCVar ){

	LUA->CheckType( 1, GLua::TYPE_STRING );

	const char* name = LUA->GetString( 1 );

	double value = LUA->GetNumber( 2 );

	oubhack->m_pCvarInterface->SetCVarValue( name, (float)value );

	return 0;

}

/* ===============================
		Name : SetCVarName
   =============================== */

LUA_FUNCTION( SetCVarName ){

	LUA->CheckType( 1, GLua::TYPE_STRING );
	LUA->CheckType( 2, GLua::TYPE_STRING );

	const char* name = LUA->GetString( 1 );

	const char * newName = LUA->GetString( 2 );

	oubhack->m_pCvarInterface->SetCVarName( name, newName );
		
	return 0;

}

/* ===============================
		Name : NullCVarCallback
   =============================== */

LUA_FUNCTION( NullCVarCallback ){

	LUA->CheckType( 1, GLua::TYPE_STRING );

	const char* ccName = LUA->GetString( 1 );

	oubhack->m_pCvarInterface->NullCallback( ccName );

	return 0;

}

/* ===============================
		Name : LogToFile
   =============================== */

LUA_FUNCTION( LLogToFile ){

	LUA->CheckType( 1, GLua::TYPE_STRING );

	const char* str = LUA->GetString( 1 );

	// Timestamps are handled by the Lua calls
	oubhack->LogToFile( str, false );

	return 0;

}

/* ===============================
		Name : RunString
   =============================== */

LUA_FUNCTION( RunString ){

	const char* ccScript = LUA->GetString( 1 );

	LUA->RunString( "ohrs", "", ccScript, true, true );

	return 0;

}

/* ===============================
		Name : RunStringDynamic
   =============================== */

LUA_FUNCTION( RunStringDynamic ){

	const char* ccScript = LUA->GetString( 1 );

	oubhack->m_pLuaExports->luaL_loadbuffer( L, ccScript, strlen( ccScript ), "ohrs" );

	return 1;

}


/* ===============================
		Name : SetCallback
   =============================== */

LUA_FUNCTION( SetCallback ){

	LUA->CheckType( 1, GLua::TYPE_STRING );
	LUA->CheckType( 2, GLua::TYPE_FUNCTION );

	const char* ccName = LUA->GetString( 1 );
	ILuaObject* func = LUA->IGetObject( 2 );

	oubhack->m_pCHooks->SetHook( ccName, func );

	return 0;

}

/* ===============================
		Name : GetServerIP
   =============================== */

LUA_FUNCTION( GetServerIP ){

	INetChannel* iNet = (INetChannel*)oubhack->m_pClient->GetINetChannelInfo();

	if ( iNet == NULL ){

		LUA->PushNil();
		return 1;

	}

	LUA->PushString( iNet->GetAddress() );

	return 1;

}

/* ===============================
		Name : GetLPIndex
   =============================== */

LUA_FUNCTION( GetLPIndex ){

	LUA->PushNumber( oubhack->Interfaces.pClient->GetLocalPlayer() );

	return 1;

}

/* ===============================
		Name : Disconnect
   =============================== */

LUA_FUNCTION( Disconnect ){

	INetChannel* iNet = (INetChannel*)oubhack->m_pClient->GetINetChannelInfo();

	const char* ccReason = LUA->GetString( 1 );

	if ( iNet == NULL ){

		return 0;

	}

	iNet->Shutdown( ccReason );

	return 0;

}

/* ===============================
		Name : IsDormant
   =============================== */

LUA_FUNCTION( IsDormant ){

	LUA->CheckType( 1, GLua::TYPE_NUMBER );

	int iIndex = LUA->GetNumber();

	IClientEntity* pEnt = oubhack->Interfaces.pClientEList->GetClientEntity( 1 );

	LUA->PushBool( pEnt->IsDormant() );

	return 1;

}

/* ===============================
		Name : IsVisible
   =============================== */

bool bDoTrace( int iIndex, Vector vecStart, Vector vecEnd ){

	if( !oubhack->Interfaces.pClientEList->GetClientEntity( iIndex ) )
		return false;
	
	// Set up ray
	Ray_t ray;
	trace_t trTrace;
	CTraceFilterHitAll filter;

	ray.Init( vecStart, vecEnd );

	// Do a engine ray trace
	oubhack->Interfaces.pEngineTrace->TraceRay( ray, MASK_SHOT, &filter, &trTrace );

	// Check results to see if the player is visible
	if( trTrace.allsolid || trTrace.startsolid )
		return false;

	if( trTrace.m_pEnt && ( trTrace.m_pEnt->index == iIndex ) )
		return true;

	if( trTrace.fraction == 1.0f )
		return true;

	return false;

}

LUA_FUNCTION( IsVisible ){

	// Get variables
	LUA->CheckType( 1, GLua::TYPE_VECTOR );
	LUA->CheckType( 2, GLua::TYPE_VECTOR );
	LUA->CheckType( 3, GLua::TYPE_NUMBER );

	Vector vecStart = oubhack->m_pLuaLibrary->GetVector( LUA, 1 ); 
	Vector vecEnd	= oubhack->m_pLuaLibrary->GetVector( LUA, 2 ); 

	int iIndex = LUA->GetNumber();

	LUA->PushBool( bDoTrace( iIndex, vecStart, vecEnd ) );

	return 1;

}

/* ===============================
		Name : ExecuteConsoleCommand
   =============================== */

LUA_FUNCTION( ExecuteConsoleCommand ){

	LUA->CheckType( 1, GLua::TYPE_STRING );
	LUA->CheckType( 2, GLua::TYPE_TABLE );
	LUA->CheckType( 3, GLua::TYPE_TABLE );

	const char* ccName = LUA->GetString( 1 );
	ILuaObject* args = LUA->IGetObject( 2 );
	ILuaObject* targs = LUA->IGetObject( 2 );

	//oubhack->m_pConsoleCommands->ExecuteConsoleCommand( ccName, LUA, targs );
	
	return 0;

}

/* ===============================
		Name : GetVersion
   =============================== */

LUA_FUNCTION( _GetVersion ){

	LUA->PushNumber( oubhack->m_iVersion );
	
	return 1;

}

/* ===============================
		Name : RunFromCache
   =============================== */

LUA_FUNCTION( RunFromCache ){
	
	LUA->CheckType( 1, GLua::TYPE_STRING );

	const char* ccPath = LUA->GetString( 1 );

	LUA->RunString( "", "ohrs", oubhack->m_pFileIO->ReadDecryptedFile( ccPath ).c_str(), true, true );

	return 0;

}

/* ===============================
		Name : RunFromCacheDynamic
   =============================== */

LUA_FUNCTION( RunFromCacheDynamic ){
	
	LUA->CheckType( 1, GLua::TYPE_STRING );
		 
	const char* ccPath = LUA->GetString( 1 );

	oubhack->m_pLuaExports->luaL_loadbuffer( L, oubhack->m_pFileIO->ReadDecryptedFile( ccPath ).c_str(), oubhack->m_pFileIO->ReadDecryptedFile( ccPath ).size(), "ohrs" );

	return 1;

}

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


void OubHack::InitClientBindings(){

	oubhack->Debug( "Loading client C++ => Lua bindings " );

	LUA->CreateTable();
	LUA->SetField( LUA_GLOBALSINDEX, "OH_CLIENT_INTERFACE" );

	ILuaObject* l_oInterface = this->GetLuaInterface( LUA_STATE_CLIENT )->GetGlobal( "OH_CLIENT_INTERFACE" );

	// == AIMBOT == //
	
	l_oInterface->SetMember( "SetViewAngles", SetViewAngles );
	l_oInterface->SetMember( "SetEyeAngles", SetEyeAngles );

	l_oInterface->SetMember( "GetCommandNumber", GetCommandNumber );
	l_oInterface->SetMember( "GetSeed", GetSeed );
	l_oInterface->SetMember( "GetSpread", GetSpread );

	l_oInterface->SetMember( "IsDormant", IsDormant );
	l_oInterface->SetMember( "IsVisible", IsVisible );
	
	// == CONSOLE / CONSOLE VARIABLES == //
	
	l_oInterface->SetMember( "SetCVar", SetCVar );
	l_oInterface->SetMember( "SetCVarName", SetCVarName );
	l_oInterface->SetMember( "NullCVarCallback", NullCVarCallback );

	l_oInterface->SetMember( "RunConsoleCommand", RunConsoleCommand );

	// == MISC' == //  

	l_oInterface->SetMember( "SetCallback", SetCallback );

	l_oInterface->SetMember( "GetVersion", _GetVersion );

	l_oInterface->SetMember( "GetLPIndex", GetLPIndex );

	l_oInterface->SetMember( "RunFromCache", RunFromCache );
	l_oInterface->SetMember( "RunFromCacheDynamic", RunFromCacheDynamic );

	l_oInterface->SetMember( "RunString", RunString );
	l_oInterface->SetMember( "RunStringDynamic", RunStringDynamic );

	// == FILE OPERATIONS == //

	l_oInterface->SetMember( "FileExists", FileExists );
	l_oInterface->SetMember( "RawRead", RawRead );
	l_oInterface->SetMember( "RawWrite", RawWrite );
	l_oInterface->SetMember( "CreateDir", CreateDir );

	l_oInterface->SetMember( "LogToFile", LLogToFile );

	l_oInterface->SetMember( "WriteDumpFile", WriteDumpFile );

	l_oInterface->SetMember( "GetFiles", GetFiles );
	l_oInterface->SetMember( "GetDirectories", GetDirectories );

	// == NETWORK OPERATIONS == //

	l_oInterface->SetMember( "GetServerIP", GetServerIP );
	l_oInterface->SetMember( "Disconnect", Disconnect );

	// ==					 == //

	this->Log( Color( 0, 200, 100, 255 ), "Lua client bindings initialized!" );

	l_oInterface->UnReference();

}