#pragma semicolon 1

#include <sourcemod>
#include <sdktools>

int server_ports[2] = {27015, 27078}; //server ports: ze server, simulated server.
int g_iServerPort;
int g_iClientIndexUserIDMapping[MAXPLAYERS + 1];
bool g_bCooldownMapChange = false;

char g_cClientNewName[MAXPLAYERS + 1][128];

//----------------------------------------------------------------------------------------------------
// Purpose:
//----------------------------------------------------------------------------------------------------
public Plugin myinfo =
{
	name         = "info from the ze server",
	author       = "jenz",
	description  = "just copies maps, hostname and client names info from the ze server",
	version      = "1.0.0"
};

public void OnPluginEnd()
{
	for(int i = 1; i <= MaxClients; i++)
	{
		if(IsValidClient(i) && IsFakeClient(i))
		{
			KickClient(i, "Disconnect by user.");
		}
	}
}

public void OnPluginStart()
{
    g_bCooldownMapChange = false;
    g_iServerPort = GetConVarInt(FindConVar("hostport"));
    if (g_iServerPort == server_ports[0])
    {
        CreateTimer(5.0, update_info_over_txt, _, TIMER_REPEAT);
    }
    else if (g_iServerPort == server_ports[1])
    {
        CreateTimer(GetRandomFloat(2.0, 5.0), read_info_over_txt);
    }
}

public Action update_client_name(Handle hTimer, int Serial)
{
    int client; 
    if ((client = GetClientFromSerial(Serial)) == 0)
    {
        return Plugin_Handled;
    }
    if (IsValidClient(client))
    {
        char letters[26] = "abcdefghijklmnopqrstuvwxyz\0";
        int iRandom = GetRandomInt(0, 25);
        char random_char_begin = letters[iRandom];
        int iRandom_end = GetRandomInt(0, 25);
        char random_char_end = letters[iRandom_end];
        char final_name[MAX_NAME_LENGTH * 2];
        Format(final_name, sizeof(final_name), "%s%s%s", random_char_begin, g_cClientNewName[client], random_char_end);
        SetClientName(client, final_name);
        Format(g_cClientNewName[client], sizeof(g_cClientNewName[]), final_name);
    }
    return Plugin_Handled;
}

