/* net_dgpsip.c -- gather and dispatch DGPS data from DGPSIP servers
 *
 * This file is Copyright by the GPSD project
 * SPDX-License-Identifier: BSD-2-clause
 */

#include "../include/gpsd_config.h"   // must be before all includes

#include <errno.h>                    // for errno
#include <fcntl.h>
#include <netdb.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>

#include "../include/gpsd.h"

/* open a connection to a DGPSIP server
 * Return: socket on success
 *         less than zero on failure
 */
socket_t dgpsip_open(struct gps_device_t *device, const char *dgpsserver)
{
    char *colon;
    const char *dgpsport = "rtcm-sc104";
    int opts;
    char hn[256], buf[BUFSIZ];
    socket_t dsock;
    ssize_t blen;
    char server[GPS_PATH_MAX];

    device->servicetype = SERVICE_DGPSIP;
    device->dgpsip.reported = false;
    strlcpy(server, dgpsserver, sizeof(server));
    if (NULL != (colon = strchr(server, ':'))) {
        dgpsport = colon + 1;
        *colon = '\0';
    }
    if (!getservbyname(dgpsport, "tcp")) {
        dgpsport = DEFAULT_RTCM_PORT;
    }

    dsock = netlib_connectsock(AF_UNSPEC, server, dgpsport, "tcp");
    if (0 > dsock) {
        // cast for 32-bit ints
        GPSD_LOG(LOG_ERROR, &device->context->errout,
                 "DGPS: can't connect to DGPS server %s, "
                 "netlib error %s(%ld).\n",
                 dgpsserver, netlib_errstr(dsock), (long)dsock);
        device->gpsdata.gps_fd = PLACEHOLDING_FD;
        return dsock;
    }
    // cast for 32-bit ints
    GPSD_LOG(LOG_PROG, &device->context->errout,
             "DGPS: connection to DGPS server %s established. fd=%ld\n",
             dgpsserver, (long)dsock);
    device->gpsdata.gps_fd = (gps_fd_t)dsock;
    (void)gethostname(hn, sizeof(hn));
    // greeting required by some RTCM104 servers; others will ignore it
    blen = snprintf(buf, sizeof(buf), "HELO %s gpsd %s\r\nR\r\n", hn,
                    VERSION);
    if (1 > blen ||
        write(device->gpsdata.gps_fd, buf, blen) != blen) {
        GPSD_LOG(LOG_ERROR, &device->context->errout,
                 "DGPS: hello to DGPS server %s failed\n",
                 dgpsserver);
    }
    opts = fcntl(device->gpsdata.gps_fd, F_GETFL);

    if (0 <= opts) {
        (void)fcntl(device->gpsdata.gps_fd, F_SETFL, opts | O_NONBLOCK);
    } else {
        GPSD_LOG(LOG_ERROR, &device->context->errout,
                 "DGPS: fcntl %s failed. %s(%d)\n",
                 dgpsserver, strerror(errno), errno);
    }
    return (socket_t)device->gpsdata.gps_fd;
}

// may be time to ship a usage report to the DGPSIP server
void dgpsip_report(struct gps_context_t *context,
                   struct gps_device_t *gps,
                   struct gps_device_t *dgpsip)
{
    /*
     * 10 is an arbitrary number, the point is to have gotten several good
     * fixes before reporting usage to our DGPSIP server.
     */
    if (10 < context->fixcnt &&
        !dgpsip->dgpsip.reported) {
        dgpsip->dgpsip.reported = true;
        if (-1 < dgpsip->gpsdata.gps_fd) {
            char buf[BUFSIZ];
            ssize_t blen;

            blen = snprintf(buf, sizeof(buf), "R %0.8f %0.8f %0.2f\r\n",
                            gps->gpsdata.fix.latitude,
                            gps->gpsdata.fix.longitude,
                            gps->gpsdata.fix.altMSL);
            if (1 > blen ||
                write(dgpsip->gpsdata.gps_fd, buf,blen) != blen) {
                GPSD_LOG(LOG_WARN, &context->errout,
                          "DGPS: write to dgps FAILED\n");
            } else {
                GPSD_LOG(LOG_IO, &context->errout, "DGPS: => dgps %s\n", buf);
            }
        }
    }
}

// vim: set expandtab shiftwidth=4
