/*
    ac.c - aircraft trace
    Copyright (C) 2013-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.

    H=D5-RZ
    D5=RZ/C5
    C5=COS(B5)
    B5=$D$2*i*PI/($A$2*180)
    D2=QRB*360/(2*PI*RZ)

*/

//mapView.modules.aircraft.instance.aircrafts.326c7192

#include "ac.h"

#include <libzia.h>

#include "header.h"

#include "fifo.h"
#include "language2.h"
#include "main.h"
#include "map.h"
#include "qrvdb.h"
#include "qsodb.h"
#include "rc.h"
#include "rotar.h"
#include "session.h"
#include "subwin.h"
#include "tsdl.h"

#include "icao2wingspan.inc"

#ifdef HAVE_MATH_H
#define _USE_MATH_DEFINES // for MSVC
#endif

struct its{
    double kx, ky;
    double qrb;
};


struct acs *gacs;

struct acs *init_acs(void){
    struct acs *acs;
    char *wwl;

    acs = g_new0(struct acs, 1);
    progress(VTEXT(T_STARTING_AC));

    acs->dead_minutes = 5;

    if (!cfg->ac_enable) return NULL;
    switch (cfg->ac_provider) {
        case AC_OSN:
            acs_osn_init(acs);
            break;

        case AC_FILE:
            acs_file_init(acs);
            break;

        case AC_ADSBONE:
            acs_adsbone_init(acs);
            break;
        
        case AC_AIRSCATTER_DK:
            acs_airdk_init(acs);
            break;

        default:
            log_addf(TRANSLATE("Unknown AS provider"));
            break;
    }



#ifdef Z_HAVE_SDL
    if (sdl){
        acs->normal = z_makecol(128, 128, 128);
        acs->active = z_makecol(0, 160, 224);
    }
#endif
    wwl = cfg->pwwlo;
    if (ctest) wwl = ctest->pwwlo;
    acs->sh.myh = qth(wwl, 0);
    acs->sh.myw = qth(wwl, 1);
    acs->sh.filename = g_strdup(cfg->ac_filename);

    init_acs_infos(acs);
    acs->unknownids = g_hash_table_new(g_int_hash, g_int_equal);
    MUTEX_INIT(acs->unknownids);
    MUTEX_INIT(acs->sh);
    acs->thread = g_thread_try_new("ac", acs_thread, (gpointer)acs, NULL);
    return acs;
}

void free_acs(struct acs *acs){
    if (!acs) return;
    
    progress(VTEXT(T_STOPPING_AC));
    if (acs->thread){
        acs->thread_break = 1;
        g_thread_join(acs->thread);
    }

    MUTEX_FREE(acs->unknownids);
    g_hash_table_destroy(acs->unknownids);

    acs_osn_free(acs);
    acs_file_free(acs);
    acs_adsbone_free(acs);
    acs_airdk_free(acs);

    zg_free0(acs->sh.filename);
    g_free(acs->infolocqso.locator);
    zg_ptr_array_free_all(acs->sh.acs);
    free_acs_infos(acs);
    MUTEX_FREE(acs->sh);
    g_free(acs);
}



