/*
 * lft_watch.h -- ncurses continuous monitoring mode for LFT
 *
 * This file is part of LFT.
 *
 * The LFT software provided in this Distribution is
 * Copyright 2007 VOSTROM Holdings, Inc.
 *
 * The full text of our legal notices is contained in the file called
 * COPYING, included with this Distribution.
 */

#ifndef LFT_WATCH_H
#define LFT_WATCH_H

#ifdef HAVE_NCURSES

#include "lft_lib.h"
#include "whois.h"

/* -------------------------------------------------------------------------
 * Tunables
 * ------------------------------------------------------------------------- */

#define WATCH_DEFAULT_INTERVAL   5    /* seconds between trace cycles */
#define WATCH_DEFAULT_PROBES     3    /* probes per hop when -j not given */
#define WATCH_MIN_ROWS          24    /* minimum terminal height */
#define WATCH_MAX_HOPS         256
#define WATCH_HISTORY_LEN      512    /* per-hop RTT history slots (circular) */
#define WATCH_MAX_ALT_IPS        4    /* multipath IPs per TTL */
#define WATCH_LOG_BUF       131072    /* log drawer ring buffer (bytes) — ~10 traces at -VV */
#define WATCH_PATH_FLASH_SECS    3    /* seconds to show PATH CHANGE notice */
#define WATCH_SPINNER_FRAMES     8    /* braille spinner frame count */

/* -------------------------------------------------------------------------
 * Per-IP annotation cache
 *
 * A session-wide hash table keyed by IP address (IPv4 or IPv6) that stores
 * pWhoIs / reverse-DNS data.  Every unique hop address gets exactly one
 * record here regardless of how many hops share it (multipath) or how many
 * cycles it is seen.  The cache is the authoritative "have we queried this?"
 * source — watch_hop_t.asn/netname/orgname/hostname are just display copies
 * populated from the cache.
 * ------------------------------------------------------------------------- */

#include <netinet/in.h>   /* struct in_addr, struct in6_addr */

/* 16-byte address union — IPv4 occupies the first 4 bytes, rest are zero */
typedef union {
    struct in_addr  v4;       /* AF_INET  */
    struct in6_addr v6;       /* AF_INET6 */
    uint8_t         raw[16];  /* for hashing / comparison */
} watch_ipaddr_t;

/* One record per unique IP address */
typedef struct watch_ip_record_s {
    int                      af;              /* AF_INET or AF_INET6 */
    watch_ipaddr_t           addr;            /* hash key */

    char                     asn[32];         /* e.g. "AS6939"              */
    char                     netname[64];     /* e.g. "HURRICANE-1"         */
    char                     orgname[64];     /* e.g. "Hurricane Electric"  */
    char                     hostname[256];   /* reverse-DNS PTR name       */

    int                      whois_queried;   /* 1 = pWhoIs attempted       */
    int                      rdns_queried;    /* 1 = rDNS attempted         */

    struct watch_ip_record_s *next;           /* collision chain            */
} watch_ip_record_t;

#define WATCH_IP_CACHE_BUCKETS  256  /* power of 2; plenty for any trace */

typedef struct {
    watch_ip_record_t *buckets[WATCH_IP_CACHE_BUCKETS];
    int                count;    /* total records allocated */
} watch_ip_cache_t;

/* -------------------------------------------------------------------------
 * Per-hop rolling accumulator
 * ------------------------------------------------------------------------- */

typedef struct {
    char    host[64];                          /* primary IP or hostname */
    char    alt_hosts[WATCH_MAX_ALT_IPS][64];  /* alternate IPs (multipath) */
    int     n_alt;                             /* number of alt_hosts populated */

    int     cycles_seen;    /* cycles in which this hop replied */
    int     cycles_total;   /* total cycles run when this hop was active */

    double  rtt_min;        /* all-time minimum RTT across all cycles (ms) */
    double  rtt_avg;        /* running average RTT (ms) */
    double  rtt_max;        /* all-time maximum RTT (ms) */
    double  rtt_jitter;     /* rtt_max - rtt_min this cycle */
    double  rtt_last;       /* most recent RTT reading (ms) */
    double  rtt_sum;        /* accumulator for running average */

    double  rtt_history[WATCH_HISTORY_LEN]; /* circular buffer, 0 = no reply */
    int     history_head;   /* next write index */
    int     history_count;  /* samples stored (capped at WATCH_HISTORY_LEN) */

    /* Display copies — populated from the session's ip_cache on demand.
     * The cache is authoritative; these are just what the renderer reads. */
    char    asn[32];        /* AS number string, e.g. "AS7922" */
    char    netname[64];    /* network name from pWhoIs/ARIN */
    char    orgname[64];    /* organisation name from pWhoIs (F2) */
    char    hostname[256];  /* reverse-DNS name for hop IP (F4) */
} watch_hop_t;

