#include <string>
#include <cstdio>

#include "simplesock.h"
#include "soldatinfo.h"

void DumpInfo(SoldatInfo& info);

using std::printf;

int main(int argc, char *argv[])
{
    if (argc != 4)
    {
        std::cout << "Usage:\n\tsoldatinfo host port pass" << std::endl;
        return 0;
    }

    // Some values that are used to form connection to soldat server
    const char *host = argv[1], *port = argv[2], *pass = argv[3];
    
    try
    {
        // Our socket stream object, can throw simplesock::init_fail() on windows, if Winsock fails to initialize
        simplesock::sockstream soldat_sock;

        // Next let's open the actual connection
        soldat_sock.open(host, port);

        // Next let's check that our connection really is open
        if (!soldat_sock.is_open())
        {
            throw std::string("Failed to connect to soldat server");
        }

        // Writes the password to the stream and flushes it (std::endl is a stream manipulator that also calls .flush())
        soldat_sock << pass << std::endl;
        // Write REFRESH similiarly
        soldat_sock << "REFRESH" << std::endl;

        // The sockstream object is completely compatible with iostream
        // We can use the standard function std::getline(), which will report EOF when soldat closes connection :)
        for (std::string line; getline(soldat_sock, line);)
        {
            // The line includes the newline delimiter, thus we use substr
            if (line.substr(0, 7) == "REFRESH")
            {
                // As said, this is completely compatible with iostream, so let us take advantage of it!
                // Let's use .read() to get our refresh packet
                unsigned char packet_buffer[1188];
                soldat_sock.read(reinterpret_cast<char*>(packet_buffer), sizeof(packet_buffer));

                // If it failed, let's throw an exception..
                if (!soldat_sock.good())
                {
                    throw std::string("Failed to fetch REFRESH packet");
                }

                // We got the packet! Let's parse it into a SoldatInfo object
                SoldatInfo info(packet_buffer);
                // Let's dump the info to standard output
                DumpInfo(info);
                // Then we'll close our admin connection by breaking from our network loop
                break;
            }
        }
    }
    catch (simplesock::init_fail ex)
    {
        printf(ex.what());
    }
    catch (std::string ex)
    {
        printf(ex.c_str());
    }

    return 0;
}

void DumpInfo(SoldatInfo& info)
{
    printf("----------------------------\n"
           "Soldat Refresh Packet Dumper\n"
           "----------------------------\n");

    std::string gamemode = SoldatInfo::GamemodeToStr(info.Gamemode());
    std::string map = info.Map();
    int num_players = info.NumPlayers();
    int num_specs = info.NumSpecs();
    int time_left_mins = SoldatInfo::TicksToMins(info.Timeleft());
    int time_left_secs = SoldatInfo::TicksToSecs(info.Timeleft()) % 60;
    int time_limit = info.Timelimit();
    int limit = info.Limit();

    printf("Info:\n\n");
    printf("%-10s%s\n", "Gamemode:", gamemode.c_str());
    printf("%-10s%i\n", "Players:", num_players);
    printf("%-10s%i\n", "Specs:", num_specs);
    printf("%-10s%s\n", "Map:", map.c_str());
    printf("%-10s%i:%02i / %i:00\n", "Timeleft:", time_left_mins, time_left_secs, time_limit);
    printf("%-10s%i\n", "Limit:", limit);

    if (num_players > 0)
    {
        printf("\n\nPlayers:\n");
        // You can either iterate through players using integer to index the players array
        // Or you can use iterators (see specs loop for example)
        for (int i = 0; i < num_players; i++)
        {
            SoldatInfo::Client player = info.Players[i];
            std::string name = player.Name();
            std::string team = SoldatInfo::TeamToStr(player.Team());
            std::string ip = player.Ip();
            int id = player.Id();
            int kills = player.Kills();
            int deaths = player.Deaths();
            int ping = player.Ping();

            printf("\n%s\n%s\n", name.c_str(), std::string(name.size(), '-').c_str());
            printf("%-8s%i\n", "Id:", id);
            printf("%-8s%s\n", "Team:", team.c_str());
            printf("%-8s%i\n", "Kills:", kills);
            printf("%-8s%i\n", "Deaths:", deaths);
            printf("%-8s%i\n", "Ping:", ping);
            printf("%-8s%s\n", "Ip:", ip.c_str());
        }
    }

    if (num_specs > 0)
    {
        printf("\n\nSpecs:\n");
        // Example of using iterators
        for (SoldatInfo::iterator spec = info.Specs.begin(); spec != info.Specs.end(); spec++)
        {
            std::string name = spec->Name();
            std::string ip = spec->Ip();
            int id = spec->Id();
            int ping = spec->Ping();

            printf("\n%s\n%s\n", name.c_str(), std::string(name.size(), '-').c_str());
            printf("%-8s%i\n", "Id:", id);
            printf("%-8s%i\n", "Ping:", ping);
            printf("%-8s%s\n", "Ip:", ip.c_str());
        }
    }
}
