/** \brief Find AOS time of current pass.
 *  \param sat The satellite to find AOS for.
 *  \param qth The ground station.
 *  \param start Start time, prefereably now.
 *  \return The time of the previous AOS or 0.0 if the satellite has no AOS.
 *
 * This function can be used to find the AOS time in the past of the
 * current pass.
 */
gdouble
find_prev_aos (sat_t *sat, qth_t *qth, gdouble start)
{
    gdouble aostime = start;


    /* make sure current sat values are
        in sync with the time
    */
    predict_calc (sat, qth, start);

    /* check whether satellite has aos */
    if ((sat->otype == ORBIT_TYPE_GEO) || 
        (sat->otype == ORBIT_TYPE_DECAYED) ||
        !has_aos (sat, qth)) {

        return 0.0;

    }

    while (sat->el >= 0.0) {
        aostime -= 0.0005; // 0.75 min
        predict_calc (sat, qth, aostime);
    }

    return aostime;
}
/** \brief Predict first pass after a certain time.
 *  \param sat Pointer to the satellite data.
 *  \param qth Pointer to the location data.
 *  \param start Starting time.
 *  \param maxdt The maximum number of days to look ahead (0 for no limit).
 *  \return Pointer to a newly allocated pass_t structure or NULL if
 *          there was an error.
 *
 * This function will find the first upcoming pass with AOS no earlier than
 * t = start and no later than t = (start+maxdt).
 *
 * \note For no time limit use maxdt = 0.0
 *
 * \note the data in sat will be corrupt (future) and must be refreshed
 *       by the caller, if the caller will need it later on (eg. if the caller
 *       is GtkSatList).
 *
 * \note Prepending to a singly linked list is much faster than appending.
 *       Therefore, the elements are prepended whereafter the GSList is
 *       reversed
 */
