/**
  \file lists.h
  \author FC
  \date 2026
  \brief List, arrays and counters initialization along with static/dynamic portability logic.

  This file is written inside of an #ifndef. If you're able to read this you're probably using the
  compiled/static version.

  If you pass -D USE_RUNTIME_LISTS to the compiler, it'll create an executable of the server in which
  most of whats defined in macros in the static version can be defined as runtime variables
  after all .txt configuration files are read when the server starts.
  Instead of config.h, arealist.h and .autogen_macros.h, this version
  uses config/config.txt and config/areas.txt to load the new configuration.

  You may want to use for example charCount instead of CHAR_LIST_COUNT when looping through the charlist
  in main.c as this is compatible with both versions, otherwise memory gets fucked up on dyn.
*/

#ifndef USE_RUNTIME_LISTS

// =====================
//    STATIC VERSION
// =====================

#define charCount CHAR_LIST_COUNT
#define musicCount MUSIC_LIST_LENGTH
#define areaCount AREA_LIST_COUNT
#define maxPlayers MAX_PLAYERS
#define maxConnections MAX_CONNECTIONS

#include "../config/arealist.h"

/*CHARLIST CREATION*/
#define X(s) s,
const char *charList[] = {CHARLIST}; /**< Charlist string array. The name of every character found in CHARLIST
                                    (.autogen_macros.h) goes here. Built using the preprocessor. */
#undef X

const char *CHAR_LIST_PACKET = "SC#" /**< The SC packet including all characters. Built using the preprocessor.*/
#define X(s) s"#"
    CHARLIST
#undef X
"%";

#define X(name) {name, ""},
#define X_STREAM_SONG(name, realName) {name, realName},

Song musicList[] = {MUSIC_LIST}; /**< Musiclist string array. The name of every song found in MUSIC_LIST
                                 (.autogen_macros.h) is here. Built using the preprocessor.*/
#undef X
#undef X_STREAM_SONG

enum { 
#define X(s) STRLEN_CT(s) +
#define X_STREAM_SONG(s, s2) STRLEN_CT(s) +
    MUSIC_LIST_TOTAL_STRING_COUNT = MUSIC_LIST 0, /**< Sums every character from the music list's (first)
                                                  names using the preprocessor*/
#undef X
#undef X_STREAM_SONG
#define X(s) 1 +
#define X_STREAM_SONG(s, s2) 1 +
    MUSIC_LIST_LENGTH = MUSIC_LIST 0 /*Gets the length of the music list using the preprocessor*/
#undef X
#undef X_STREAM_SONG
};

#define AREA_LIST_COUNT ARRAY_SIZE(areaList)
#define AREA_MUSIC_LIST_LENGTH MUSIC_LIST_LENGTH + AREA_LIST_COUNT

static char MUSIC_LIST_PACKET[SUM_COMMON_EXT((AREA_LIST_TOTAL_STRING_COUNT + MUSIC_LIST_TOTAL_STRING_COUNT + AREA_MUSIC_LIST_LENGTH)) - 1 ];

Player players[MAX_PLAYERS];
Conn conns[MAX_CONNECTIONS];

#define ADVERTISER_REQUEST_FORMAT_STRING "POST " MASTER_PATH " HTTP/1.1\r\n" \
        "Host: " MASTER_HOST "\r\n" \
        "Content-Type: application/json\r\n" \
        "Content-Length: %lld\r\n" \
        "Connection: close\r\n" \
        "\r\n" \
        "%s"

