#include <sourcemod>
#include <cstrike>
#include <smlib>
#include <sdktools>
#include <sdkhooks>
#include <botmimic>

//for recording surfing properly with things such as trigger_pushes should sm_botmimic_snapshotinterval be set to 50
public Plugin myinfo = 
{
        name = "test surfing plugin",
        author = "jenzur",
        description = "hello ",
        version = "1.1",
        url = "www.unloze.com"
};

//is botplayer
bool is_bot_player[MAXPLAYERS + 1];
bool is_player_jenz[MAXPLAYERS + 1];


float bot_finished_surf_coords[MAXPLAYERS + 1][3];
char map_coorindates[65][256];
char map_recordname[65][256];
bool round_switch = false;
Handle g_hTraceTimer = null;
Database g_dDatabase;

public void OnPluginStart()
{
    for (int i = 1; i <= MaxClients; i++)
        if (IsValidClient(i))
        {
            reset_client(i);
            reset_bot_finished_surf(i);
        }
    g_hTraceTimer = CreateTimer(0.30, update_mimic_coords, _, TIMER_REPEAT);
    OnMapStart();
    HookEvent("round_start", Event_RoundStart, EventHookMode_Pre);
    HookEvent("round_end", OnRoundEnd, EventHookMode_Pre);
}

public void OnPluginEnd()
{
    for (int i = 1; i <= MaxClients; i++)
        if (IsValidClient(i))
        {
            reset_client(i);
            reset_bot_finished_surf(i);
        }
    if (g_hTraceTimer != null)
        delete g_hTraceTimer;
}

public Action OnRoundEnd(Handle event, const char[] name, bool dontBroadcast)
{
    perform_resets();
    return Plugin_Handled;
}  

public void Event_RoundStart(Handle event, const char[] name, bool dontBroadcast)
{
	perform_resets();
}

public void perform_resets()
{
    round_switch = true;
    for (int i = 1; i <= MaxClients; i++)
        if (IsValidClient(i))
        {
            reset_client(i);
            reset_bot_finished_surf(i);
        }
    CreateTimer(7.00, reset_round_switch, INVALID_HANDLE);
}

public Action reset_round_switch(Handle timer, any data)
{
    round_switch = false;
    return Plugin_Handled;
}

stock bool IsValidClient(int client)
{
    if (client > 0 && client <= MaxClients && IsClientConnected(client) && IsClientInGame(client))
        return true;
    return false;
}

public void OnClientPostAdminCheck(int client)
{
    is_bot_player[client] = false;
    is_player_jenz[client] = false;
    is_bot_player_call(client);
    reset_bot_finished_surf(client);
}

public void OnClientDisconnect(int client)
{
    is_bot_player[client] = false;
    is_player_jenz[client] = false;
    reset_bot_finished_surf(client);
}

public void reset_bot_finished_surf(int client)
{
    bot_finished_surf_coords[client][0] = 0.0;
    bot_finished_surf_coords[client][1] = 0.0;
    bot_finished_surf_coords[client][2] = 0.0;
}

public void reset_client(int client)
{   
	if (BotMimic_IsPlayerMimicing(client))
    {
        BotMimic_StopPlayerMimic(client);
        if (GetClientTeam(client) == CS_TEAM_T)
        {
            Client_switch_to_knife(client);
        }
    }
}

public int BotMimic_OnPlayerMimicLoops(int client)
{
    char map_name[256];
    GetCurrentMap(map_name, sizeof(map_name));
    if (StrContains(map_name, "ze_surf", false) == -1)
    {
        reset_client(client);
        reset_bot_finished_surf(client);
    }
    else if (bot_finished_surf_coords[client][0] != 0.0)
    {
        reset_client(client);
        TeleportEntity(client, bot_finished_surf_coords[client], NULL_VECTOR, NULL_VECTOR);
        reset_bot_finished_surf(client);
    }
    else
    {
        GetEntPropVector(client, Prop_Send, "m_vecOrigin", bot_finished_surf_coords[client]);
    }
}

