#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <curl/curl.h>
#include <jansson.h>

/*
 * mcstatus - simple cli using mcserverstat.us' API
 * see https://api.mcsrvstat.us/
 * 29/12/2025 - this software provides no guarantees.
 * made by texturawasd (https://texturawasd.ddns.net)
 * License: MIT
 *
 * note 06/01/2026: api has updated (V3) so player list logic was updated
 */

/*
 * use:
 * cc mcstatus.c -ljansson -lcurl -o mcstatus && ./mcstatus <server's IP>
 */

#define OK        0
#define NOT_OK    1
#define ERR_USAGE 64

struct memory {
    char           *response;
    size_t         size;
};

static size_t write_callback(
    void           *data,
    size_t         size,
    size_t         nmemb,
    void           *userp)
{
    size_t         realsize = size * nmemb;
    struct memory  *mem = (struct memory *)userp;

    char *ptr =    realloc(mem->response, mem->size + realsize + 1);
    if (!ptr) return 0; /* have curl abort the transfer */

    mem->response = ptr;
    memcpy(&(mem->response[mem->size]), data, realsize);
    mem->size += realsize;
    mem->response[mem->size] = 0;

    return realsize;
}

int main(int argc, char **argv)
{
    if (argc != 2) {
        printf("Usage: %s <server-address>\n", argv[0]);
        return ERR_USAGE;
    }

    char           url[256];
    snprintf(url, sizeof(url), "https://api.mcsrvstat.us/3/%s", argv[1]);

    CURL           *curl = curl_easy_init();
    struct memory  chunk = {.response = NULL, .size = 0};

    if (!curl) return NOT_OK;

    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk);
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "mcstatus-cli/1.0");

    CURLcode       res = curl_easy_perform(curl);
    curl_easy_cleanup(curl);

    if (res != CURLE_OK) {
        fprintf(stderr, "curl failed: %s\n", curl_easy_strerror(res));
        free(chunk.response);
        return NOT_OK;
    }

    json_t         *root;
    json_error_t   error;

    if (!chunk.response) { // guard before loading chunk.response
        fprintf(stderr, "Empty response\n");
        return NOT_OK;
    } // chunk.resonse could technically be NULL

    root = json_loads(chunk.response, 0, &error);
    free(chunk.response);

    if (!root) {
        fprintf(stderr, "JSON parse error: %s\n", error.text);
        return NOT_OK;
    }

    json_t         *online = json_object_get(root, "online");
    if (!json_is_boolean(online) || !json_boolean_value(online)) {
        puts("server is OFFLINE");
        json_decref(root);
        return OK;
    }

    puts("server is ONLINE!!!!11!!!!1!!!");

    /* version */
    json_t         *version = json_object_get(root, "version");
    if (json_is_string(version))
        printf("Version: %s\n", json_string_value(version));

    /* players */
    json_t         *players = json_object_get(root, "players");
    if (json_is_object(players)) {
        json_t     *online_players = json_object_get(players, "online");
        json_t     *max_players = json_object_get(players, "max");

        long long  online_val = json_is_integer(online_players)
                                 ? json_integer_value(online_players)
                                 : 0;
        long long  max_val =    json_is_integer(max_players)
                                 ? json_integer_value(max_players)
                                 : 0;

        printf("Players: %lld/%lld\n", online_val, max_val);

        /* player list */
        json_t *list = json_object_get(players, "list");
        if (json_is_array(list) && json_array_size(list) > 0) {
            size_t index;
            json_t *value;

            printf("Player List:\n");
            json_array_foreach(list, index, value) {
                /* 06/01/2026: In v3, list items are objects:
                {"name": "...", "uuid": "..."} */
                if (json_is_object(value)) {
                    json_t *pname = json_object_get(value, "name");
                    if (json_is_string(pname)) {
                        printf("%s\n", json_string_value(pname));
                    }
                }
            }
        } else {
            printf("Player list unavailable or hidden\n");
        }
    }

    json_decref(root);
    return OK;
}