/**Send the advertise packet to the masterlist server. */
void advertise(){
    lastAdvertised = time(NULL);
    struct addrinfo hints, *res;
    int sockfd;
    
    char request[STRLEN_CT(ADVERTISER_REQUEST_FORMAT_STRING) - 3 + DIGITS(MAX_PLAYERS) + STRLEN_CT(ADVERTISER_JSON_FORMAT_STRING) - 2 + 1] = "";

    uint8_t playerCountDigits = 1;
    uint8_t playerCountCp = playerCount;
    while(playerCountCp >= 10){
        playerCountCp /= 10;
        playerCountDigits++;
    }

    sprintf(request, ADVERTISER_REQUEST_FORMAT_STRING, 
    STRLEN_CT(ADVERTISER_JSON_FORMAT_STRING) + playerCountDigits - 2, 
    ADVERTISER_JSON_FORMAT_STRING);
    sprintf(request, request, playerCount);

    memset(&hints, 0, sizeof(hints));
    hints.ai_family = AF_UNSPEC;
    hints.ai_socktype = SOCK_STREAM;

    if (getaddrinfo(MASTER_HOST, "80", &hints, &res) != 0) {
        perror("getaddrinfo");
        return;
    }

    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd < 0) {
        perror("socket");
        return;
    }

    if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
        perror("connect");
        closeSocket(sockfd);
        return;
    }

    send(sockfd, request, strlen(request), 0);

    #if DEBUG
      char buffer[4096];
      int bytes;

      while ((bytes = recv(sockfd, buffer, sizeof(buffer) - 1, 0)) > 0) {
          buffer[bytes] = '\0';
          printf("%s", buffer);
      }

      if (bytes < 0) {
          perror("recv");
      }
    #endif

    closeSocket(sockfd);
    freeaddrinfo(res);
}

void lists_init(void) {}


/*============================ NEGRO ZONE BELOW THIS LINE.  ============================*/













#else
// =====================
//    DYNAMIC VERSION
//      - NEVER -
//     -- FUCK --
// =====================



/*
  THIS IS HOW THE LISTS AND SOME SHIT IS MADE IF YOU COMPILE USING -D USE_RUNTIME_LISTS.
  ITS SO UGLY I'M NOT GOING TO BOTHER COMMENTING ANY OF THIS. 
  DONT KNOW WHY I MADE A DYNAMIC VERSION WHEN NO ONE IS GOING TO USE THIS SHIT BESIDES ME
  AND SOME OTHER 2-3 BASEMENT DWELLERS WHO KNOW HOW TO USE A COMPUTER AND WOULD RATHER COMPILE
  AS THEIR CAUCASIAN BLOOD COMMANDS
*/

#include <stdlib.h>

#define MAX_LINE 512

static char **charList = NULL;
static uint16_t charCount = 0;

static Song *musicList = NULL;
static uint16_t musicCount = 0;

static Area *areaList = NULL;
static uint8_t areaCount = 0;

#define AREA_PROP_LIST \
  X(background) \
  X(areaMessage) \
  X(currentTrack)

#define X(prop) \
  if (!strcmp(propName, STR(prop))) { \
    strcpy(areaList[areaCount-1].prop, propVal); \
    return 1; \
  }

uint8_t checkAndSetAreaProp(char *propName, char *propVal){
  AREA_PROP_LIST
  return 0;
}

#undef X

enum {
  MUSIC_LIST_LENGTH = 4096,
  MUSIC_LIST_TOTAL_STRING_COUNT = MUSIC_LIST_LENGTH * 32
};

/*CHAR_LIST_COUNT is defined on main.c*/
/*this is jewish child sacrifice black ritual magicks*/
#define AREA_LIST_COUNT 512
#define AREA_NAME_MAX_LENGTH 32
#define AREA_LIST_TOTAL_STRING_COUNT 1024 * 32
#define AREA_MUSIC_LIST_LENGTH (4096 + 1024) * 32 + 4096 + 1024
static char MUSIC_LIST_PACKET[SUM_COMMON_EXT((AREA_LIST_TOTAL_STRING_COUNT + MUSIC_LIST_TOTAL_STRING_COUNT + AREA_MUSIC_LIST_LENGTH)) - 1 ];
static char CHAR_LIST_PACKET[SUM_COMMON_EXT(CHAR_LIST_COUNT * 32 + CHAR_LIST_COUNT)];

//fuckingc onfig
#undef TCP_LISTEN_PORT
#undef WS_LISTEN_PORT

#undef SERVER_DESCRIPTION
#undef ASS_PACKET_STRING
#undef MOTD
#undef SERVER_FULL_MESSAGE
#undef MODPASS

#undef MAX_ALLOWED_MULTICLIENT
#undef LOGGING
#undef DEFAULT_AUTOPAIR
#undef ADVERTISE
#undef MASTER_HOST
#undef MASTER_PATH
#undef MAX_CONNECTIONS
#undef DEFAULT_HIDE_WIT_DESK_ON_JOIN
#undef SERVER_NAME
#undef SERVER_IP

