/*
 * dxlAPRS toolchain
 *
 * Copyright (C) Christian Rabler <oe5dxl@oevsv.at>
 *
 * SPDX-License-Identifier:	GPL-2.0+
 */


#define X2C_int32
#define X2C_index32
#ifndef X2C_H_
#include "X2C.h"
#endif
#define adsb2aprs_C_
#ifndef osi_H_
#include "osi.h"
#endif
#include <osic.h>
#ifndef mlib_H_
#include "mlib.h"
#endif
#ifndef tcp_H_
#include "tcp.h"
#endif
#ifndef udp_H_
#include "udp.h"
#endif
#ifndef aprsstr_H_
#include "aprsstr.h"
#endif
#ifndef tcpb_H_
#include "tcpb.h"
#endif

/* dump1090 tcp output to aprs beacon by OE5DXL */
#define adsb2aprs_TIMETOL 20
/* max seconds between dir/speed and pos */

#define adsb2aprs_PURGETIME 120
/* seconds keep context */

#define adsb2aprs_DEFAULTBEACONTIME 20

#define adsb2aprs_SYMBOL "/^"

#define adsb2aprs_KNOTS 1.852
/* nautic miles */

#define adsb2aprs_FEET 0.3048

typedef char CSV[100][21];

struct FLY;

typedef struct FLY * pFLY;


struct FLY {
   pFLY next;
   char hex[6];
   char name[21];
   float lat;
   float long0;
   float alt;
   float speed;
   float dir;
   uint32_t speedtime;
   uint32_t postime;
   uint32_t lasttime;
   uint32_t lastbeacon;
   char newpos;
};

static char url[1001];

static char port[1001];

static char reconn;

static char verb;

static char verb2;

static int32_t fd;

static pFLY dbase;

static uint32_t btime;

static char mycall[10];

static char symbol[2];

static int32_t udpsock;

static uint32_t ipnum;

static uint32_t toport;


static void Error(char text[], uint32_t text_len)
{
   X2C_PCOPY((void **)&text,text_len);
   osi_Werr(text, text_len);
   osi_WerrLn(" error abort", 13ul);
   X2C_ABORT();
   X2C_PFREE(text);
} /* end Error() */


static char GetNum(const char h[], uint32_t h_len, char eot,
                 uint32_t * p, uint32_t * n)
{
   *n = 0UL;
   while ((uint8_t)h[*p]>='0' && (uint8_t)h[*p]<='9') {
      *n = ( *n*10UL+(uint32_t)(uint8_t)h[*p])-48UL;
      ++*p;
   }
   return h[*p]==eot;
} /* end GetNum() */


static uint32_t truncc(double r)
{
   if (r<=0.0) return 0UL;
   else if (r>=2.147483647E+9) return 2147483647UL;
   else return (uint32_t)X2C_TRUNCC(r,0UL,X2C_max_longcard);
   return 0;
} /* end truncc() */


static uint32_t truncr(float r)
{
   if (r<=0.0f) return 0UL;
   else if (r>=2.147483647E+9f) return 2147483647UL;
   else return (uint32_t)X2C_TRUNCC(r,0UL,X2C_max_longcard);
   return 0;
} /* end truncr() */


static int32_t GetIp(char h[], uint32_t h_len, uint32_t * p,
                uint32_t * ip0, uint32_t * port0)
{
   uint32_t n;
   uint32_t i;
   char ok0;
   int32_t GetIp_ret;
   X2C_PCOPY((void **)&h,h_len);
   *p = 0UL;
   h[h_len-1] = 0;
   *ip0 = 0UL;
   for (i = 0UL; i<=4UL; i++) {
      n = 0UL;
      ok0 = 0;
      while ((uint8_t)h[*p]>='0' && (uint8_t)h[*p]<='9') {
         ok0 = 1;
         n = (n*10UL+(uint32_t)(uint8_t)h[*p])-48UL;
         ++*p;
      }
      if (!ok0) {
         GetIp_ret = -1L;
         goto label;
      }
      if (i<3UL) {
         if (h[*p]!='.' || n>255UL) {
            GetIp_ret = -1L;
            goto label;
         }
         *ip0 =  *ip0*256UL+n;
      }
      else if (i==3UL) {
         *ip0 =  *ip0*256UL+n;
         if (h[*p]!=':' || n>255UL) {
            GetIp_ret = -1L;
            goto label;
         }
      }
      else if (n>65535UL) {
         GetIp_ret = -1L;
         goto label;
      }
      *port0 = n;
      ++*p;
   } /* end for */
   GetIp_ret = 0L;
   label:;
   X2C_PFREE(h);
   return GetIp_ret;
} /* end GetIp() */