pass_t *
get_pass   (sat_t *sat_in, qth_t *qth, gdouble start, gdouble maxdt)
{
    gdouble        aos = 0.0;    /* time of AOS */
    gdouble        tca = 0.0;    /* time of TCA */
    gdouble        los = 0.0;    /* time of LOS */
    gdouble        dt = 0.0;     /* time diff */
    gdouble        step = 0.0;   /* time step */
    gdouble        t0 = start;
    gdouble        t;            /* current time counter */
    gdouble        tres = 0.0; /* required time resolution */
    gdouble        max_el = 0.0; /* maximum elevation */
    pass_t        *pass = NULL;
    pass_detail_t *detail = NULL;
    gboolean       done = FALSE;
    guint          iter = 0;      /* number of iterations */
    sat_t         *sat,sat_working;
    /* FIXME: watchdog */

    /*copy sat_in to a working structure*/
    sat = memcpy(&sat_working,sat_in,sizeof(sat_t));

    /* get time resolution; sat-cfg stores it in seconds */
    tres = sat_cfg_get_int (SAT_CFG_INT_PRED_RESOLUTION) / 86400.0;

    /* loop until we find a pass with elevation > SAT_CFG_INT_PRED_MIN_EL
        or we run out of time
        FIXME: we should have a safety break
    */
    while (!done) {

        /* Find los of next pass or of current pass */
        los = find_los (sat, qth, t0, maxdt); // See if a pass is ongoing
        aos = find_aos (sat, qth, t0, maxdt);
        /* sat_log_log(SAT_LOG_LEVEL_MSG, "%s:%s:%d: found aos %f and los %f for t0=%f", */
        /*          __FILE__,  */
        /*          __FUNCTION__, */
        /*          __LINE__, */
        /*          aos, */
        /*          los,  */
        /*          t0); */
        if (aos > los) {
            // los is from an currently happening pass, find previous aos
            aos = find_prev_aos(sat, qth, t0);
        }

        /* aos = 0.0 means no aos */
        if (aos == 0.0) {
            done = TRUE;
        }

        /* check whether we are within time limits;
            maxdt = 0 mean no time limit.
        */
        else if ((maxdt > 0.0) && (aos > (start + maxdt)) ) {
            done = TRUE;
        }
        else {
            //los = find_los (sat, qth, aos + 0.001, maxdt); // +1.5 min later
            dt = los - aos;

            /* get time step, which will give us the max number of entries */
            step = dt / sat_cfg_get_int (SAT_CFG_INT_PRED_NUM_ENTRIES);

            /* but if this is smaller than the required resolution
                we go with the resolution
            */
            if (step < tres)
                step = tres;

            /* create a pass_t entry; FIXME: g_try_new in 2.8 */
            pass = g_new (pass_t, 1);
            
            pass->aos = aos;
            pass->los = los;
            pass->max_el = 0.0;
            pass->aos_az = 0.0;
            pass->los_az = 0.0;
            pass->maxel_az = 0.0;
            pass->vis[0] = '-';
            pass->vis[1] = '-';
            pass->vis[2] = '-';
            pass->vis[3] = 0;
            pass->satname = g_strdup (sat->nickname);
            pass->details = NULL;

            /* iterate over each time step */
            for (t = pass->aos; t <= pass->los; t += step) {
            
                /* calculate satellite data */
                predict_calc (sat, qth, t);

                /* in the first iter we want to store
                    pass->aos_az
                */
                if (t == pass->aos) {
                    pass->aos_az = sat->az;
                    pass->orbit = sat->orbit;
                }

                /* append details to sat->details */
                detail = g_new (pass_detail_t, 1);
                detail->time = t;
                detail->pos.x = sat->pos.x;
                detail->pos.y = sat->pos.y;
                detail->pos.z = sat->pos.z;
                detail->pos.w = sat->pos.w;
                detail->vel.x = sat->vel.x;
                detail->vel.y = sat->vel.y;
                detail->vel.z = sat->vel.z;
                detail->vel.w = sat->vel.w;
                detail->velo = sat->velo;
                detail->az = sat->az;
                detail->el = sat->el;
                detail->range = sat->range;
                detail->range_rate = sat->range_rate;
                detail->lat = sat->ssplat;
                detail->lon = sat->ssplon;
                detail->alt = sat->alt;
                detail->ma = sat->ma;
                detail->phase = sat->phase;
                detail->footprint = sat->footprint;
                detail->orbit = sat->orbit;
                detail->vis = get_sat_vis (sat, qth, t);

                /* also store visibility "bit" */
                switch (detail->vis) {
                case SAT_VIS_VISIBLE:
                    pass->vis[0] = 'V';
                    break;
                case SAT_VIS_DAYLIGHT:
                    pass->vis[1] = 'D';
                    break;
                case SAT_VIS_ECLIPSED:
                    pass->vis[2] = 'E';
                    break;
                default:
                    break;
                }

                pass->details = g_slist_prepend (pass->details, detail);

                /* store elevation if greater than the
                    previously stored one
                */
                if (sat->el > max_el) {
                    max_el = sat->el;
                    tca = t;
                    pass->maxel_az = sat->az;
                }

                /*     g_print ("TIME: %f\tAZ: %f\tEL: %f (MAX: %f)\n", */
                /*           t, sat->az, sat->el, max_el); */
            }

            pass->details = g_slist_reverse (pass->details);

            /* calculate satellite data */
            predict_calc (sat, qth, pass->los);
            /* store los_az, max_el and tca */
            pass->los_az = sat->az;
            pass->max_el = max_el;
            pass->tca    = tca;

            /* check whether this pass is good */
            if (max_el >= sat_cfg_get_int (SAT_CFG_INT_PRED_MIN_EL)) {
                done = TRUE;
            }
            else {
                done = FALSE;
                t0 = los + 0.014; // +20 min
                free_pass (pass);
                pass = NULL;
            }

            iter++;
        }
    }

    return pass;
}
/** \brief Find the AOS time of the next pass.
 *  \author Alexandru Csete, OZ9AEC
 *  \author John A. Magliacane, KD2BD
 *  \param sat Pointer to the satellite data.
 *  \param qth Pointer to the QTH data.
 *  \param start The time where calculation should start.
 *  \param maxdt The upper time limit in days (0.0 = no limit)
 *  \return The time of the next AOS or 0.0 if the satellite has no AOS.
 *
 * This function finds the time of AOS for the first coming pass taking place
 * no earlier that start.
 * If the satellite is currently within range, the function first calls
 * find_los to get the next LOS time. Then the calculations are done using
 * the new start time.
 *
 */