// thread
void ac_compute(struct acs *acs, struct ac *ac, struct ac_counterpart *ctp, struct ac_result *res){
    double h, w, qrb, qtf;
    int i, ret, retin, ii, ari;
    time_t now, dt;
    double h2, w2, ikx, iky, iqrb, iqtf, minasl;
    struct its its[2], *a, *b;

   // dbg("ac: w=%5.3f  h=%5.3f  qtf=%3.0f  asl=%d  speed=%d  when=%ld  now=%ld  kx=%3.0f  ky=%3.0f\n", 
   //         ac->w * 180.0 / M_PI, ac->h * 180.0 / M_PI, ac->qtf * 180.0 / M_PI, ac->asl, ac->speed, (long)ac->when, (long)time(NULL), ac->kx, ac->ky);

    res->under = 0;
    
    now = time(NULL);
    dt = now - ac->when;
    //dbg("dt=%d\n", (int)dt);
    ac->flyingdutchman = (dt > gacs->dead_minutes * 60);
    qrbqtf2hw(ac->h, ac->w, ac->speed * dt / 3600.0, ac->qtfdir + (90 * M_PI / 180.0), &h, &w);
    hw2qrbqtf(acs->th.myh, acs->th.myw, h, w, &qrb, &ac->qtf); 
    ac->kx = (int)round(qrb * sin(ac->qtf));
    ac->ky = (int)round(-qrb * cos(ac->qtf));
   // dbg("%5.3f %5.3f -> %5.3f %5.3f = %f %f %f | %d %d\n", mh, mw, ac->h, ac->w, qrb, qtfrad, (qtfrad * 180/M_PI + 90), ac->kx, ac->ky);
    //dbg("qtf=%f %f speed=%d\n", ac->qtf, ac->qtf * 180.0 / M_PI, ac->speed);
    
    qrbqtf2hw(h, w, ac->speed * 10.0 / 60.0, ac->qtfdir + (90 * M_PI / 180.0), &h2, &w2);
    hw2qrbqtf(acs->th.myh, acs->th.myw, h2, w2, &qrb, &qtf);
    ac->pakx = (int)round(qrb * sin(qtf));
    ac->paky = (int)round(-qrb * cos(qtf));
    //dbg("h=%f  w=%f | kx=%d  ky=%d \n", h * 180.0/M_PI, w * 180.0/M_PI, ac->kx[i], ac->ky[i]);

    ac->is_computed = 1;

#ifdef Z_HAVE_SDL
    ii = 0;
    retin = z_point_is_in_quadrangle(ac->kx, ac->ky, ctp->arkx[0], ctp->arky[0], ctp->arkx[1], ctp->arky[1], ctp->arkx[2], ctp->arky[2], ctp->arkx[3], ctp->arky[3]);
    if (retin > 0) {
        its[ii].kx = ac->kx;
        its[ii].ky = ac->ky;
        its[ii].qrb = 0;
        ii++;
    }

    for (ari = 0; ari < 4; ari++){ 
        ret = z_line_intersect(ctp->arkx[ari], ctp->arky[ari], ctp->arkx[(ari + 1) % 4], ctp->arky[(ari + 1) % 4],    
                                ac->kx, ac->ky, ac->pakx, ac->paky, &ikx, &iky);
        if (ret <= 0) continue;
        its[ii].kx = ikx;
        its[ii].ky = iky;
        its[ii].qrb = sqrt(z_sqr(ikx - ac->kx) + z_sqr(iky - ac->ky));
        ii++;
        if (ii == 2) break;
    }
    for (i = 0; i < ii; i++){
        km2qrbqtf(its[i].kx, its[i].ky, &iqrb, &iqtf);
        minasl = acs_min_asl(acs, iqrb, aband, cfg->ac_minelev * M_PI / 180.0);
        if (ac->asl < minasl * 1000.0) {
            res->crossing = 0;
            res->under = 1;
            return;
        }
    }
    if (ii == 0){
        res->crossing = 0;
        return;
    }

    res->crossing = 1;
    if (ii == 1){
        res->crossing = 0;
        //dbg("ac_compute: strange, ii=%d\n", ii);
        return;
    }

    if (its[0].qrb < its[1].qrb){
        a = its + 0;
        b = its + 1;
    }else{
        a = its + 1;
        b = its + 0;
    }

    res->startkx = a->kx;
    res->startky = a->ky;
    res->start = a->qrb * 3600 / ac->speed;

    res->stopkx = b->kx;
    res->stopky = b->ky;
    res->stop = b->qrb * 3600 / ac->speed;

    //dbg("ac_compute: crossing %3.0f..%3.0f km, %ld..%ld seconds (retin=%d)\n", a->qrb, b->qrb, (long)res->start, (long)res->stop, retin);
    res->start += now;
    res->stop += now;

    res->now = now;
    res->angle = ctp->qtfrad - (ac->qtfdir + M_PI / 2);
    while (res->angle < 0.0) res->angle += 2 * M_PI;
    while (res->angle > 2 * M_PI) res->angle -= 2 * M_PI;
    if (res->angle > M_PI) res->angle = res->angle - M_PI;
    if (res->angle > M_PI / 2) res->angle = M_PI - res->angle;
#endif    
}

void ac_compute_area(struct acs *acs, struct ac_counterpart *ctp){
    double h, w, hh, ww;

    //dbg("ac_compute_area myh=%5.3f  myw=%5.3f  qrb=%3.0f  qtf=%3.0f\n", acs->th.myh, acs->th.myw, ctp->qrb, ctp->qtfrad);
    //double asl0 = acs_min_asl(sw->acs, qso->qrb, band, 0.0 * M_PI / 180.0);
    //double asl5 = acs_min_asl(sw->acs, 127.617, band, 5.0 * M_PI / 180.0);

    ctp->minqrb = acs_max_qrb(acs, 13 /* TODO */, NULL /*band*/, cfg->ac_maxelev * M_PI / 180.0);
    ctp->maxqrb = acs_max_qrb(acs, 13 /* TODO */, NULL /*band*/, cfg->ac_minelev * M_PI / 180.0);
    if (ctp->minqrb > ctp->qrb) ctp->minqrb = ctp->qrb;
    if (ctp->maxqrb > ctp->qrb) ctp->maxqrb = ctp->qrb;


    ctp->minqrb = Z_MAX(ctp->minqrb, ctp->qrb - ctp->maxqrb);
    ctp->maxqrb = Z_MIN(ctp->maxqrb, ctp->qrb - ctp->minqrb);

    if (ctp->maxqrb > ctp->qrb) ctp->maxqrb = ctp->qrb;
    if (ctp->maxqrb < ctp->qrb / 2){
        ctp->path_valid = -1;
        //dbg("short maxqrb\n");
        return;
    }
    ctp->path_valid = 1;

    qrbqtf2hw(acs->th.myh, acs->th.myw, ctp->maxqrb, ctp->qtfrad, &h, &w);
    hw2km_d(acs->th.myh, acs->th.myw, h, w, &ctp->arm1kx, &ctp->arm1ky);

    qrbqtf2hw(h, w, cfg->ac_arwidth/2.0, ctp->qtfrad + M_PI/2.0, &hh, &ww);
    hw2km_d(acs->th.myh, acs->th.myw, hh, ww, &ctp->arkx[0], &ctp->arky[0]);

    qrbqtf2hw(h, w, cfg->ac_arwidth/2.0, ctp->qtfrad - M_PI/2.0, &hh, &ww);
    hw2km_d(acs->th.myh, acs->th.myw, hh, ww, &ctp->arkx[1], &ctp->arky[1]);

    
    qrbqtf2hw(acs->th.myh, acs->th.myw, ctp->qrb - ctp->maxqrb, ctp->qtfrad, &h, &w);
    hw2km_d(acs->th.myh, acs->th.myw, h, w, &ctp->arm2kx, &ctp->arm2ky);

    qrbqtf2hw(h, w, cfg->ac_arwidth/2.0, ctp->qtfrad - M_PI/2.0, &hh, &ww);
    hw2km_d(acs->th.myh, acs->th.myw, hh, ww, &ctp->arkx[2], &ctp->arky[2]);

    qrbqtf2hw(h, w, cfg->ac_arwidth/2.0, ctp->qtfrad + M_PI/2.0, &hh, &ww);
    hw2km_d(acs->th.myh, acs->th.myw, hh, ww, &ctp->arkx[3], &ctp->arky[3]);
    
}