static void Parms(void)
{
   char s[1001];
   uint32_t n;
   uint32_t m;
   reconn = 0;
   verb = 0;
   verb2 = 0;
   strncpy(url,"127.0.0.1",1001u);
   strncpy(port,"30003",1001u);
   mycall[0] = 0;
   btime = 20UL;
   strncpy(symbol,"/^",2u);
   for (;;) {
      osi_NextArg(s, 1001ul);
      if (s[0U]==0) break;
      if ((s[0U]=='-' && s[1U]) && s[2U]==0) {
         if (s[1U]=='t') {
            osi_NextArg(s, 1001ul); /* url */
            n = 0UL;
            while ((n<1000UL && s[n]) && s[n]!=':') {
               if (n<1000UL) url[n] = s[n];
               ++n;
            }
            if (n>1000UL) n = 1000UL;
            url[n] = 0;
            if (s[n]==':') {
               m = 0UL;
               ++n;
               while ((n<1000UL && s[n]) && m<1000UL) {
                  port[m] = s[n];
                  ++n;
                  ++m;
               }
               if (m>1000UL) m = 1000UL;
               port[m] = 0;
            }
         }
         else if (s[1U]=='k') reconn = 1;
         else if (s[1U]=='b') {
            osi_NextArg(s, 1001ul);
            n = 0UL;
            if (!GetNum(s, 1001ul, 0, &n, &btime)) Error("-b <s>", 7ul);
         }
         else if (s[1U]=='I') {
            osi_NextArg(mycall, 10ul);
            if (aprsstr_Length(mycall, 10ul)<3UL || aprsstr_Length(mycall,
                10ul)>9UL) Error("-I <callsign>", 14ul);
         }
         else if (s[1U]=='s') {
            osi_NextArg(symbol, 2ul);
            if (aprsstr_Length(symbol, 2ul)!=2UL || symbol[0U]=='-') {
               Error("-s <symbol>", 12ul);
            }
         }
         else if (s[1U]=='u') {
            osi_NextArg(s, 1001ul);
            n = 0UL;
            if (GetIp(s, 1001ul, &n, &ipnum, &toport)<0L) {
               Error("-u ip:port number", 18ul);
            }
            udpsock = openudp();
            if (udpsock<0L) Error("cannot open udp socket", 23ul);
         }
         else if (s[1U]=='v') verb = 1;
         else if (s[1U]=='V') {
            verb = 1;
            verb2 = 1;
         }
         else if (s[1U]=='h') {
            osi_WrStrLn("", 1ul);
            osi_WrStrLn("dump1090 basestation format tcp output to aprs objec\
t beacon", 61ul);
            osi_WrStrLn("", 1ul);
            osi_WrStrLn(" -b <seconds>        aprs minimum send intervall -b \
10 (20)", 60ul);
            osi_WrStrLn(" -h                  help", 26ul);
            osi_WrStrLn(" -I <mycall>         Sender of Object Callsign -I OE\
0AAA", 57ul);
            osi_WrStrLn(" -k                  keep tcp connection", 41ul);
            osi_WrStrLn(" -s <symbol>         aprs symbol (/^)", 38ul);
            osi_WrStrLn(" -t <url:port>       connect dump1090 tcp server (12\
7.0.0.1:30003)", 67ul);
            osi_WrStrLn(" -u <ip>:<port>      send AXUDP -u 127.0.0.1:9001 us\
e udpgate4 or aprsmap as receiver", 86ul);
            osi_WrStrLn(" -v                  verbous", 29ul);
            osi_WrStrLn("example: -t 127.0.0.1:30003 -I YOURCALL-11 -u 127.0.\
0.1:9002 -k -v", 67ul);
            osi_WrStrLn("before this start \"dump1090 --net\"", 35ul);
            osi_WrStrLn("", 1ul);
            X2C_ABORT();
         }
         else Error("-h", 3ul);
      }
      else Error("-h", 3ul);
   }
} /* end Parms() */