gdouble
find_aos (sat_t *sat, qth_t *qth, gdouble start, gdouble maxdt)
{
    gdouble t = start;
    gdouble aostime = 0.0;


    /* make sure current sat values are
        in sync with the time
    */
    predict_calc (sat, qth, start);

    /* check whether satellite has aos */
    if ((sat->otype == ORBIT_TYPE_GEO) || 
        (sat->otype == ORBIT_TYPE_DECAYED) ||
        !has_aos (sat, qth)) {

        return 0.0;

    }


    if (sat->el > 0.0)
        t = find_los (sat, qth, start, maxdt) + 0.014; // +20 min

    /* invalid time (potentially returned by find_los) */
    if (t < 0.1)
        return 0.0;

    /* update satellite data */
    predict_calc (sat, qth, t);

    /* use upper time limit */
    if (maxdt > 0.0) {

        /* coarse time steps */
        while ((sat->el < -1.0) && (t <= (start + maxdt))) {
            t -= 0.00035 * (sat->el * ((sat->alt / 8400.0) + 0.46) - 2.0);
            predict_calc (sat, qth, t);
        }

        /* fine steps */
        while ((aostime == 0.0) && (t <= (start + maxdt))) {

            if (fabs (sat->el) < 0.005) {
                aostime = t;
            }
            else {
                t -= sat->el * sqrt (sat->alt) / 530000.0;
                predict_calc (sat, qth, t);
            }

        }

    }
    /* don't use upper time limit */
    else {

        /* coarse time steps */
        while (sat->el < -1.0) {

            t -= 0.00035 * (sat->el * ((sat->alt / 8400.0) + 0.46) - 2.0);
            predict_calc (sat, qth, t);
        }

        /* fine steps */
        while (aostime == 0.0) {

            if (fabs (sat->el) < 0.005) {
                aostime = t;
            }
            else {
                t -= sat->el * sqrt (sat->alt) / 530000.0;
                predict_calc (sat, qth, t);
            }

        }
    }


    return aostime;
}
/** \brief Find the LOS time of the next pass.
 *  \author Alexandru Csete, OZ9AEC
 *  \author John A. Magliacane, KD2BD
 *  \param sat Pointer to the satellite data.
 *  \param qth Pointer to the QTH data.
 *  \param start The time where calculation should start.
 *  \param maxdt The upper time limit in days (0.0 = no limit)
 *  \return The time of the next LOS or 0.0 if the satellite has no LOS.
 *
 * This function finds the time of LOS for the first coming pass taking place
 * no earlier that start.
 * If the satellite is currently out of range, the function first calls
 * find_aos to get the next AOS time. Then the calculations are done using
 * the new start time.
 * The function has a built-in watchdog to ensure that we don't end up in
 * lengthy loops.
 *
 */
gdouble
find_los (sat_t *sat, qth_t *qth, gdouble start, gdouble maxdt)
{
    gdouble t = start;
    gdouble lostime = 0.0;


    predict_calc (sat, qth, start);

    /* check whether satellite has aos */
    if ((sat->otype == ORBIT_TYPE_GEO) || 
        (sat->otype == ORBIT_TYPE_DECAYED) ||
        !has_aos (sat, qth)) {

        return 0.0;

    }


    if (sat->el < 0.0)
        t = find_aos (sat, qth, start, maxdt) + 0.001; // +1.5 min

    /* invalid time (potentially returned by find_aos) */
    if (t < 0.01)
        return 0.0;

    /* update satellite data */
    predict_calc (sat, qth, t);


    /* use upper time limit */
    if (maxdt > 0.0) {

        /* coarse steps */
        while ((sat->el >= 1.0) && (t <= (start + maxdt))) {
            t += cos((sat->el - 1.0) * de2ra) * sqrt(sat->alt) / 25000.0;
            predict_calc (sat, qth, t);
        }

        /* fine steps */
        while ((lostime == 0.0) && (t <= (start + maxdt)))  {
            
            t += sat->el * sqrt(sat->alt)/502500.0;
            predict_calc (sat, qth, t);
            
            if (fabs(sat->el) < 0.005)
                lostime = t;
        }
    }

    /* don't use upper limit */
    else {

        /* coarse steps */
        while (sat->el >= 1.0) {
            t += cos((sat->el - 1.0) * de2ra) * sqrt(sat->alt) / 25000.0;
            predict_calc (sat, qth, t);
        }

        /* fine steps */
        while (lostime == 0.0) {
            
            t += sat->el * sqrt(sat->alt)/502500.0;
            predict_calc (sat, qth, t);
            
            if (fabs(sat->el) < 0.005)
                lostime = t;
        }
    }


    return lostime;
}
/** \brief Create and show ground track for a satellite.
 *  \param satmap The satellite map widget.
 *  \param sat Pointer to the satellite object.
 *  \param qth Pointer to the QTH data.
 *  \param obj the satellite object.
 *  
 * Gpredict allows the user to require the ground track for any number of orbits
 * ahead. Therfore, the resulting ground track may cross the map boundaries many
 * times, and using one single polyline for the whole ground track would look very
 * silly. To avoid this, the points will be split into several polylines.
 *
 */