#ifdef Z_HAVE_SDL
void plot_path(struct subwin *sw, SDL_Surface *surface, struct band *band, struct qso *qso, struct ac_counterpart *ctp){
    int px1, py1, px2, py2, px3, py3, px4, py4, px, py, px11, py11, px12, py12;
    
    //dbg("plot_path %d\n", sw->acs->path_valid);
    if (!ctp->path_valid) return;

    km2px_d(sw, qso->kx, qso->ky, &px, &py);

    if (ctp->path_valid < 0){
        // todo grey path
        z_line(surface, sw->ox, sw->oy, px, py, gacs->normal);
        return;
    }

    km2px_d(sw, ctp->arm1kx, ctp->arm1ky, &px11, &py11);
    km2px_d(sw, ctp->arkx[0], ctp->arky[0], &px1, &py1);
    km2px_d(sw, ctp->arkx[1], ctp->arky[1], &px2, &py2);
    km2px_d(sw, ctp->arm1kx, ctp->arm1ky, &px12, &py12);
    km2px_d(sw, ctp->arkx[2], ctp->arky[2], &px3, &py3);
    km2px_d(sw, ctp->arkx[3], ctp->arky[3], &px4, &py4);

    
    
    z_line(surface, sw->ox, sw->oy, px12, py12, gacs->normal);
    z_line(surface, px11, py11, px, py, gacs->normal);
    
    z_line(surface, px1, py1, px2, py2, sdl->yellow);
    z_line(surface, px2, py2, px3, py3, sdl->yellow);
    z_line(surface, px3, py3, px4, py4, sdl->yellow);
    z_line(surface, px4, py4, px1, py1, sdl->yellow);
    
    
}

void ac_zorder(struct subwin *sw, struct ac *ac){
    int px, py;
    int vx,vy,vx1,vy1,vx2,vy2;
    int undercursor;
    double rad, r;
    struct ac_counterpart *ctp = sw->type == SWT_KST ? &gacs->infoqso : &gacs->tmpqso;
    struct ac_result *res = sw->type == SWT_KST ? &ac->infores : &ac->tmpres;

    km2px_d(sw, ac->kx, ac->ky, &px, &py);
    r = 16;
    rad = atan2((double)(ac->paky - ac->ky), (double)(ac->pakx - ac->kx));

    vx  = (int)round(px + 0.6*r * cos(rad) + 0.5);
    vy  = (int)round(py + 0.6*r * sin(rad) + 0.5);
    vx1 = (int)round(px - 0.4*r * cos(rad + 0.6) + 0.5);
    vy1 = (int)round(py - 0.4*r * sin(rad + 0.6) + 0.5);
    vx2 = (int)round(px - 0.4*r * cos(rad - 0.6) + 0.5);
    vy2 = (int)round(py - 0.4*r * sin(rad - 0.6) + 0.5);

    undercursor = 0;
    if (sw->mx >= z_min3_d(vx, vx1, vx2) && 
        sw->my >= z_min3_d(vy, vy1, vy2) &&
        sw->mx <= z_max3_d(vx, vx1, vx2) &&
        sw->my <= z_max3_d(vy, vy1, vy2)) undercursor = 1;

    if (undercursor){
        ac->zorder = ACZO_CURSOR;
    }else if (ctp->path_valid == 1 && res->under){
        ac->zorder = ACZO_LOWALT;
    }else if (ctp->path_valid == 1 && res->crossing){
        ac->zorder = ACZO_CROSSING;
    }else{
        ac->zorder = ACZO_NORMAL;
    }
}