static void decodeline(const char line0[], uint32_t line_len,
                CSV csv0)
{
   uint32_t j;
   uint32_t w;
   uint32_t i;
   memset((char *)csv0,(char)0,2100UL);
   i = 0UL;
   j = 0UL;
   w = 0UL;
   while (i<=line_len-1 && (uint8_t)line0[i]>=' ') {
      if (line0[i]!=',') {
         if (w<=99UL && j<=20UL) {
            csv0[w][j] = line0[i];
            ++j;
         }
      }
      else {
         ++w;
         j = 0UL;
      }
      ++i;
   }
} /* end decodeline() */


static char num(uint32_t n)
{
   return (char)(n%10UL+48UL);
} /* end num() */


static uint32_t dao91(double x)
/* radix91(xx/1.1) of dddmm.mmxx */
{
   double a;
   a = fabs(x);
   return ((truncc((a-(double)(float)truncc(a))*6.E+5)%100UL)
                *20UL+11UL)/22UL;
} /* end dao91() */


static void sendaprs(char dao, uint32_t time0, char mycall0[],
                uint32_t mycall_len, char destcall[],
                uint32_t destcall_len, char via[], uint32_t via_len,
                char sym[], uint32_t sym_len, char obj[],
                uint32_t obj_len, double lat, double long0,
                double alt, double course, double speed,
                char comm[], uint32_t comm_len)
{
   char ds[201];
   char h[201];
   char b[201];
   char raw[361];
   int32_t rp;
   uint32_t n;
   uint32_t i;
   float a;
   X2C_PCOPY((void **)&mycall0,mycall_len);
   X2C_PCOPY((void **)&destcall,destcall_len);
   X2C_PCOPY((void **)&via,via_len);
   X2C_PCOPY((void **)&sym,sym_len);
   X2C_PCOPY((void **)&obj,obj_len);
   X2C_PCOPY((void **)&comm,comm_len);
   b[0] = 0;
   aprsstr_Append(b, 201ul, mycall0, mycall_len);
   aprsstr_Append(b, 201ul, ">", 2ul);
   aprsstr_Append(b, 201ul, destcall, destcall_len);
   if (via[0UL]) {
      aprsstr_Append(b, 201ul, ",", 2ul);
      aprsstr_Append(b, 201ul, via, via_len);
   }
   aprsstr_Append(b, 201ul, ":;", 3ul);
   aprsstr_Assign(h, 201ul, obj, obj_len);
   aprsstr_Append(h, 201ul, "         ", 10ul);
   h[9U] = 0;
   aprsstr_Append(b, 201ul, h, 201ul);
   aprsstr_Append(b, 201ul, "*", 2ul);
   aprsstr_DateToStr(time0, ds, 201ul);
   ds[0U] = ds[11U];
   ds[1U] = ds[12U];
   ds[2U] = ds[14U];
   ds[3U] = ds[15U];
   ds[4U] = ds[17U];
   ds[5U] = ds[18U];
   ds[6U] = 0;
   aprsstr_Append(b, 201ul, ds, 201ul);
   aprsstr_Append(b, 201ul, "h", 2ul);
   i = aprsstr_Length(b, 201ul);
   a = (float)fabs(lat);
   n = truncr(a);
   b[i] = num(n/10UL);
   ++i;
   b[i] = num(n);
   ++i;
   n = truncr((a-(float)n)*6000.0f);
   b[i] = num(n/1000UL);
   ++i;
   b[i] = num(n/100UL);
   ++i;
   b[i] = '.';
   ++i;
   b[i] = num(n/10UL);
   ++i;
   b[i] = num(n);
   ++i;
   if (lat>=0.0) b[i] = 'N';
   else b[i] = 'S';
   ++i;
   b[i] = sym[0UL];
   ++i;
   a = (float)fabs(long0);
   n = truncr(a);
   b[i] = num(n/100UL);
   ++i;
   b[i] = num(n/10UL);
   ++i;
   b[i] = num(n);
   ++i;
   n = truncr((a-(float)n)*6000.0f);
   b[i] = num(n/1000UL);
   ++i;
   b[i] = num(n/100UL);
   ++i;
   b[i] = '.';
   ++i;
   b[i] = num(n/10UL);
   ++i;
   b[i] = num(n);
   ++i;
   if (lat>=0.0) b[i] = 'E';
   else b[i] = 'W';
   ++i;
   b[i] = sym[1UL];
   ++i;
   if (speed>0.5) {
      n = truncr((float)(course+1.5));
      b[i] = num(n/100UL);
      ++i;
      b[i] = num(n/10UL);
      ++i;
      b[i] = num(n);
      ++i;
      b[i] = '/';
      ++i;
      n = truncr((float)(speed*5.3995680345572E-1+0.5));
      b[i] = num(n/100UL);
      ++i;
      b[i] = num(n/10UL);
      ++i;
      b[i] = num(n);
      ++i;
   }
   if (alt>0.5) {
      b[i] = '/';
      ++i;
      b[i] = 'A';
      ++i;
      b[i] = '=';
      ++i;
      n = truncr((float)fabs(alt*3.2808398950131+0.5));
      if (alt>=0.0) b[i] = num(n/100000UL);
      else b[i] = '-';
      ++i;
      b[i] = num(n/10000UL);
      ++i;
      b[i] = num(n/1000UL);
      ++i;
      b[i] = num(n/100UL);
      ++i;
      b[i] = num(n/10UL);
      ++i;
      b[i] = num(n);
      ++i;
   }
   if (dao) {
      b[i] = '!';
      ++i;
      b[i] = 'w';
      ++i;
      b[i] = (char)(33UL+dao91(lat));
      ++i;
      b[i] = (char)(33UL+dao91(long0));
      ++i;
      b[i] = '!';
      ++i;
   }
   b[i] = 0;
   aprsstr_Append(b, 201ul, comm, comm_len);
   if (verb) osi_WrStrLn(b, 201ul);
   aprsstr_mon2raw(b, 201ul, raw, 361ul, &rp);
   rp = udpsend(udpsock, raw, rp, toport, ipnum);
   X2C_PFREE(mycall0);
   X2C_PFREE(destcall);
   X2C_PFREE(via);
   X2C_PFREE(sym);
   X2C_PFREE(obj);
   X2C_PFREE(comm);
} /* end sendaprs() */


