/*
    acosn.c - openskynetwork provider for AS
    Copyright 2023 Ladislav Vaiz <ok1zia@nagano.cz>

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License
    version 2 as published by the Free Software Foundation.
*/
#include "ac.h"
#include <libzia.h>
#include "header.h"
#include "main.h"
#include "tsdl.h"

int acs_osn_init(struct acs* acs) {
    acs->osn_http_timer = zselect_timer_new(zsel, 1000, acs_osn_http_timer, acs);

    acs->load_from_main_thread = acs_osn_load_from_main_thread;
#ifdef Z_HAVE_SDL
    acs->plot_info = acs_osn_plot_info;
#endif
    acs->osn_requests_remaining = -1;
    acs->dead_minutes = cfg->ac_osn_minutes + 3;
    return 0;
}

void acs_osn_free(struct acs* acs) {
    if (acs->osn_http_timer) zselect_timer_kill(zsel, acs->osn_http_timer);
    zhttp_free(acs->osn_http);
    g_free(acs->osn_http_error);
}

void acs_osn_http_timer(void* arg) {
    struct acs* acs = (struct acs*)arg;
    acs->osn_http_timer = 0;

    time_t now = time(NULL);
    struct tm utc;
    gmtime_r(&now, &utc);

    dbg("\n%02d:%02dz acs_osn_http_timer\n", utc.tm_hour, utc.tm_min);
    const char* mywwl = ctest ? ctest->pwwlo : cfg->pwwlo;
    if (cfg->ac_centerwwl != NULL && strlen(cfg->ac_centerwwl) >= 4 && strcmp(cfg->ac_centerwwl, "(null)") != 0) mywwl = cfg->ac_centerwwl;
    double myh = qth(mywwl, 0);
    double myw = qth(mywwl, 1);
    double lamin, lomin, lamax, lomax, dummy;
    double qrb = cfg->rain_maxqrb;
    qrbqtf2hw(myh, myw, qrb, 180.0 * M_PI / 180.0, &dummy, &lamin);
    qrbqtf2hw(myh, myw, qrb, 270 * M_PI / 180.0, &lomin, &dummy);
    qrbqtf2hw(myh, myw, qrb, 0 * M_PI / 180.0, &dummy, &lamax);
    qrbqtf2hw(myh, myw, qrb, 90 * M_PI / 180.0, &lomax, &dummy);
    lamin *= 180.0 / M_PI;
    lomin *= 180.0 / M_PI;
    lamax *= 180.0 / M_PI;
    lomax *= 180.0 / M_PI;

    char* url = g_strdup_printf("https://opensky-network.org/api/states/all?lamin=%6.4f&lomin=%6.4f&lamax=%6.4f&lomax=%6.4f", lamin, lomin, lamax, lomax);

    acs->osn_http = zhttp_init();
    if (cfg->ac_osn_username && *cfg->ac_osn_username && cfg->ac_osn_password && *cfg->ac_osn_password)
    {
        zhttp_auth_basic(acs->osn_http, cfg->ac_osn_username, cfg->ac_osn_password);
    }
    zhttp_get(acs->osn_http, zsel, url, acs_osn_downloaded_callback, acs);
    g_free(url);
}

void acs_osn_downloaded_callback(struct zhttp* http) {
    char* data;
    int remains;
    struct acs* acs = (struct acs*)http->arg;

    //dbg("acs_osn_downloaded_callback\n");
    
    if (http->state == ZHTTPST_ERROR && http->errorstr) {
        dbg("ERROR acs_osn_downloaded_callback: %s\n", http->errorstr);
        g_free(acs->osn_http_error);
        acs->osn_http_error = g_strdup(http->errorstr);
        zhttp_free(http);
        acs->osn_http = NULL;
    }else {
        if (http_is_content_type(http, "application/json")) {
            dbg("OK acs_osn_downloaded_callback\n");
            data = g_new(char, http->response->len + 1);
            zbinbuf_getstr(http->response, http->dataofs, data, http->response->len + 1);

            char* s = http_get_header(http, "X-Rate-Limit-Remaining");
            if (s != NULL) {
                char* end;
                long r = strtol(s, &end, 10);
                if (end != s) {
                    acs->osn_requests_remaining = (int)r;
                    dbg("osn_requests_remaining=%d\n", acs->osn_requests_remaining);
                }
                g_free(s);
            }

            zhttp_free(http);
            acs->osn_http = NULL;

            MUTEX_LOCK(acs->sh);
            g_free(acs->sh.data);
            acs->sh.data = data;
            MUTEX_UNLOCK(acs->sh);

            zg_free0(acs->osn_http_error);

            remains = cfg->ac_osn_minutes * 60000;
            acs->osn_http_timer = zselect_timer_new(zsel, remains, acs_osn_http_timer, acs);
            return;
        }
        else {
            dbg("ERROR acs_osn_downloaded_callback: Bad Content-Type\n");
            g_free(acs->osn_http_error);
            acs->osn_http_error = g_strdup("Bad Content-Type");
        }
    }

    remains = 60000; // if error retry after 1 minute (longer, maybe server can decrease remaining if reply failed in the middle)
    acs->osn_http_timer = zselect_timer_new(zsel, remains, acs_osn_http_timer, acs);

}