// main thread, acs->sh locked
void plot_ac(struct subwin *sw, struct ac *ac){
    int px, py, opx, opy/*, bpx, bpy*/;
    double rad,r;
    int vx,vy,vx1,vy1,vx2,vy2;
    int fcolor, bcolor, undercursor;
    struct ac_counterpart *ctp = sw->type == SWT_KST ? &gacs->infoqso : &gacs->tmpqso;
    struct ac_result *res = sw->type == SWT_KST ? &ac->infores : &ac->tmpres;

    if (!ac->is_computed) return;

    //todo outline
    km2px_d(sw, ac->kx, ac->ky, &px, &py);
    //r = acs_get_wingspan(gacs, ac->icao) / 8;
    r = ac->wingspan / 8;
    if (r == 0) r = 16; 
    if (sw->zoom < 10000) r = (r * sw->zoom) / 10000;
    
    //dbg("qtf=%f %f \n", ac->qtf, ac->qtf * 180.0 / M_PI);

    rad = atan2((double)(ac->paky - ac->ky), (double)(ac->pakx - ac->kx));
    
    vx  = (int)round(px + 0.6*r * cos(rad) + 0.5);
    vy  = (int)round(py + 0.6*r * sin(rad) + 0.5);
    vx1 = (int)round(px - 0.4*r * cos(rad + 0.6) + 0.5);
    vy1 = (int)round(py - 0.4*r * sin(rad + 0.6) + 0.5);
    vx2 = (int)round(px - 0.4*r * cos(rad - 0.6) + 0.5);
    vy2 = (int)round(py - 0.4*r * sin(rad - 0.6) + 0.5);


    undercursor = 0;
    if (sw->mx >= z_min3_d(vx, vx1, vx2) && 
        sw->my >= z_min3_d(vy, vy1, vy2) &&
        sw->mx <= z_max3_d(vx, vx1, vx2) &&
        sw->my <= z_max3_d(vy, vy1, vy2)) undercursor = 1;
    if (undercursor){
        fcolor = z_makecol(21, 193, 106);
        bcolor = z_makecol(15, 150, 70);
    }else if (/*ac->id &&*/ *ac->id && strcmp(ac->id, gacs->sh.tracked) == 0){
        fcolor = z_makecol(255, 0, 255);
        bcolor = z_makecol(192, 0, 192);
    }else if (ctp->path_valid == 1 && res->under){
        fcolor = z_makecol(128, 128, 128);
        bcolor = z_makecol(64, 64, 64);
    }else if (ctp->path_valid == 1 && res->crossing){
        fcolor = z_makecol(0, 160, 224);
        bcolor = z_makecol(0, 120, 195);
    }else{
        fcolor = z_makecol(192, 192, 192);
        bcolor = z_makecol(128, 128, 128);
    }
   
    if (ac->flyingdutchman) {
        z_do_line(sw->screen, vx, vy, vx1, vy1, fcolor, z_dotfce);
        z_do_line(sw->screen, vx, vy, vx2, vy2, fcolor, z_dotfce);
        z_do_line(sw->screen, vx1, vy1, vx2, vy2, fcolor, z_dotfce);
    }
    else {
        if (ac->wingspan > 0) {
            z_triangle(sw->screen, vx, vy, vx1, vy1, vx2, vy2, bcolor);
        }
        z_line(sw->screen, vx, vy, vx1, vy1, fcolor);
        z_line(sw->screen, vx, vy, vx2, vy2, fcolor);
        z_line(sw->screen, vx1, vy1, vx2, vy2, fcolor);
        z_putpixel(sw->screen, px, py, fcolor);
    }

    vx  = (int)round(px + 0.6*r * cos(rad));
    vy  = (int)round(py + 0.6*r * sin(rad));
    vx1 = (int)round(px - 0.4*r * cos(rad + 0.6));
    vy1 = (int)round(py - 0.4*r * sin(rad + 0.6));
    vx2 = (int)round(px - 0.4*r * cos(rad - 0.6));
    vy2 = (int)round(py - 0.4*r * sin(rad - 0.6));

    if (undercursor)    
    {
        opx = px;
        opy = py;
        km2px_d(sw, ac->pakx, ac->paky, &px, &py);
        z_line(sw->screen, opx, opy, px, py, fcolor);

        if (!sw->ac_plotinfo) plot_info_ac(sw, sw->screen, ac);
        return;
    }

    if (!res->crossing) return;
    
    if (!ctp->path_valid) return; 

    opx = px;
    opy = py;
    km2px_d(sw, res->startkx, res->startky, &px, &py);
    z_line(sw->screen, opx, opy, px, py, gacs->normal);
    opx = px;
    opy = py;

    km2px_d(sw, res->stopkx, res->stopky, &px, &py);
    z_line(sw->screen, opx, opy, px, py, gacs->active);
    opx = px;
    opy = py;

    km2px_d(sw, ac->pakx, ac->paky, &px, &py);
    z_line(sw->screen, opx, opy, px, py, gacs->normal);

}