public Action read_info_over_txt(Handle timer, any data)
{
    new Handle:fileHandle = OpenFile("/addons/sourcemod/logs/simulate.txt", "r");
    char lineBuffer[2048];
    int count = 0;

    int current_ClientUserID_iteration[MAXPLAYERS + 1];

    bool already_created_fakeClient = false;

    while(!IsEndOfFile(fileHandle) && ReadFileLine(fileHandle, lineBuffer, sizeof(lineBuffer)))
    {
        TrimString(lineBuffer);
        if (strlen(lineBuffer) == 0)
        {
            continue;
        }
        if (count == 0) //mapname
        {
            //its prefered instead to just fake the mapname since the fakeclients wont have to be kicked on mapchange then.
            /*
            char mapname[256];
            GetCurrentMap(mapname, sizeof(mapname));
            if (!StrEqual(mapname, lineBuffer))
            {
                //ForceChangeLevel(lineBuffer, "");
                break;
            }
            */
            char cmd[256];
            Format(cmd, sizeof(cmd), "sv_mapname_override %s", lineBuffer);
            ServerCommand(cmd);
            //handled server hibernation with https://forums.alliedmods.net/showthread.php?t=331283
        }
        else if (count == 1) //hostname
        {
            static Handle hHostName;
            if((hHostName = FindConVar("hostname")) == INVALID_HANDLE)
                return Plugin_Handled;
            SetConVarString(hHostName, lineBuffer, false, false);
        }
        else //reading clientUserID, client frag and playername
        {
            //index 0 = ClientUserID, index 1 = client frags, index 2 = client user name.
            char[][] sPart = new char[3][256];
            ExplodeString(lineBuffer, " ", sPart, 3, 256);
            int ClientUserID = StringToInt(sPart[0]);

            char fixedName[128];
            char final_name[MAX_NAME_LENGTH * 2];
            //take everything after the frag. thats a white space + the entire name.
            int index_after_frag = StrContains(lineBuffer, sPart[1], false) + 1 + strlen(sPart[1]); //skipping the whitespace after frag
            strcopy(fixedName, sizeof(fixedName), lineBuffer[index_after_frag]);
            
            bool does_client_user_id_exist = false;
            for (int i = 0; i <= MaxClients; i++)
            {
                //the clients on the simulate server will always first exist after CreateFakeClient so this approach should be safe.
                if (IsValidClient(i) && !IsClientSourceTV(i) && g_iClientIndexUserIDMapping[i] == ClientUserID)
                {
                    does_client_user_id_exist = true;
                    break;
                }
            }

            //create one new fakeclient if not existing already
            if (!does_client_user_id_exist && !already_created_fakeClient)
            {
                char letters[26] = "abcdefghijklmnopqrstuvwxyz\0";
                int iRandom = GetRandomInt(0, 25);
                char random_char_begin = letters[iRandom];
                int iRandom_end = GetRandomInt(0, 25);
                char random_char_end = letters[iRandom_end];
                Format(final_name, sizeof(final_name), "%s%s%s", random_char_begin, fixedName, random_char_end);

                //User ID is new, so lets create the FakeClient.
                int iIndex = CreateFakeClient(final_name);

                if(iIndex < 1 || iIndex > MaxClients)
                    continue;

                Format(g_cClientNewName[iIndex], sizeof(g_cClientNewName[]), final_name);

                SetEntityFlags(iIndex, FL_CLIENT);
                DispatchKeyValue(iIndex, "classname", "player");
                DispatchSpawn(iIndex);

                AdminId FakeAdmin = CreateAdmin();
                SetAdminFlag(FakeAdmin, Admin_Custom6, true);
                SetUserAdmin(iIndex, FakeAdmin, true);

                g_iClientIndexUserIDMapping[iIndex] = ClientUserID;
                already_created_fakeClient = true;
            }

            //updating the clients frags and username based on the ClientUserID.
            for (int i = 0; i <= MaxClients; i++)
            {
                if (IsValidClient(i) && !IsClientSourceTV(i) && g_iClientIndexUserIDMapping[i] == ClientUserID)
                {
                    int clientFrag = StringToInt(sPart[1]);
                    SetEntProp(i, Prop_Data, "m_iFrags", clientFrag);

                    /*
                    if (!StrEqual(g_cClientNewName[i], fixedName))
                    {
                        Format(g_cClientNewName[i], sizeof(g_cClientNewName[]), fixedName);
                        CreateTimer(GetRandomFloat(1.0, 5.0), update_client_name, GetClientSerial(i));
                    }
                    */
                    //indicating that the client still exists right now.
                    current_ClientUserID_iteration[i] = ClientUserID;
                    break;
                }
            }
        }
        count++;
    }
    CloseHandle(fileHandle);

    //kicking clients whos UserID is not connected on ze anymore.


    for (int i = 0; i <= MaxClients; i++)
    {
        if (IsValidClient(i) && !IsClientSourceTV(i))
        {
            bool does_UserID_still_exist = false;
            for (int j = 0; j <= MaxClients; j++)
            {
                //for every user in g_iClientIndexUserIDMapping does the userID exist in the current iteration still.
                if (IsValidClient(j) && !IsClientSourceTV(j) && g_iClientIndexUserIDMapping[i] == current_ClientUserID_iteration[j])
                {
                    does_UserID_still_exist = true;
                    break;
                }
            }
            if (!does_UserID_still_exist)
            {
                g_iClientIndexUserIDMapping[i] = -1;
                KickClient(i, "Disconnect by user.");
            }
        }
    }
    CreateTimer(GetRandomFloat(2.0, 5.0), read_info_over_txt);
    return Plugin_Handled;
}


public void OnMapEnd()
{
    g_bCooldownMapChange = true;
    CreateTimer(20.0, allow_updating_again);
}

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

public Action update_info_over_txt(Handle timer, any data)
{
    if (g_bCooldownMapChange)
    {
        return Plugin_Handled;
    }
    //sending map name, hostname and player names to the simulate server from ze server.
    char g_cMapname[256];
    GetCurrentMap(g_cMapname, sizeof(g_cMapname));
    
    static Handle hHostName;
    if((hHostName = FindConVar("hostname")) == INVALID_HANDLE)
        return Plugin_Handled;
    char hostname[256];
    GetConVarString(hHostName, hostname, sizeof(hostname));

    char msg[2048];
    Format(msg, sizeof(msg), "%s\n%s\n", g_cMapname, hostname);
    //cleaning the file so it only contains mapname and hostname
    new Handle:testFile = OpenFile("/addons/sourcemod/logs/simulate.txt", "w");
    WriteFileLine(testFile, msg);
    CloseHandle(testFile);

    Format(msg, sizeof(msg), "");

    for (int i = 0; i < MaxClients; i++)
    {
        if (IsValidClient(i) && !IsClientSourceTV(i))
        {
            int frags = GetClientFrags(i);
            //GetClientUserId is the only consistent thing for fakeclients.
            Format(msg, sizeof(msg), "%d %i %N\n", GetClientUserId(i), frags, i);

            new Handle:testFile_l = OpenFile("/addons/sourcemod/logs/simulate.txt", "a");
            WriteFileLine(testFile_l, msg);
            CloseHandle(testFile_l);
        }
    }
    return Plugin_Handled;
}

//sending players over to the actual ze server if its the simulated server.
public void OnClientPostAdminCheck(int client)
{
    if (g_iServerPort != server_ports[1])
    {
        return;
    }
    if (!IsFakeClient(client) && (!IsClientSourceTV(client)))
    {
        ClientCommand(client, "redirect ze.unloze.com:27015");
    }
}

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