/* -------------------------------------------------------------------------
 * Overall watch session state
 * ------------------------------------------------------------------------- */

typedef struct {
    watch_hop_t hops[WATCH_MAX_HOPS];

    int  n_hops;            /* highest TTL seen */
    int  cycle;             /* current cycle number (1-based) */
    int  max_cycles;        /* 0 = run indefinitely */
    int  interval;          /* seconds between traces */
    int  probes_per_hop;    /* -j N value (probes per hop per cycle) */

    int  paused;            /* 1 = traces suspended */
    int  running;           /* 0 = quit requested */
    int  current_view;      /* 1-4 */
    int  log_open;          /* 1 = log drawer visible */
    int  whois_enabled;     /* 1 = show ASN/NetName columns */

    /* Cycling: during a trace, is it the first cycle or a re-run? */
    int  first_cycle;

    /* Log drawer ring buffer (captures -VV style output) */
    char log_buf[WATCH_LOG_BUF];
    int  log_head;          /* oldest byte position (ring-buffer head) */
    int  log_len;           /* bytes currently stored */

    /* Log drawer scroll state */
    int    log_scroll_offset;   /* lines scrolled up from frozen anchor; 0 = live */
    int    log_anchor_nlines;   /* total lines when user first scrolled (frozen anchor);
                                 * 0 means live — cleared when returning to live mode */
    time_t log_last_scroll;     /* wall time of last ↑/↓/k/j keypress (30 s timeout) */

    /* Path change detection */
    char path_snapshot[WATCH_MAX_HOPS][64];
    int  path_changed;
    time_t path_change_time;

    /* Terminal geometry */
    int  term_rows;
    int  term_cols;

    /* In-flight trace state (set by event callback) */
    int  in_trace;          /* 1 while LFTExecute is running */
    int  last_hop_flashed;  /* most recent TTL that was flashed */

    /* Target description for header line */
    char tgt_label[128];    /* e.g. "tcp://208.74.248.40:443" */

    /* Target reach state (updated each cycle) */
    int  target_hopno;      /* hop index of the target (0 = not reached) */
    int  target_open;       /* 1 = OPEN (SYN-ACK) */
    int  target_filtered;   /* 1 = FILTERED (ICMP port unreachable) */
    int  protocol;          /* LFT probe protocol (0=TCP, 1=UDP, 2=ICMP, 3=ICMP-RFC1393) */
    /* TCP: !open && !filtered → [CLOSED] (RST); ICMP: !open && !filtered → [REPLIED] (Echo Reply) */

    /* Per-IP annotation cache (pWhoIs + rDNS); keyed by address */
    watch_ip_cache_t     ip_cache;

    /* pWhoIs live lookup state (F2) */
    whois_session_params *watch_whois_wsess; /* allocated on first 'w' press */
    int  whois_bulk_done;   /* 1 = bulk query already completed */

    /* Hostname display toggle (F4) */
    int  show_hostnames;    /* 1 = show PTR names instead of IPs */

    /* Spinner animation frame counter (0..WATCH_SPINNER_FRAMES-1) */
    int  spinner_frame;

    /* Set by key handlers that require immediate trace start (breaks idle loop) */
    int  interrupt_sleep;

    /* Cumulative probe counters — accumulate across all cycles; reset with 'r'.
     * max_hops_seen and target_ever_reached are sticky-max / sticky-true. */
    int  total_replies;        /* Σ probe replies received */
    int  total_sent;           /* Σ probes sent */
    int  max_hops_seen;        /* peak hop count ever seen */
    int  target_ever_reached;  /* 1 once target replied in any cycle */

    /* Transient status bar message (F3 — shown for ~3 s after a save) */
    char   status_msg[192];   /* if non-empty and not expired, shown in status bar */
    time_t status_msg_time;   /* time() when the message was set */
} watch_session_t;

/* -------------------------------------------------------------------------
 * Public interface — called from lft_lib.c EVT_ON_EXIT handler
 * ------------------------------------------------------------------------- */

void WatchModeRun(lft_session_params *sess);

#endif /* HAVE_NCURSES */
#endif /* LFT_WATCH_H */