void plot_info_ac(struct subwin *sw, SDL_Surface *surface, struct ac *ac){
    int x, y, color;
    SDL_Rect rect, oldrect;
    struct tm utc;
    time_t diff; 
    struct ac_result *res;

    x = sw->info.x + 4;
    //y = sw->info.y + 4 + 18 * FONT_H;
    y = sw->info.y;

    rect.x = sw->info.x;  // same as plot_info_qso
    rect.y = y;
    rect.w = sw->info.w;
    //rect.h = sw->y - y;
    rect.h = sw->info.y + (11 * FONT_H) + 12;
    
    
    SDL_GetClipRect(sw->screen, &oldrect);
    SDL_SetClipRect(sw->screen, &rect);
    SDL_FillRect(sw->screen, &rect, z_makecol(0,0,35)) ;

    if (ac){
        sw->ac_plotinfo = 1;
        color=z_makecol(21, 193, 106);
        zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, "ICAO24: %s", ac->id); y += FONT_H + 4;

        if (cfg->ac_imperial){
            zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, "ASL: %5d ft", ac->feets); y += FONT_H + 4;
            zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, TRANSLATE("Speed: %3d kt"), ac->knots); y += FONT_H + 4;
        }else{
            zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, VTEXT(T_ASL_5D_M), ac->asl); y += FONT_H + 4;
            zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, VTEXT(T_SPEED4D_KMH), ac->speed); y += FONT_H + 4;
        }

        if (/*ac->icaotype != NULL &&*/ strlen(ac->icaotype) > 0) {
            zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, TRANSLATE("Type:  %s")/*VTEXT(T_SPEED4D_KMH)*/, ac->icaotype); y += FONT_H + 4;
        }
        if (/*ac->callsign != NULL &&*/ strlen(ac->callsign) > 0) {
            zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, TRANSLATE("Call:  %s")/*VTEXT(T_SPEED4D_KMH)*/, ac->callsign); y += FONT_H + 4;
        }
        if (ac->wingspan == 0) {
            zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, TRANSLATE("WingS: unknown")); y += FONT_H + 4;
        }else{
            if (cfg->ac_imperial) {
                zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, TRANSLATE("WingS: %d ft"), ac->wingspan); y += FONT_H + 4;
            }else{
                zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, TRANSLATE("WingS: %d m"), (int)round(ac->wingspan * 0.3048)); y += FONT_H + 4;
            }
        }
        time_t upd = time(NULL) - ac->when;
        zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, TRANSLATE("Updated: %d min"), upd / 60); y += FONT_H + 4;
        //zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, TRANSLATE("%3.1f %3.1f"), ac->w*180.0/M_PI, ac->h * 180.0 / M_PI); y += FONT_H + 4;
        //zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, TRANSLATE("%3.1f %3.1f"), ac->kx, ac->ky); y += FONT_H + 4;


        res = sw->type == SWT_KST ? &ac->infores : &ac->tmpres;
        if (res->start != (time_t)0){
            diff = res->start - time(NULL);
            gmtime_r(&res->start, &utc);

            zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, VTEXT(T_AC_START), utc.tm_hour, utc.tm_min, ((long)diff) / 60, ((long)diff) % 60); y+=FONT_H+4;
            zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, VTEXT(T_AC_DURAT_LD), (long)(res->stop - res->start)); y+=FONT_H+4;
            //zsdl_printf(sw->screen, x, y, color, 0, ZFONT_TRANSP, "Angle: %d", (int)(res->angle * 180.0 / M_PI)); y+=FONT_H+4;
        }
    }
    SDL_SetClipRect(sw->screen, &oldrect);
}


void acs_redraw(struct subwin *sw, struct acs *acs){
    struct ac *ac;
    int i, z;

    //dbg("------- acs_redraw\n");
    if (!gacs) return;

    MUTEX_LOCK(acs->sh);
    sw->ac_plotinfo = 0;
    if (acs->sh.acs){
        for (i = 0; i < acs->sh.acs->len; i++){
            ac = (struct ac *)g_ptr_array_index(acs->sh.acs, i);
            ac_zorder(sw, ac);
        }

        for (z = ACZO_LOWALT; z <= ACZO_CURSOR; z++){
            for (i = 0; i < acs->sh.acs->len; i++){
                ac = (struct ac *)g_ptr_array_index(acs->sh.acs, i);
                if (ac->zorder == z) plot_ac(sw, ac);
            }
        }
    }
    if (!sw->ac_plotinfo) plot_info_ac(sw, sw->screen, NULL);
    MUTEX_UNLOCK(acs->sh);

}
#endif


double acs_min_asl(struct acs *acs, double qrb, struct band *b, double elev_rad){
    double k = 1.0;

    /*if (b && b->ac_k == 0.0) ac_fill_band_ac_k(b);
    k = b ? b->ac_k : 0.8;*/

    return k * ZLOC_R_EARTH * cfg->ac_kfactor * ( 1 / cos(qrb / (ZLOC_R_EARTH * cfg->ac_kfactor)) - 1) + qrb * sin(elev_rad);
}

double acs_max_qrb(struct acs *acs, double asl, struct band *b, double elev_rad){
    double k = 1.0, asl2, qrb;

    /*if (b && b->ac_k == 0.0) ac_fill_band_ac_k(b);
    k = b ? b->ac_k : 0.8;*/                         

    if (elev_rad <= 0.0){
        return ZLOC_R_EARTH * cfg->ac_kfactor * acos( 1 / (1 + (asl * k)/(ZLOC_R_EARTH * cfg->ac_kfactor)));
    }
    
    qrb = 0;
    while(1){
        asl2 = acs_min_asl(acs, qrb, b, elev_rad);
        if (asl2 > asl) return qrb;
        qrb += 5.0;
    }

    
    //return qrb;
}

#if 0
void acs_http_timer(void *arg){
    struct acs *acs = (struct acs *)arg;


    acs->http_timer = 0;
    if (acs->http){
        zhttp_free(acs->http);
        acs->http = NULL;
    }
    
    acs->http = zhttp_init();
    if (acs->url == NULL){
        char *url = g_strdup_printf("tucnak.vaiz.cz/acurl.php?mycall=%s&mywwl=%s", ctest ? ctest->pcall : cfg->pcall, ctest ? ctest->pwwlo : cfg->pwwlo);
        zhttp_get(acs->http, zsel, url, acs_goturl, acs);
        g_free(url);
    /* } else if (strcmp(acs->url, "planefinder") == 0) {
        char* url = g_strdup_printf("https://data-live.planefinder.net/api/aircraft/live/bounds/27,-43,63,34/%d", time(NULL));
        dbg("url='%s'\n", url);
        zhttp_get(acs->http, zsel, url, acs_downloaded_callback, acs);
        g_free(url);*/
    }else{
        zhttp_get(acs->http, zsel, acs->url, acs_downloaded_callback, acs);
    }
    acs->http_timer = zselect_timer_new(zsel, 50*1000, acs_http_timer, acs); // restart when freezes

    //dbg("acs_timer: zhttp_get\n");
}