static void aprs(const struct FLY f)
{
   char h[31];
   aprsstr_Assign(h, 31ul, f.name, 21ul);
   h[9U] = 0;
   while (aprsstr_Length(h, 31ul)<9UL) aprsstr_Append(h, 31ul, " ", 2ul);
   sendaprs(0, f.postime, mycall, 10ul, "APLFR1", 7ul, "", 1ul, "/^", 3ul, h,
                 31ul, (double)f.lat, (double)f.long0,
                (double)f.alt, (double)f.dir,
                (double)(f.speed*1.852f), "", 1ul);
} /* end aprs() */


static void store(const CSV csv0)
{
   pFLY f0;
   pFLY f1;
   pFLY f;
   uint32_t msg;
   uint32_t t;
   float oalt;
   float olong;
   float olat;
   t = osic_time();
   if ((((csv0[0U][0U]=='M' && csv0[0U][1U]=='S') && csv0[0U][2U]=='G')
                && aprsstr_StrToCard(csv0[1U], 21ul,
                &msg)) && ((msg==1UL || msg==3UL) || msg==4UL)) {
      f = dbase;
      f0 = 0;
      while (f && !aprsstr_StrCmp(f->hex, 6ul, csv0[4U], 21ul)) {
         f1 = f->next;
         if (f->lasttime+120UL<t) {
            if (f0==0) dbase = f1;
            else f0->next = f1;
            if (verb2) {
               osi_WrStr("purge ", 7ul);
               osi_WrStrLn(f->hex, 6ul);
            }
            osic_free((char * *) &f, sizeof(struct FLY));
         }
         else f0 = f;
         f = f1;
      }
      if (f==0) {
         osic_alloc((char * *) &f, sizeof(struct FLY));
         if (f==0) {
            osi_WerrLn("Out of Memory", 14ul);
            return;
         }
         memset((char *)f,(char)0,sizeof(struct FLY));
         f->next = dbase;
         dbase = f;
         aprsstr_Assign(f->hex, 6ul, csv0[4U], 21ul);
         if (verb2) {
            osi_WrStr("new ", 5ul);
            osi_WrStrLn(f->hex, 6ul);
         }
      }
      f->lasttime = t;
      if (msg==1UL) {
         if (verb2 && f->name[0U]==0) {
            osi_WrStr("found name ", 12ul);
            osi_WrStr(f->hex, 6ul);
            osi_WrStr(" ", 2ul);
            osi_WrStrLn(f->name, 21ul);
         }
         aprsstr_Assign(f->name, 21ul, csv0[10U], 21ul);
      }
      else if (msg==4UL) {
         if (((aprsstr_StrToFix(&f->speed, csv0[12U],
                21ul) && aprsstr_StrToFix(&f->dir, csv0[13U],
                21ul)) && f->dir>=0.0f) && f->dir<=360.0f) f->speedtime = t;
      }
      else if (msg==3UL) {
         if (((((((aprsstr_StrToFix(&oalt, csv0[11U],
                21ul) && aprsstr_StrToFix(&olat, csv0[14U],
                21ul)) && olat>(-90.0f)) && olat<90.0f)
                && aprsstr_StrToFix(&olong, csv0[15U],
                21ul)) && olong>(-180.0f)) && olong<180.0f)
                && (olong!=f->long0 || olat!=f->lat)) {
            f->postime = t;
            f->newpos = 1;
            f->lat = olat;
            f->long0 = olong;
            f->alt = oalt*0.3048f;
         }
      }
      if (f->lastbeacon>t) f->lastbeacon = t;
      if (((((((f->newpos && f->name[0U]) && f->postime+20UL>=t)
                && f->speedtime+20UL>=t) && f->speed>0.0f) && f->lat!=0.0f)
                && f->long0!=0.0f) && f->lastbeacon+btime<t) {
         aprs(*f);
         f->newpos = 0;
         f->lastbeacon = t;
      }
   }
} /* end store() */