void acs_osn_load_from_main_thread(struct acs* acs, const char* data) {
    struct zjson* json = zjson_init(data);
    struct zjson* states = zjson_get_array(json, "states");
    int n = 0, added = 0;
    while (1) {
        struct zjson* one = zjson_get_array(states, NULL);
        if (one == NULL) break;

        n++;
        char* id = zjson_get_str(one, NULL, NULL); // 0
        char* call = zjson_get_str(one, NULL, NULL); // 1 
        zjson_get_int(one, NULL, 0); // 2 skip orig_contry
        time_t time_pos = zjson_get_epoch(one, NULL, 0); // 3
        /*time_t last_contact =*/ zjson_get_epoch(one, NULL, 0); // 4
        double lon = zjson_get_double(one, NULL, 0); // 5
        double lat = zjson_get_double(one, NULL, 0); // 6
        double baralt = zjson_get_double(one, NULL, 0); // 7 meters
        bool ongound = zjson_get_bool(one, NULL, false); // 8
        double velocity = zjson_get_double(one, NULL, 0); // 9 m/s
        double truetrack = zjson_get_double(one, NULL, 0); // 10
        //double vertrate = zjson_get_double(one, NULL, 0); // 11
        //int sensors = zjson_get_int(one, NULL, 0); // 12
        //double geoalt = zjson_get_double(one, NULL, 0); // 13
        //zjson_get_int(one, NULL, 0); // 14 skip squawk
        //bool spi = zjson_get_bool(one, NULL, false); // 15 special purpose indicator
        //int src = zjson_get_int(one, NULL, 0); // 16
        //int categ = zjson_get_int(one, NULL, 0); // 17

        double alt = baralt;
        if (id != NULL && ongound == false && alt >= cfg->ac_minalt) {

            struct ac* ac = g_new0(struct ac, 1);
            g_strlcpy(ac->id, id, AC_ID_MAXLEN);
            g_strlcpy(ac->callsign, call, AC_CALLSIGN_MAXLEN);
            ac->when = time_pos;
            ac->w = lat * M_PI / 180.0;
            ac->h = lon * M_PI / 180.0;
            ac->qtfdir = (truetrack - 90) * M_PI / 180.0;

            ac->asl = baralt;
            ac->feets = ac->asl / 0.3048;

            ac->speed = velocity * 3600 / 1000;
            ac->knots = ac->speed / 1.852;

            ac_fill_wingspan(acs, ac);

            g_ptr_array_add(acs->th.acs, ac);
            added++;
        }
        g_free(id);
        g_free(call);
        zjson_free(one);
    }

    dbg("downloaded %d, added %d\n", n, added);
    zjson_free(states);
    zjson_free(json);
}

#ifdef Z_HAVE_SDL
void acs_osn_plot_info(struct subwin* sw, SDL_Rect* area, struct acs* acs) {
    SDL_Surface* surface = sdl->screen;
    int c;

    if (acs->osn_http_error != NULL) {
        c = z_makecol(252, 0, 0);
        zsdl_printf(surface, area->x + 50, area->y + 5, c, 0, ZFONT_TRANSP | ZFONT_OUTLINE, TRANSLATE("OpenSkyNetwork error: %s"), acs->osn_http_error);
        return;
    }

    if (acs->osn_requests_remaining < 0) return; // -1 is no information

    if (acs->osn_requests_remaining >= 40)
        c = sdl->gr[11];
    else
        c = z_makecol(252, 0, 0);

    zsdl_printf(surface, area->x + 50, area->y + 5, c, 0, ZFONT_TRANSP | ZFONT_OUTLINE, TRANSLATE("OpenSkyNetwork requests remaining: %d"), acs->osn_requests_remaining);
}
#endif