public Action update_mimic_coords(Handle timer, any data)
{
    for (int i = 1; i <= MaxClients; i++)
    {
        if (IsValidClient(i) && is_bot_player[i])
        {
            if (bot_finished_surf_coords[i][0] != 0.0)
            {
                GetEntPropVector(i, Prop_Send, "m_vecOrigin", bot_finished_surf_coords[i]);
            }
        }
    }
    if (round_switch)
    {
        return Plugin_Continue;
    }
    Handle records_list = BotMimic_GetLoadedRecordList();
    for (int i = 0; i < sizeof(map_coorindates); i++)
    {
        bool found = false;
        char char_coord[3][256];
        if (strlen(map_coorindates[i]) > 0)
        {
            ExplodeString(map_coorindates[i], " ", char_coord, sizeof(char_coord[]), sizeof(char_coord[]));
            float coords[3];
            coords[0] = StringToFloat(char_coord[0]);
            coords[1] = StringToFloat(char_coord[1]);
            coords[2] = StringToFloat(char_coord[2]);
            for (int j = 1; j <= MaxClients; j++)
            {
                if (IsValidClient(j) && bot_finished_surf_coords[j][0] == 0.0 && is_bot_player[j])
                {
                    float distance = get_power_distance(j, coords);
                    if (distance < 225.0)
                    {
                        char path[1024];
                        for (int f = 0; f < GetArraySize(records_list); f++)
                        {
                            if (f != i) continue;
                            GetArrayString(records_list, f, path, sizeof(path));
                            for (int h = 0; h < sizeof(map_recordname); h++)
                            {
                                //i != h assures coordinates match the right iterated name
                                if (strlen(map_recordname[h]) == 0 || i != h)
                                {
                                    continue;
                                }
                                if (StrEqual(path, map_recordname[h], false))
                                {
                                    BotMimic_PlayRecordFromFile(j, path);
                                    found = true;
                                }
                                if (found)
                                    break;
                            }
                            if (found)
                                break;
                        }
                    }
                }
                if (found)
                    break;
            }
        }
        if (found)
            break;
    }
    return Plugin_Continue;
}

public float get_power_distance(int target_player, float pos[3])
{
        float vec[3];
        GetClientAbsOrigin(target_player, vec);
        return GetVectorDistance(vec, pos);
}

public int BotMimic_OnRecordSaved(int client, char[] name, char[] category, char[] subdir, char[] file)
{
	//file =  The actual path to the saved .rec file.
	char sQuery[256];
	Format(sQuery, sizeof(sQuery), "UPDATE `maps_coordinates` SET `recordname` = '%s' WHERE `recordname` = 'unknown'", file);
	g_dDatabase.Query(SQL_insertQuery_record_saved, sQuery, _, DBPrio_Low);
}


public Action BotMimic_OnStartRecording(int client, char[] name, char[] category, char[] subdir, char[] path)
{
	float bot_start_coords[3];
        GetEntPropVector(client, Prop_Send, "m_vecOrigin", bot_start_coords);
        char map_name[256];
        GetCurrentMap(map_name, sizeof(map_name));
        char sQuery[256];
	char coords[3][256];
	FloatToString(bot_start_coords[0], coords[0], sizeof(coords[]));
	FloatToString(bot_start_coords[1], coords[1], sizeof(coords[]));
	FloatToString(bot_start_coords[2], coords[2], sizeof(coords[]));
        Format(sQuery, sizeof(sQuery), "insert into `maps_coordinates` (`mapname`, `recordname`, `coordinate`) VALUES ('%s', 'unknown', '%s %s %s')", map_name, coords[0], coords[1], coords[2]);
        g_dDatabase.Query(SQL_insertQuery, sQuery, _, DBPrio_Low);
}

public void select_map_related_surfs()
{
    char map_name[256];
    GetCurrentMap(map_name, sizeof(map_name));
    char sQuery[256];
    Format(sQuery, sizeof(sQuery), "select recordname, coordinate from `maps_coordinates` where mapname = '%s'", map_name);
    g_dDatabase.Query(SQL_OnQueryCompleted, sQuery, _, DBPrio_Low);
}

public void SQL_OnQueryCompleted(Database db, DBResultSet results, const char[] error, DataPack data)
{
    if (!db || strlen(error))
    {       
        LogError("Query error: %s", error);
        return;
    }
    int iterator = 0;
    for (int i = 0; i < sizeof(map_coorindates); i++)
    {
        Format(map_coorindates[i], sizeof(map_coorindates[]), "");
        Format(map_recordname[i], sizeof(map_recordname[]), "");
    }
    while (results.RowCount > 0 && results.FetchRow())
    {
        results.FetchString(0, map_recordname[iterator], sizeof(map_recordname[]));
        results.FetchString(1, map_coorindates[iterator], sizeof(map_coorindates[]));
        iterator++;
    }
}