int TCP_LISTEN_PORT = 27090;
int WS_LISTEN_PORT = 27019;

uint16_t maxPlayers = 100;
uint16_t maxConnections = 150;
char ASS_PACKET_STRING[256] = "";
char MOTD[512] = "";
char SERVER_FULL_MESSAGE[128] = "Server is full!";
char MODPASS[64] = "changeme";

char SERVER_IP[64] = "127.0.0.1";
char SERVER_NAME[128] = "An Unnamed Server";
char SERVER_DESCRIPTION[256] = "";

char MASTER_HOST[128] = "servers.aceattorneyonline.com";
char MASTER_PATH[128] = "/servers";

uint8_t MAX_ALLOWED_MULTICLIENT = 10;
uint8_t LOGGING = 0;
uint8_t ADVERTISE = 0;
uint8_t DEFAULT_AUTOPAIR = 0;
uint8_t DEFAULT_HIDE_WIT_DESK_ON_JOIN = 0;

Player *players;
Conn *conns;


void lists_init(void) {
  FILE *file = fopen("config/charlist.txt", "r");
  
  char buffer[MAX_LINE];
  size_t used = sprintf(CHAR_LIST_PACKET, "SC#");
  while (fgets(buffer, sizeof(buffer), file)) {
      buffer[strcspn(buffer, "\n")] = 0;
      char **temp = realloc(charList, (charCount + 1) * sizeof(char *));
      charList = temp;
      charList[charCount] = strdup(buffer);
      used += sprintf(CHAR_LIST_PACKET + used, "%s#", charList[charCount]);
      charCount++;
  }
  sprintf(CHAR_LIST_PACKET + used, "%%");

  fclose(file);
  file = fopen("config/musiclist.txt", "r");

  while (fgets(buffer, sizeof(buffer), file)) {
        buffer[strcspn(buffer, "\n")] = 0;

        char *name = buffer;
        char *realName = strstr(buffer, " | ");
        if (realName) {
            *realName = '\0';
            realName++;
            while(*realName == '|' || *realName == ' ') realName++;
        }

        Song *temp = realloc(musicList, (musicCount + 1) * sizeof(Song));
        musicList = temp;
        musicList[musicCount].name = strdup(name);
        musicList[musicCount].realName = realName ? strdup(realName) : strdup("");
        musicCount++;
    }
  fclose(file);

  file = fopen("config/areas.txt", "r");
  while (fgets(buffer, sizeof(buffer), file)) {
    buffer[strcspn(buffer, "\n")] = 0;
    if(*buffer == '\0') continue;
    if(*buffer == '>'){
      char *areaName = buffer + 1;
      Area *temp = realloc(areaList, (areaCount + 1) * sizeof(Area));
      areaList = temp;
      areaList[areaCount] = (Area){0};
      areaList[areaCount].name = strdup(areaName);
      areaCount++;
      continue;
    }

    char *propName = buffer;
    char *propVal = strchr(buffer, '=');
    if(!propVal) continue;
    *propVal = '\0'; propVal++;
    while(*propVal == ' ' || *propVal == ':') propVal++;
    checkAndSetAreaProp(propName, propVal);
  }
  fclose(file);

  file = fopen("config/config.txt", "r");
  if (!file) return;

  while (fgets(buffer, sizeof(buffer), file)) {
      buffer[strcspn(buffer, "\n")] = 0;

      if (*buffer == '\0' || *buffer == '#') continue;

      char *key = buffer;
      char *value = strchr(buffer, '=');
      if (!value) continue;

      *value = '\0';
      value++;

      // ints
      if (!strcmp(key, "TCP_LISTEN_PORT")) TCP_LISTEN_PORT = atoi(value);
      else if (!strcmp(key, "WS_LISTEN_PORT")) WS_LISTEN_PORT = atoi(value);
      else if (!strcmp(key, "MAX_PLAYERS")) maxPlayers = atoi(value);
      else if (!strcmp(key, "MAX_CONNECTIONS")) maxConnections = atoi(value);
      else if (!strcmp(key, "MAX_ALLOWED_MULTICLIENT")) MAX_ALLOWED_MULTICLIENT = atoi(value);
      else if (!strcmp(key, "LOGGING")) LOGGING = atoi(value);
      else if (!strcmp(key, "ADVERTISE")) ADVERTISE = atoi(value);
      else if (!strcmp(key, "DEFAULT_AUTOPAIR")) DEFAULT_AUTOPAIR = atoi(value);
      else if (!strcmp(key, "DEFAULT_HIDE_WIT_DESK_ON_JOIN")) DEFAULT_HIDE_WIT_DESK_ON_JOIN = atoi(value);

      // strings
      else if (!strcmp(key, "SERVER_DESCRIPTION")) strncpy(SERVER_DESCRIPTION, value, sizeof(SERVER_DESCRIPTION));
      else if (!strcmp(key, "ASSET_LINK")) sprintf(ASS_PACKET_STRING, "ASS#%s#%%", value);
      else if (!strcmp(key, "MOTD")) strncpy(MOTD, value, sizeof(MOTD));
      else if (!strcmp(key, "SERVER_FULL_MESSAGE")) strncpy(SERVER_FULL_MESSAGE, value, sizeof(SERVER_FULL_MESSAGE));
      else if (!strcmp(key, "MODPASS")) strncpy(MODPASS, value, sizeof(MODPASS));
      else if (!strcmp(key, "SERVER_IP")) strncpy(SERVER_IP, value, sizeof(SERVER_IP) - 1);
      else if (!strcmp(key, "SERVER_NAME")) strncpy(SERVER_NAME, value, sizeof(SERVER_NAME) - 1);
      else if (!strcmp(key, "SERVER_DESCRIPTION")) strncpy(SERVER_DESCRIPTION, value, sizeof(SERVER_DESCRIPTION) - 1);
      else if (!strcmp(key, "MASTER_HOST")) strncpy(MASTER_HOST, value, sizeof(MASTER_HOST) - 1);
      else if (!strcmp(key, "MASTER_PATH")) strncpy(MASTER_PATH, value, sizeof(MASTER_PATH) - 1);
  }
  players = malloc(sizeof(Player) * maxPlayers);
  conns = malloc(sizeof(Conn) * maxConnections);

  fclose(file);
}