void
ground_track_create (GtkSatMap *satmap, sat_t *sat, qth_t *qth, sat_map_obj_t *obj)
{
     long  this_orbit;  /* current orbit number */
     long  max_orbit;   /* target orbit number, ie. this + num - 1 */
     double         t0;          /* time when this_orbit starts */
     double         t;
     ssp_t         *this_ssp;


     sat_log_log (SAT_LOG_LEVEL_DEBUG,
                     _("%s: Creating ground track for %s"),
                 __FUNCTION__, sat->nickname);

     /* just to be safe... if empty GSList is not NULL => segfault */
     obj->track_data.latlon = NULL;

     /* get configuration parameters */
     this_orbit = sat->orbit;
     max_orbit = sat->orbit -1 + mod_cfg_get_int (satmap->cfgdata,
                                                             MOD_CFG_MAP_SECTION,
                                                             MOD_CFG_MAP_TRACK_NUM,
                                                             SAT_CFG_INT_MAP_TRACK_NUM);

                               
     sat_log_log (SAT_LOG_LEVEL_DEBUG,
                     _("%s: Start orbit: %d"),
                     __FUNCTION__, this_orbit);
     sat_log_log (SAT_LOG_LEVEL_DEBUG,
                     _("%s: End orbit %d"),
                     __FUNCTION__, max_orbit);


     /* find the time when the current orbit started */

     /* Iterate backwards in time until we reach sat->orbit < this_orbit.
        Use predict_calc from predict-tools.c as SGP/SDP driver.
        As a built-in safety, we stop iteration if the orbit crossing is
        more than 24 hours back in time.
     */
     t0 = satmap->tstamp;//get_current_daynum ();
     /* use == instead of >= as it is more robust */
     for (t = t0; (sat->orbit == this_orbit) && ((t + 1.0) > t0); t -= 0.0007) {

          predict_calc (sat, qth, t);

     }

     /* set it so that we are in the same orbit as this_orbit
        and not a different one */
     t += 2*0.0007;
     t0 = t;     
     predict_calc (sat, qth, t0);

     sat_log_log (SAT_LOG_LEVEL_DEBUG,
                     _("%s: T0: %f (%d)"),
                     __FUNCTION__, t0, sat->orbit);


     /* calculate (lat,lon) for the required orbits */
     while ((sat->orbit <= max_orbit) &&
            (sat->orbit >= this_orbit) &&
            (!decayed(sat))) {

          /* We use 30 sec time steps. If resolution is too fine, the
             line drawing routine will filter out unnecessary points
          */
          t += 0.00035;
          predict_calc (sat, qth, t);

          /* store this SSP */

          /* Note: g_slist_append() has to traverse the entire list to find the end, which
             is inefficient when adding multiple elements. Therefore, we use g_slist_prepend()
             and reverse the entire list when we are done.
          */
          this_ssp = g_try_new (ssp_t, 1);

          if (this_ssp == NULL) {
               sat_log_log (SAT_LOG_LEVEL_ERROR,
                               _("%s: MAYDAY: Insufficient memory for ground track!"),
                               __FUNCTION__);
               return;
          }
          
          this_ssp->lat = sat->ssplat;
          this_ssp->lon = sat->ssplon;
          obj->track_data.latlon = g_slist_prepend (obj->track_data.latlon, this_ssp);
          
     }
     /* log if there is a problem with the orbit calculation */
     if (sat->orbit != (max_orbit+1)) {
         sat_log_log (SAT_LOG_LEVEL_ERROR,
                      _("%s: Problem computing ground track for %s"),
                      __FUNCTION__, sat->nickname);
         return;
     }
     

     /* Reset satellite structure to eliminate glitches in single sat 
        view and other places when new ground track is layed out */
     predict_calc(sat, qth, satmap->tstamp);

     /* reverse GSList */
     obj->track_data.latlon = g_slist_reverse (obj->track_data.latlon);

     /* split points into polylines */
     create_polylines (satmap, sat, qth, obj);

     /* misc book-keeping */
     obj->track_orbit = this_orbit;

}