public void OnMapStart()
{
    for (int i = 1; i <= MaxClients; i++)
        if (IsValidClient(i))
        {
            reset_bot_finished_surf(i);
        }
    char map_name[256];
    GetCurrentMap(map_name, sizeof(map_name));
    if (!g_dDatabase)
        Database.Connect(SQL_OnDatabaseConnect, "bot_surfing");
    else if(StrContains(map_name, "ze_surf", false) != -1)
    {
        //should not be needed
        for (int i = 0; i < sizeof(map_coorindates); i++)
        {
            Format(map_coorindates[i], sizeof(map_coorindates[]), "");
            Format(map_recordname[i], sizeof(map_recordname[]), "");
        }
        select_map_related_surfs();
    }
}

public void SQL_OnDatabaseConnect(Database db, const char[] error, any data)
{
        if(!db || strlen(error))
        {
                LogError("Database error: %s", error);
                return;
        }
        g_dDatabase = db;
        char sQuery[256];
        Format(sQuery, sizeof(sQuery), "CREATE TABLE IF NOT EXISTS `maps_coordinates` (`mapname` VARCHAR(254) NOT NULL, `coordinate` VARCHAR(254) NOT NULL, `recordname` VARCHAR(254) NOT NULL, PRIMARY KEY (`mapname`, `recordname`, `coordinate`))");
        g_dDatabase.Query(SQL_FinishedQuery_creating_tables, sQuery, _, DBPrio_Low);
}

public void SQL_insertQuery_record_saved(Database db, DBResultSet results, const char[] error, DataPack data)
{
    if (!db || strlen(error))
    {
            LogError("Query error: %s", error);
    }
    select_map_related_surfs();
}

public void SQL_insertQuery(Database db, DBResultSet results, const char[] error, DataPack data)
{
        if (!db || strlen(error))
        {
                LogError("Query error: %s", error);
        }
}

public void SQL_FinishedQuery_creating_tables(Database db, DBResultSet results, const char[] error, DataPack data)
{
    if (!db || strlen(error))
    {
            LogError("Query error: %s", error);
    }
    char map_name[256];
    GetCurrentMap(map_name, sizeof(map_name));
    if (StrContains(map_name, "ze_surf", false) != -1)
    {
        select_map_related_surfs();
    }
}

public void is_bot_player_call(int client)
{
    bool debug_b = false;
    if (debug_b && is_jenz(client))
    {
        is_player_jenz[client] = true;
    }
    is_autism_bot1(client);
    is_autism_bot2(client); 
    is_autism_bot3(client); 
    is_autism_bot4(client);
}

public bool is_jenz(int client)
{
    char auth[50];
    GetClientAuthId(client, AuthId_Engine, auth, sizeof(auth));
    if(StrEqual("[U:1:69566635]", auth, false) || StrEqual("STEAM_0:1:34783317", auth, false))
    {
        return true;
    }
    return false;
}

public bool is_autism_bot1(int client)
{
    char auth[50];
    GetClientAuthId(client, AuthId_Engine, auth, sizeof(auth));
    if (StrEqual("[U:1:120378081]", auth, false) || StrEqual("STEAM_0:1:60189040", auth, false))
    {
        is_bot_player[client] = true;
        return true;
    }
    return false;
}

public bool is_autism_bot2(int client)
{
    char auth[50];
    GetClientAuthId(client, AuthId_Engine, auth, sizeof(auth));
    if (StrEqual("[U:1:1036189204]", auth, false) || StrEqual("STEAM_0:0:518094602", auth, false))
    {
        is_bot_player[client] = true;
        return true;
    }
    return false;
}

public bool is_autism_bot3(int client)
{
    char auth[50];
    GetClientAuthId(client, AuthId_Engine, auth, sizeof(auth));
    if (StrEqual("[U:1:408797742]", auth, false) || StrEqual("STEAM_0:0:204398871", auth, false))
    {
        is_bot_player[client] = true;
        return true;
    }
    return false;
}

public bool is_autism_bot4(int client)
{
    char auth[50];
    GetClientAuthId(client, AuthId_Engine, auth, sizeof(auth));
    if (StrEqual("[U:1:1221121532]", auth, false) || StrEqual("STEAM_0:0:610560766", auth, false))
    {
        is_bot_player[client] = true;
        return true;
    }
    return false;
}

stock Client_switch_to_knife(int client)
{
    //2 is always knife
    int weapon = GetPlayerWeaponSlot(client, 2);
    if (Weapon_IsValid(weapon)) {
        Client_SetActiveWeapon(client, weapon);
    }
}