#include <sourcemod>
#include <SteamWorks>
#include <json>
#include <ccc>
#include <cstrike>

public Plugin myinfo =
{
	name			= "Racetimer Rank",
	author			= "jenz",
	description		= "Give racetimer points as a level clan tag to players, also for chat tag",
	version			= "1.1",
	url				= ""
};

bool g_b_ignoring_tags[MAXPLAYERS + 1];
Database g_hDatabase;

public void OnPluginStart()
{
    RegConsoleCmd("sm_lvl", Command_LvTag, "Turns the Lv. tag feature on or off");
    if (!g_hDatabase)
    {
        Database.Connect(SQL_OnDatabaseConnect, "racetimercss");
        return;
    }
    for(int client = 1; client <= MaxClients; client++)
    {
        if(IsClientInGame(client))
        {
            OnClientPostAdminFilter(client);
        }
    }
}

public void OnMapStart()
{
    if (!g_hDatabase)
    {
        Database.Connect(SQL_OnDatabaseConnect, "racetimercss");
        return;
    }
}

public void SQL_OnDatabaseConnect(Database db, const char[] error, any data)
{
    if(!db || strlen(error))
    {
        LogError("Database error: %s", error);
        return;
    }
    g_hDatabase = db;

    for(int client = 1; client <= MaxClients; client++)
    {
        if(IsClientInGame(client))
        {
            OnClientPostAdminFilter(client);
        }
    }
}

public Action Command_LvTag(int client, int args)
{
    if (!g_hDatabase)
    {
        return Plugin_Handled;
    }
    //i am only adding this shit because lighty wanted it to be turn-off able, after half a year probably only 7 people will have made use of it.
    //kinda hate wasting time on pointless stuff like this.
    char sSID[64];
    char sQuery[512];
    if (g_b_ignoring_tags[client])
    {
        g_b_ignoring_tags[client] = false;
    }
    else
    {
        g_b_ignoring_tags[client] = true;
    }
    GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID));
    Format(sQuery, sizeof(sQuery), "insert into zetimer_table_ignore_tag (`steam_auth`, `is_ignoring`) values ('%s', '%i') ON DUPLICATE KEY UPDATE `is_ignoring` = '%i'", sSID, g_b_ignoring_tags[client], g_b_ignoring_tags[client]);
    g_hDatabase.Query(SQL_FinishedQuery, sQuery, GetClientSerial(client), DBPrio_Low);
    return Plugin_Handled;
}

public void SQL_FinishedQuery(Database db, DBResultSet results, const char[] error, int iSerial)
{
    if (!db || strlen(error))
    {
        LogError("Query error 3: %s", error);
    }

    int client;
    if ((client = GetClientFromSerial(iSerial)) == 0)
    {
        delete results;
        return;
    }
    delete results;

    if (g_b_ignoring_tags[client])
    {
        PrintToChat(client, "Now hiding LVL tag.");
        CS_SetClientClanTag(client, "");
    }
    else
    {
        PrintToChat(client, "Now displaying LVL tag again.");
        OnClientPostAdminFilter(client);
    }
}

public void OnClientDisconnect(int client)
{
    g_b_ignoring_tags[client] = false;
}

public void OnClientPostAdminFilter(int client)
{
    if(!IsClientAuthorized(client) || IsClientSourceTV(client))
        return;
    
    if (IsFakeClient(client))
    {
        char tag[64];
        Format(tag, sizeof(tag), "[LVL 1]");
        CS_SetClientClanTag(client, tag);
        return;
    }

    g_b_ignoring_tags[client] = false;
    check_ignoring_tags(client);
}

public bool HasClanTag(int client)
{
    char sClanID[32];
    GetClientInfo(client, "cl_clanid", sClanID, sizeof(sClanID));
    return strlen(sClanID) > 1; //without a tag the len is 1.
}

public void check_ignoring_tags(int client)
{
    char sQuery[512];
    char sSID[64];

    GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID));
    Format(sQuery, sizeof(sQuery), "select is_ignoring from zetimer_table_ignore_tag where steam_auth = '%s'", sSID);
    g_hDatabase.Query(SQL_OnQueryCompletedIgnoreTag, sQuery, GetClientSerial(client), DBPrio_Low);
}

public void SQL_OnQueryCompletedIgnoreTag(Database db, DBResultSet results, const char[] error, int iSerial)
{
    if (!db || strlen(error))
    {
        delete results;
        LogError("Query error 3: %s", error);
        return;
    }

    int client;
    if ((client = GetClientFromSerial(iSerial)) == 0)
    {
        delete results;
        return;
    }

    int val = 0;
    if (results.RowCount && results.FetchRow())
    {
        val = results.FetchInt(0);
    }

    delete results;
    if (val == 1)
    {
        g_b_ignoring_tags[client] = true;
    }
    else
    {
        check_client_racetimer_rank(client);
    }
}

public void check_client_racetimer_rank(int client)
{
    char sSID[64];
    char sRequest[256];
    GetClientAuthId(client, AuthId_Steam2, sSID, sizeof(sSID));
    FormatEx(sRequest, sizeof(sRequest), "https://racebackend.unloze.com/racetimer_endpoints-1.0/api/timers/player/%s", sSID);
    
    Handle hRequest = SteamWorks_CreateHTTPRequest(k_EHTTPMethodGET, sRequest);
    if (!hRequest ||
        !SteamWorks_SetHTTPCallbacks(hRequest, OnTransferComplete) ||
        !SteamWorks_SetHTTPRequestContextValue(hRequest, GetClientSerial(client)) ||
        !SteamWorks_SendHTTPRequest(hRequest))
    {
        delete hRequest;
    }
}