void acs_goturl(struct zhttp *http){
    char *data, *status, *url;
    int remains = 10000;
    struct acs *acs = (struct acs *)http->arg;

    //dbg("acs_goturl\n");

    if (acs->http_timer){
        zselect_timer_kill(zsel, acs->http_timer);
        acs->http_timer = 0;
    }

    if (http_is_content_type(http, "text/plain;charset=UTF-8")){
        data = g_new(char, http->response->len + 1);
        zbinbuf_getstr(http->response, http->dataofs, data, http->response->len + 1);

        if (acs->http->errorstr == NULL){
            z_split2(data, ';', &status, &url, 0);
            if (status != NULL && status[0] == 'A'){
                remains = 1;
                acs->url = g_strdup(url);
                g_free(status);
                g_free(url);
            }
        }
        g_free(data);
    }
    zhttp_free(http);
    acs->http = NULL;

    acs->http_timer = zselect_timer_new(zsel, remains, acs_http_timer, acs);
}

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

    //dbg("acs_downloaded_callback\n");

    if (acs->http_timer){
        zselect_timer_kill(zsel, acs->http_timer);
        acs->http_timer = 0;
    }

    if (http->state == ZHTTPST_ERROR && http->errorstr){
        dbg("ERROR acs_downloaded_callback: %s\n", http->errorstr);
        zhttp_free(http);
        acs->http = NULL;
    }else{
        if (http_is_content_type(http, "application/json") || 
            http_is_content_type(http, "text/plain;charset=UTF-8") /* ||
            strcmp(acs->url, "planefinder") == 0 // any*/
           ){
            data = g_new(char, http->response->len + 1);
            zbinbuf_getstr(http->response, http->dataofs, data, http->response->len + 1);
            zhttp_free(http);
            acs->http = NULL;

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

    gettimeofday(&tv, NULL);
    remains = 60000 - ((tv.tv_sec % 60) * 1000 + tv.tv_usec/1000);
    acs->http_timer = zselect_timer_new(zsel, remains, acs_http_timer, acs);
}
#endif
gpointer acs_thread(gpointer arg){
    struct acs *acs = (struct acs *)arg;
    int new_tmpctp = 0;
    int new_infoctp = 0;

    zg_thread_set_name("Tucnak AS");
    while (!acs->thread_break){
        //char *data;
        //int data_len;
        struct ac *ac;
        int i, j;
        char tracked[AC_ID_MAXLEN];
        int rotnr;


        for (i = 0; i < 10; i++) {
            if (acs->sh.new_tmpctp) break;
            if (acs->sh.new_infoctp) break;
            if (acs->thread_break) break;
            usleep(100000);
        }

        //dbg("------acs_thread--------\n");

        MUTEX_LOCK(acs->sh);
        acs->th.myh = acs->sh.myh;
        acs->th.myw = acs->sh.myw;
        MUTEX_UNLOCK(acs->sh);

        if (acs->load_from_thread) acs->load_from_thread(acs);
        //acs_read_feed(acs);

        MUTEX_LOCK(acs->sh);
     
        if (acs->sh.data){ // take data from main thread (http)
            char *data = acs->sh.data;
          //  data_len = acs->sh.data_len;
            acs->sh.data = NULL;
            acs->th.myh = acs->sh.myh;
            acs->th.myw = acs->sh.myw;
            MUTEX_UNLOCK(acs->sh);
        
            acs->th.acs = g_ptr_array_new();

            if (acs->load_from_main_thread) acs->load_from_main_thread(acs, data);

            g_free(data);
            MUTEX_LOCK(acs->sh);
            zg_ptr_array_free_all(acs->sh.acs);
            acs->sh.acs = acs->th.acs;
            acs->th.acs = NULL;
        }
        if (acs->sh.new_tmpctp){
            acs->tmpqso.qrb = acs->sh.tmpctpqrb;
            acs->tmpqso.qtfrad = acs->sh.tmpctpqtf * M_PI / 180.0;
            acs->sh.new_tmpctp = 0;
            new_tmpctp = 1;
        }
        if (acs->sh.new_infoctp){
            acs->infoqso.qrb = acs->sh.infoctpqrb;
            acs->infoqso.qtfrad = acs->sh.infoctpqtf * M_PI / 180.0;
            acs->sh.new_infoctp = 0;
            new_infoctp = 1;
        }
        g_strlcpy(tracked, acs->sh.tracked, AC_ID_MAXLEN);
        rotnr = acs->sh.rotnr;
        MUTEX_UNLOCK(acs->sh);

        if (acs->sh.acs){
            if (new_tmpctp) {
                ac_compute_area(acs, &acs->tmpqso);
                new_tmpctp = 0;
            }
            if (new_infoctp) {
                ac_compute_area(acs, &acs->infoqso);
                new_infoctp = 0;
            }
            //ST_START();
            int qtf = -360;
            for (i = 0; i < acs->sh.acs->len; i++){
                ac = (struct ac *)g_ptr_array_index(acs->sh.acs, i);
                ac_compute(acs, ac, &acs->tmpqso, &ac->tmpres);
                if (strlen(ac->id) > 0 && strcmp(ac->id, tracked) == 0){
                    qtf = (int)round(ac->qtf * 180.0 / M_PI);
                    //dbg("qtf=%d\n", qtf);
                    //if (qtf >= 360) qtf -= 360;
                }
                if (acs->thread_break) break;
            }
            for (i = 0; i < acs->sh.acs->len; i++){
                ac = (struct ac *)g_ptr_array_index(acs->sh.acs, i);
                ac_compute(acs, ac, &acs->infoqso, &ac->infores);
                if (acs->thread_break) break;
            }
            for (j = 0; j < qrv->qrvs->len; j++){
                struct qrv_item *qi;
                time_t start = 0;
                int n = 0;
                time_t interval = 0;
                struct ac_counterpart *ctp = &acs->qrvqso;
                struct ac_result *res;

                if (acs->sh.new_tmpctp) break;
                if (acs->sh.new_infoctp) break;

                MUTEX_LOCK(qrv->qrvs);
                if (j >= qrv->qrvs->len) goto next;

                qi = (struct qrv_item*)g_ptr_array_index(qrv->qrvs, j);
                //if (!qi->ac_drawn) {
                //  //qi->ac_start = (time_t)0;
                //  goto next;
                //}

                /*if (strcmp(qi->call, "S57C") == 0){
                    int aa = 0;
                } */
                //dbg("acs_thread: %3d %s\n", j, qi->call);
                usleep(1000);
                //ctp->qrb = qi->qrb;
                hw2qrbqtf(acs->th.myh, acs->th.myw, qth(qi->wwl, 0), qth(qi->wwl, 1), &ctp->qrb, &ctp->qtfrad); 
                //ctp->qtfrad = qi->qtf * M_PI / 180.0;
                ac_compute_area(acs, ctp);
                if (ctp->path_valid != 1) {
                    qi->ac_start = 0;
                    goto next;
                }

                for (i = 0; i < acs->sh.acs->len; i++){
                    ac = (struct ac *)g_ptr_array_index(acs->sh.acs, i);
                    res = &ac->qrvres;
                    ac_compute(acs, ac, ctp, res);
                    
                    if (res->crossing){
                        if (n == 0){
                            start = res->start;
                        }else{
                            start = Z_MIN(start, res->start);
                        }
                        interval += res->stop - res->start;
                        n++;
                    }
                    if (acs->thread_break) goto next;
                }
                if (interval < cfg->ac_mindur){
                    qi->ac_start = (time_t)0;
                }else{
                    //dbg("SET: %s %d\n", qi->call, qi->ac_interval);
                    qi->ac_start = start;
                    qi->ac_interval = interval;
                    qi->ac_n = n;
                }
next:;
                MUTEX_UNLOCK(qrv->qrvs);
                if (acs->thread_break) break;
            }

            if (qtf > -360){
                MUTEX_LOCK(acs->sh);
#define HYSTERESIS 1
                if (strlen(acs->sh.tracked) > 0){ // prevent race condition when ac_track(NULL) was called during for { ac_compute }
                    struct rotar *rot = get_rotar(rotnr);
                        //dbg("rot_seek(%d, %d)\n", rotnr, qtf);
                    if (abs(rot->qtf - qtf) > HYSTERESIS){
                        rot_seek(rot, qtf, -90);
                    }
                }
                MUTEX_UNLOCK(acs->sh);
            }
            //ST_STOP("ac_recompute");
            zselect_msg_send(zsel, "AC;r");
        }

    }
    return NULL;
}

void acs_update_qth(struct acs *acs){
    if (!acs) return;
    if (!gses) return;

    MUTEX_LOCK(acs->sh);
    if (acs->sh.myh != gses->myh || acs->sh.myw != gses->myw){
        MUTEX_UNLOCK(acs->sh);
        free_acs(acs);
        gacs = init_acs();
        return;
    }
    //acs->sh.myh = sw->myh;
    //acs->sh.myw = sw->myw;
    MUTEX_UNLOCK(acs->sh);

}

void ac_redraw(){
    if (!gses) return;
#ifdef Z_HAVE_SDL
    if (gses->ontop->type == SWT_MAP || gses->ontop->type == SWT_KST){
        gses->ontop->gdirty = 1;
    }
#endif
    redraw_later();
}

void ac_update_tmpctp(char *wwl){
    struct acs *acs;

    if (!gacs) return;
    if (!gses) return;
    if (gses->ontop->type != SWT_MAP && gses->ontop->type != SWT_KST) return;
        
    acs = gacs;
    dbg("ac_update_tmpctp(%s)\n", wwl);
    MUTEX_LOCK(acs->sh);
    if (!wwl || strlen(wwl) < 6){
        acs->tmpqso.path_valid = 0;
    }else{
        qrbqtf(ctest ? ctest->pwwlo : cfg->pwwlo, wwl, 
                &acs->sh.tmpctpqrb, &acs->sh.tmpctpqtf,
                NULL, 0);
        acs->sh.new_tmpctp = 1;
        acs->tmpqso.path_valid = 1;
    }
    MUTEX_UNLOCK(acs->sh);
}

void ac_update_infoctp(char *wwl){
    struct acs *acs;

    if (!gses) return;
    //if (gses->ontop->type != SWT_MAP) return;
        
    acs = gacs;
    dbg("ac_update_infoctp(%s)\n", wwl);
    MUTEX_LOCK(acs->sh);
    if (!wwl || !*wwl){
        acs->infoqso.path_valid = 0;
    }else{
        qrbqtf(ctest ? ctest->pwwlo : cfg->pwwlo, wwl, 
                &acs->sh.infoctpqrb, &acs->sh.infoctpqtf,
                NULL, 0);
        acs->sh.new_infoctp = 1;
        acs->infoqso.path_valid = 1;
    }
    MUTEX_UNLOCK(acs->sh);
}


void ac_format(struct qrv_item *qi, char *acstart, char *acint, int flags){
    struct tm utc;
    time_t diff; 


    if (!gacs || !qi || qi->ac_start == (time_t)0){
        if (flags & 1)
            strcpy(acstart, "              ");
        else
            strcpy(acstart, "     ");
        strcpy(acint, "      ");
    }else{
        diff = qi->ac_start - time(NULL);
        if (diff < 0 && diff > -3) diff = 0; 
        /*if (diff < 0){
            strcpy(acstart, "---");
        }else if (diff >= 3600){
            strcpy(acstart, "+++");
            dbg("diff = %ld\n", (long)diff);
        }else*/{
            gmtime_r(&qi->ac_start, &utc);
            if (flags & 1)
                sprintf(acstart, "%02d:%02d:%02d %02dm%02d", utc.tm_hour, utc.tm_min, utc.tm_sec, (int)((diff / 60) % 60), (int)(diff / 60));
            else
                sprintf(acstart, "%02dm%02d", (int)((diff / 60) % 60), (int)(diff % 60));
        }
        //dbg("%s: %02d:%02d:%02d start=%3ld diff=%3ld\n", qi->call, utc.tm_hour, utc.tm_min, utc.tm_sec, (long)(qi->ac_start % 60), (long)diff);
        //dbg("%s: start=%ld  now=%ld  -=%ld  diff=%ld\n", qi->call, (long)qi->ac_start % 1000, (long)time(NULL) % 1000, (long)qi->ac_start - (long)time(NULL), (long)diff);

        if (qi->ac_interval < 0){
            sprintf(acint, "--- %2d", qi->ac_n);
        }else if (qi->ac_interval > 999){
            sprintf(acint, "+++ %2d", qi->ac_n);
        }else{
            sprintf(acint, "%3ld %2d", (long)qi->ac_interval, qi->ac_n);
        }
        if ((flags & 1) == 0) strcat(acint, " ");
    }

}


void init_acs_infos(struct acs *acs){
    char *token_ptr = NULL;
    char *ic, *wingspan;
    char *buf = g_strdup(txt_aircraft);
    char *buf2 = buf;

    acs->infos = g_ptr_array_new();
    while (1){
        struct ac_info *aci;

        ic = strtok_r(buf2, " ", &token_ptr);
        wingspan = strtok_r(NULL, "\n", &token_ptr);
        buf2 = NULL;

        if (ic == NULL && wingspan == NULL) break;
        if (ic == NULL || wingspan == NULL) continue;
        
        aci = g_new0(struct ac_info, 1);
        g_strlcpy(aci->icaotype, ic, AC_ICAOTYPE_MAXLEN);
        aci->wingspan = atoi(wingspan);
        g_ptr_array_add(acs->infos, aci);
    }
    g_free(buf);
} 

void free_acs_infos(struct acs *acs){
    if (acs->infos == NULL) return;
    
    zg_ptr_array_free_all(acs->infos);
    acs->infos = NULL;
}
    
//int acs_get_wingspan(struct acs *acs, char *icao){
//    int i;
//
//    for (i = 0; i < acs->infos->len; i++){
//        struct ac_info *aci = (struct ac_info *)g_ptr_array_index(acs->infos, i);
//        if (strcmp(aci->icao, icao) == 0) return aci->wingspan;
//    }
//    return 0;
//}

void ac_fill_wingspan(struct acs *acs, struct ac* ac) {
    uint32_t id = strtoul(ac->id, NULL, 16);

    for (const uint32_t* u = icao2wingspan; *u != 0; u++) {
        if ((*u >> 8) == id) {
            ac->wingspan = *u & 0xff;
            return;
        }
    }

    int i;
     
    for (i = 0; i < acs->infos->len; i++){
        struct ac_info *aci = (struct ac_info *)g_ptr_array_index(acs->infos, i);
        if (strcmp(aci->icaotype, ac->icaotype) == 0) ac->wingspan = aci->wingspan;
    }

     /*gpointer p = g_hash_table_lookup(acs->unknownids, GINT_TO_POINTER(id));
     if (p == NULL) {
         g_hash_table_insert(acs->unknownids, GINT_TO_POINTER(id), NULL);
         dbg("unknownids insert %06X\n", id);
     }*/
}

#ifdef Z_HAVE_SDL
struct ac *ac_under(struct acs *acs, struct subwin *sw){
    struct ac *ac, *ret = NULL;
    int i;

    if (!gacs) return NULL;

    MUTEX_LOCK(acs->sh);
    if (acs->sh.acs){
        for (i = 0; i < acs->sh.acs->len; i++){
            ac = (struct ac *)g_ptr_array_index(acs->sh.acs, i);
            ac_zorder(sw, ac);
            if (ac->zorder == ACZO_CURSOR){
                ret = ac;
                break;
            }
        }
    }
    MUTEX_UNLOCK(acs->sh);
    return ret;
}
#endif

void ac_track(struct acs *acs, char *id, int rotnr){
    if (!acs) return;

    MUTEX_LOCK(acs->sh);

    if (id == NULL){
        g_strlcpy(acs->sh.tracked, "", AC_ID_MAXLEN);
        acs->sh.rotnr = -1;
    }else{
        g_strlcpy(acs->sh.tracked, id, AC_ID_MAXLEN);
        acs->sh.rotnr = rotnr;
    }

    MUTEX_UNLOCK(acs->sh);
}