void advertise() {
  lastAdvertised = time(NULL);

  struct addrinfo hints, *res;
  int sockfd;

  // -------- JSON --------
  char json[1024];

  int json_len = snprintf(json, sizeof(json),
  "{"
  "\"ip\":\"%s\","
  "\"port\":%d,"
  "\"ws_port\":%d,"
  "\"players\":%u,"
  "\"name\":\"%s\","
  "\"description\":\"%s\""
  "}",
    SERVER_IP,
    TCP_LISTEN_PORT,
    WS_LISTEN_PORT,
    playerCount,
    SERVER_NAME,
    SERVER_DESCRIPTION
  );

  if (json_len < 0 || json_len >= (int)sizeof(json)) {
      fprintf(stderr, "JSON overflow\n");
      return;
  }

  // -------- REQUEST --------
  char request[2048];

  int req_len = snprintf(request, sizeof(request),
      "POST %s HTTP/1.1\r\n"
      "Host: %s\r\n"
      "Content-Type: application/json\r\n"
      "Content-Length: %d\r\n"
      "Connection: close\r\n"
      "\r\n"
      "%s",
      MASTER_PATH,
      MASTER_HOST,
      json_len,
      json
  );

  if (req_len < 0 || req_len >= (int)sizeof(request)) {
      fprintf(stderr, "Request overflow\n");
      return;
  }

  // -------- SOCKET --------
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;

  if (getaddrinfo(MASTER_HOST, "80", &hints, &res) != 0) {
      perror("getaddrinfo");
      return;
  }

  sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
  if (sockfd < 0) {
      perror("socket");
      freeaddrinfo(res);
      return;
  }

  if (connect(sockfd, res->ai_addr, res->ai_addrlen) < 0) {
      perror("connect");
      closeSocket(sockfd);
      freeaddrinfo(res);
      return;
  }

  // -------- SEND --------
  int sent = 0;
  while (sent < req_len) {
      int n = send(sockfd, request + sent, req_len - sent, 0);
      if (n <= 0) {
          perror("send");
          break;
      }
      sent += n;
  }

  // -------- CLEANUP --------
  closeSocket(sockfd);
  freeaddrinfo(res);
}

#endif