public int OnTransferComplete(Handle hRequest, bool bFailure, bool bSuccessful, EHTTPStatusCode eStatusCode, int iSerial)
{
    int client = GetClientFromSerial(iSerial);
    if (!client) //Player disconnected.
    {
        delete hRequest;
        return 0;
    }

    if (bFailure || !bSuccessful || eStatusCode != k_EHTTPStatusCode200OK)
    {
        delete hRequest;
        LogError("Request-Error: %d", eStatusCode);
        return 0;
    }

    SteamWorks_GetHTTPResponseBodyCallback(hRequest, OnTransferResponse, iSerial);
    return 0;
}

public int OnTransferResponse(char[] sData, int iSerial)
{
    int client = GetClientFromSerial(iSerial);
    if (!client) //Player disconnected.
    {
        return 0;
    }

    JSON_Object obj = json_decode(sData); //https://github.com/clugg/sm-json
    
    int I_player_points = obj.GetInt("PlayerPoints") //if no endpoint for steamID default value is -1
    if (I_player_points < 1000)
    {
        I_player_points = 1000; //setting level 1
    }

    if (!HasClanTag(client))
    {
        char tag[64];
        Format(tag, sizeof(tag), "[LVL %i]", I_player_points/1000);
        CS_SetClientClanTag(client, tag);
    }

    int I_rank = obj.GetInt("Rank") //if no endpoint for steamID default value is -1
    if (0 < I_rank < 1000) //only give chat tag to top 1000
    {
        if (player_has_no_chattag(client))
        {
            set_level_tag(client, I_player_points/1000, I_rank);
        }
    }
    json_cleanup_and_delete(obj);
    return 0;
}

public bool player_has_no_chattag(int client)
{
    char tag[256];
    CCC_GetTag(client, tag, sizeof(tag));
    return strlen(tag) < 2;
}

public void set_level_tag(int client, int lvl, int I_rank)
{
    char hexadecimals[16];
    char hexadecimals2[16];
    char hexadecimals3[16];
    int red = (lvl * 17) % 256;
    int green = ((lvl + 50) * 13) % 256;
    int blue = ((lvl + 100) * 19) % 256;
    Format(hexadecimals, sizeof(hexadecimals), "%02X%02X%02X", red, green, blue);
    if (I_rank > 250)
    {
        char tag[64];
        Format(tag, sizeof(tag), "[LVL %i] ", lvl);
        CCC_SetTag(client, tag);
        CCC_SetColor(client, CCC_TagColor, StringToInt(hexadecimals, 16), false);
        return;
    }
    if (I_rank > 50)
    {
        red = (lvl * 27) % 256;
        green = ((lvl + 50) * 23) % 256;
        blue = ((lvl + 100) * 29) % 256;
        Format(hexadecimals2, sizeof(hexadecimals2), "%02X%02X%02X", red, green, blue);

        red = (lvl * 37) % 256;
        green = ((lvl + 50) * 33) % 256;
        blue = ((lvl + 100) * 39) % 256;
        Format(hexadecimals3, sizeof(hexadecimals3), "%02X%02X%02X", red, green, blue);

        char tag[128];
        char charlvl[16];
        IntToString(lvl, charlvl, sizeof(charlvl));

        char coloured_numbers[32];
        for (int i = 0; i < strlen(charlvl); i++)
        {
            int i2 = i + 2;
            i2 = i * i;
            char number_hexa[16];
            red = (lvl * (i2 * 37)) % 256;
            green = ((lvl + (i2 * 50)) * 33) % 256;
            blue = ((lvl + (i2 * 100)) * 39) % 256;
            Format(number_hexa, sizeof(number_hexa), "%02X%02X%02X", red, green, blue);
            Format(coloured_numbers, sizeof(coloured_numbers), "%s\x07%s%c", coloured_numbers, number_hexa, charlvl[i]);
        }
        Format(tag, sizeof(tag), "\x07%s[L\x07%sV\x07%sL %s] ", hexadecimals, hexadecimals2, hexadecimals3, coloured_numbers);
        CCC_SetTag(client, tag);
        return;
    }

    red = (GetRandomInt(0, 255)) % 256;
    green = (GetRandomInt(0, 255)) % 256;
    blue = (GetRandomInt(0, 255)) % 256;
    Format(hexadecimals, sizeof(hexadecimals), "%02X%02X%02X", red, green, blue);

    red = (GetRandomInt(0, 255)) % 256;
    green = (GetRandomInt(0, 255)) % 256;
    blue = (GetRandomInt(0, 255)) % 256;
    Format(hexadecimals2, sizeof(hexadecimals2), "%02X%02X%02X", red, green, blue);

    red = (GetRandomInt(0, 255)) % 256;
    green = (GetRandomInt(0, 255)) % 256;
    blue = (GetRandomInt(0, 255)) % 256;
    Format(hexadecimals3, sizeof(hexadecimals3), "%02X%02X%02X", red, green, blue);

    char tag[128];
    char charlvl[16];
    IntToString(lvl, charlvl, sizeof(charlvl));

    char coloured_numbers[32];
    for (int i = 0; i < strlen(charlvl); i++)
    {
        char number_hexa[16];
        red = (GetRandomInt(0, 255)) % 256;
        green = (GetRandomInt(0, 255)) % 256;
        blue = (GetRandomInt(0, 255)) % 256;
        Format(number_hexa, sizeof(number_hexa), "%02X%02X%02X", red, green, blue);
        Format(coloured_numbers, sizeof(coloured_numbers), "%s\x07%s%c", coloured_numbers, number_hexa, charlvl[i]);
    }
    Format(tag, sizeof(tag), "\x07%s[L\x07%sV\x07%sL %s] ", hexadecimals, hexadecimals2, hexadecimals3, coloured_numbers);
    CCC_SetTag(client, tag);
}