static char ibuf[201];

static char line[201];

static uint32_t ip;

static uint32_t lp;

static CSV csv;


X2C_STACK_LIMIT(100000l)
extern int main(int argc, char **argv)
{
   X2C_BEGIN(&argc,argv,1,4000000l,8000000l);
   if (sizeof(CSV)!=2100) X2C_ASSERT(0);
   aprsstr_BEGIN();
   osi_BEGIN();
   Parms();
   fd = -1L;
   dbase = 0;
   fd = connecttob(url, port);
   lp = 0UL;
   for (;;) {
      if (fd>=0L) {
         if (readsockb(fd, (char *)ibuf, 201L)<0L) {
            /* connect lost */
            osic_Close(fd);
            fd = -1L;
         }
         else {
            for (ip = 0UL; ip<=200UL; ip++) {
               if ((uint8_t)ibuf[ip]<' ') {
                  if (lp<200UL) line[lp] = 0;
                  if (verb) osi_WrStrLn(line, 201ul);
                  decodeline(line, 201ul, csv);
                  store(csv);
                  lp = 0UL;
               }
               else if (lp<200UL) {
                  line[lp] = ibuf[ip];
                  ++lp;
               }
            } /* end for */
         }
      }
      else if (reconn) {
         osi_WerrLn("connection lost", 16ul);
         usleep(1000000UL);
         fd = connecttob(url, port);
      }
      else break;
   }
   X2C_EXIT();
   return 0;
}

X2C_MAIN_DEFINITION
