/*
 * This file is part of Xfce (https://gitlab.xfce.org).
 *
 * Copyright (c) 2021-2022 Jan Ziak <0xe2.0x9a.0x9b@xfce.org>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <stdio.h>
#include <string.h>
#include "network.h"

#ifdef HAVE_LIBGTOP
/* Defined by obsoleted AC_HEADER_TIME macro, wanted by libgtop */
#define TIME_WITH_SYS_TIME 1
#include <glibtop/netlist.h>
#include <glibtop/netload.h>

static gint
read_netload_libgtop (gulong *bytes)
{
    glibtop_netlist netlist;
    char **interfaces = glibtop_get_netlist (&netlist);
    if (!interfaces)
        return -1;

    *bytes = 0;
    for (char **i = interfaces; *i != NULL; i++)
    {
        glibtop_netload netload;
        glibtop_get_netload (&netload, *i);
        *bytes += netload.bytes_total;
    }

    return 0;
}

#else

static gint
read_netload_libgtop (gulong *bytes)
{
    return -1;
}

#endif

static const char *const PROC_NET_NETSTAT = "/proc/net/netstat";

static gint
read_netload_proc (gulong *bytes)
{
    char buf[4*1024];
    unsigned long long dummy, in_octets, out_octets;

    {
        FILE *fd = fopen (PROC_NET_NETSTAT, "r");
        if (!fd)
            return -1;

        size_t size = fread (buf, sizeof (*buf), G_N_ELEMENTS (buf) - 1, fd);
        if (size == 0)
        {
            fclose(fd);
            return -1;
        }
        buf[size] = '\0';

        if (fclose (fd) != 0)
            return -1;
    }

    const char *s = buf;

    /* Skip first 3 lines */
    s = strchr(s, '\n'); if (!s) return -1;
    s++;
    s = strchr(s, '\n'); if (!s) return -1;
    s++;
    s = strchr(s, '\n'); if (!s) return -1;
    s++;

    if (sscanf (s, "IpExt: %llu %llu %llu %llu %llu %llu %llu %llu",
                &dummy, &dummy, &dummy, &dummy, &dummy, &dummy,
                &in_octets, &out_octets) != 8)
        return -1;

    *bytes = in_octets + out_octets;
    return 0;
}

gint
read_netload (gulong *net, gulong *NTotal)
{
    static gulong bytes[2];
    static gint64 time[2];

    *net = 0;
    *NTotal = 0;

    time[1] = g_get_monotonic_time ();

    if (read_netload_proc (&bytes[1]) != 0)
        if (read_netload_libgtop (&bytes[1]) != 0)
            return -1;

    if (time[0] != 0 && G_LIKELY (time[1] > time[0]) && G_LIKELY (bytes[1] >= bytes[0]))
    {
        guint64 diff_bits = 8 * (bytes[1] - bytes[0]);
        gdouble diff_time = (time[1] - time[0]) / 1e6;
        *net = MIN (100 * diff_bits / diff_time / MAX_BANDWIDTH_BITS, 100);
        *NTotal = diff_bits / diff_time;
    }

    bytes[0] = bytes[1];
    time[0] = time[1];

    return 0;
}

