/*
 * $Id: nodes_common.c,v 1.7 2004/10/25 21:18:25 cbiere Exp $
 *
 * Copyright (c) 2001-2003, Raphael Manfredi, Richard Eckart
 *
 * Common GUI functions for displaying node information.
 *
 *----------------------------------------------------------------------
 * This file is part of gtk-gnutella.
 *
 *  gtk-gnutella is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  gtk-gnutella is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with gtk-gnutella; if not, write to the Free Software
 *  Foundation, Inc.:
 *      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *----------------------------------------------------------------------
 */

#include "gui.h"

RCSID("$Id: nodes_common.c,v 1.7 2004/10/25 21:18:25 cbiere Exp $");

#include "nodes_common.h"

#include "gtk/statusbar.h"

#include "if/bridge/ui2c.h"
#include "if/core/nodes.h"
#include "if/gui_property_priv.h"

#include "lib/glib-missing.h"
#include "lib/misc.h"
#include "lib/override.h"	/* Must be the last header included */

static gchar gui_tmp[4096];

/**
 * Compute info string for node.
 *
 * @return pointer to static data.
 */
const gchar *
nodes_gui_common_status_str(const gnet_node_status_t *n)
{
	const gchar *a;

	switch (n->status) {
	case GTA_NODE_CONNECTING:
		a = _("Connecting...");
		break;

	case GTA_NODE_HELLO_SENT:
		a = _("Hello sent");
		break;

	case GTA_NODE_WELCOME_SENT:
		a = _("Welcome sent");
		break;

	case GTA_NODE_CONNECTED:
		if (n->sent || n->received) {
			size_t slen = 0;

			if (!node_show_detailed_info) {
				gm_snprintf(gui_tmp, sizeof(gui_tmp),
					"TX=%d RX=%d Q=%d,%d%% %s",
					n->sent, n->received,
					n->mqueue_count, n->mqueue_percent_used,
					n->in_tx_swift_control ? " [SW]" :
					n->in_tx_flow_control ? " [FC]" : "");
				a = gui_tmp;
				break;
			}

			if (n->tx_compressed && show_gnet_info_txc)
				slen += gm_snprintf(gui_tmp, sizeof(gui_tmp), "TXc=%d,%d%%",
					(gint) n->sent, (gint) (n->tx_compression_ratio * 100));
			else
				slen += gm_snprintf(gui_tmp, sizeof(gui_tmp), "TX=%d",
							(gint) n->sent);

			if (show_gnet_info_tx_speed || show_gnet_info_tx_wire) {
				gboolean is_first = TRUE;

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					" (" /* ')' */);

				if (show_gnet_info_tx_wire) {
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%s", compact_size(n->tx_written));
					is_first = FALSE;
				}

				if (show_gnet_info_tx_speed)
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%s%.1f k/s", is_first ? "" : ", ", n->tx_bps);

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					/* '(' */ ")");
			}

			if (n->rx_compressed && show_gnet_info_rxc)
				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					" RXc=%d,%d%%",
					(gint) n->received, (gint) (n->rx_compression_ratio * 100));
			else
				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					" RX=%d", (gint) n->received);

			if (show_gnet_info_rx_speed || show_gnet_info_rx_wire) {
				gboolean is_first = TRUE;

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					" (" /* ')' */);

				if (show_gnet_info_rx_wire) {
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%s", compact_size(n->rx_given));
					is_first = FALSE;
				}

				if (show_gnet_info_rx_speed)
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%s%.1f k/s", is_first ? "" : ", ", n->rx_bps);

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					/* '(' */ ")");
			}

			if (
				show_gnet_info_tx_queries || show_gnet_info_rx_queries ||
				show_gnet_info_gen_queries || show_gnet_info_sq_queries
			) {
				gboolean is_first = TRUE;

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					" Query(" /* ')' */);

				if (show_gnet_info_gen_queries) {
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"Gen=%d", n->squeue_sent);
					is_first = FALSE;
				}
				if (show_gnet_info_sq_queries) {
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%sQ=%d", is_first ? "" : ", ", n->squeue_count);
					is_first = FALSE;
				}
				if (show_gnet_info_tx_queries) {
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%sTX=%u", is_first ? "" : ", ", n->tx_queries);
					is_first = FALSE;
				}
				if (show_gnet_info_rx_queries)
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%sRX=%u", is_first ? "" : ", ", n->rx_queries);

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					/* '(' */ ")");
			}

			if (show_gnet_info_tx_hits || show_gnet_info_rx_hits) {
				gboolean is_first = TRUE;

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					" QHit(" /* ')' */);

				if (show_gnet_info_tx_hits) {
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"TX=%u", n->tx_qhits);
					is_first = FALSE;
				}
				if (show_gnet_info_rx_hits)
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%sRX=%u", is_first ? "" : ", ", n->rx_qhits);

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					/* '(' */ ")");
			}

			if (show_gnet_info_tx_dropped || show_gnet_info_rx_dropped) {
				gboolean is_first = TRUE;

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					" Drop(" /* ')' */);

				if (show_gnet_info_tx_dropped) {
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"TX=%u", n->tx_dropped);
					is_first = FALSE;
				}
				if (show_gnet_info_rx_dropped)
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%sRX=%u", is_first ? "" : ", ", n->rx_dropped);

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					/* '(' */ ")");
			}

			if (show_gnet_info_shared_size || show_gnet_info_shared_files) {
				gboolean is_first = TRUE;

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					" Lib(" /* ')' */);

				if (show_gnet_info_shared_size && n->gnet_info_known) {
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%s", compact_kb_size(
							n->gnet_files_count ? n->gnet_kbytes_count : 0));
					is_first = FALSE;
				}
				if (show_gnet_info_shared_files && n->gnet_info_known)
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						"%s#=%u", is_first ? "" : ", ", n->gnet_files_count);

				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					/* '(' */ "%s)", n->gnet_info_known ? "" : "?");
			}

			if (show_gnet_info_qrp_stats) {
				if (n->has_qrp)
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						" QRP=%u%%",
						(guint) (n->qrp_efficiency * 100.0));

				if (n->qrt_slots != 0)
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						" QRT(%s, g=%d, f=%d%%, t=%d%%, e=%d%%)",
						compact_size(n->qrt_slots), n->qrt_generation,
						n->qrt_fill_ratio, n->qrt_pass_throw,
						(guint) (n->qrp_efficiency * 100.0));
			}

			if (show_gnet_info_dbw)
				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
				" Dup=%u Bad=%u W=%u H=%u",
				n->n_dups, n->n_bad, n->n_weird, n->n_hostile);

			if (show_gnet_info_rt) {
				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
				" RT(avg=%d, last=%d", n->rt_avg, n->rt_last);	/* ) */
				if (n->tcp_rtt)
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						", tcp=%d", n->tcp_rtt);
				if (n->udp_rtt)
					slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
						", udp=%d", n->udp_rtt);
				slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
					/* ( */ ")");
			}

			slen += gm_snprintf(&gui_tmp[slen], sizeof(gui_tmp)-slen,
				" Q=%d,%d%% %s",
				n->mqueue_count, n->mqueue_percent_used,
				n->in_tx_swift_control ? " [SW]" :
				n->in_tx_flow_control ? " [FC]" : "");
			a = gui_tmp;
		} else
			a = _("Connected");
		break;

	case GTA_NODE_SHUTDOWN:
		{
			gm_snprintf(gui_tmp, sizeof(gui_tmp),
				_("Closing: %s [Stop in %ds] RX=%d Q=%d,%d%%"),
				n->message, n->shutdown_remain, n->received,
				n->mqueue_count, n->mqueue_percent_used);
			a = gui_tmp;
		}
		break;

	case GTA_NODE_REMOVING:
		a =  *n->message ? n->message : _("Removing");
		break;

	case GTA_NODE_RECEIVING_HELLO:
		a = _("Receiving hello");
		break;

	default:
		a = _("UNKNOWN STATUS");
	}

	return a;
}

/*
 * nodes_gui_common_flags_str
 *
 * Display a summary of the node flags:
 *
 *    012345678AB (offset)
 *    NIrwqxZPFhT
 *    ^^^^^^^^^^^
 *    ||||||||||+ T indicates a TLS-tunneled connection
 *    |||||||||+  hops flow triggerd (h), or total query flow control (f)
 *    ||||||||+   flow control (F), or pending data in queue (d)
 *    |||||||+    indicates whether we're a push proxy (P) / node is proxy (p)
 *    ||||||+     indicates whether Rx, Tx or both (Z) are compressed
 *    |||||+      indicates whether we sent our last-hop QRT to remote UP
 *    ||||+       indicates whether we sent/received a QRT, or send/receive one
 *    |||+        indicates whether node is writable
 *    ||+         indicates whether node is readable
 *    |+          indicates connection type (Incoming, Outgoing, Ponging)
 *    +           indicates peer mode (Normal, Ultra, Leaf)
 */
const gchar *
nodes_gui_common_flags_str(const gnet_node_flags_t *flags)
{
	static gchar status[] = "NIrwqTRPFhS";

	switch (flags->peermode) {
		case NODE_P_UNKNOWN:	status[0] = '-'; break;
		case NODE_P_ULTRA:		status[0] = 'U'; break;
		case NODE_P_NORMAL:		status[0] = 'N'; break;
		case NODE_P_LEAF:		status[0] = 'L'; break;
		case NODE_P_CRAWLER:	status[0] = 'C'; break;
		case NODE_P_UDP:		status[0] = 'P'; break;
		default:				g_assert(0); break;
	}

	status[1] = flags->incoming ? 'I' : 'O';
	status[2] = flags->readable ? 'r' : '-';
	status[3] = flags->writable ? 'w' : '-';

	switch (flags->qrt_state) {
		case QRT_S_SENT: case QRT_S_RECEIVED:		status[4] = 'Q'; break;
		case QRT_S_SENDING: case QRT_S_RECEIVING:	status[4] = 'q'; break;
		case QRT_S_PATCHING:						status[4] = 'p'; break;
		default:									status[4] = '-';
	}

	switch (flags->uqrt_state) {
		case QRT_S_SENT:		status[5] = 'X'; break;
		case QRT_S_SENDING:		status[5] = 'x'; break;
		case QRT_S_PATCHING:	status[5] = 'p'; break;
		default:				status[5] = '-';
	}

	status[6] = 
		flags->tx_compressed && flags->rx_compressed ? 'Z' :
		flags->tx_compressed ? 'T' :
		flags->rx_compressed ? 'R' : '-';

	if (flags->is_push_proxied)  status[7] = 'P';
	else if (flags->is_proxying) status[7] = 'p';
	else status[7] = '-';

	if (flags->in_tx_swift_control) status[8]     = 'S';
	else if (flags->in_tx_flow_control) status[8] = 'F';
	else if (flags->mqueue_above_lowat) status[8] = 'D';
	else if (!flags->mqueue_empty) status[8]      = 'd';
	else status[8]                                = '-';

	if (flags->hops_flow == 0)
		status[9] = 'f';
	else if (flags->hops_flow < GTA_NORMAL_TTL)
		status[9] = 'h';
	else
		status[9] = '-';

	status[10] = flags->tls ? 'T' : '-';

	status[sizeof(status) - 1] = '\0';
	return status;
}

static void
add_node_helper(guint32 ip, gpointer port)
{
	guc_node_add(ip, GPOINTER_TO_UINT(port));
}

/**
 * Try to connect to the node given by the addr string in the form
 * [ip]:[port]. Port may be omitted.
 */
void
nodes_gui_common_connect_by_name(const gchar *addr) 
{
    guint32 port = GTA_PORT;
    gchar *e, *p;

    g_assert(addr != NULL);
    
    e = g_strdup(addr);
	g_strstrip(e);

	p = strchr(e, ':');
	if (p) {
		gchar *ep;
		guint64 v;
		gint error;
		
		*p++ = '\0';
		v = parse_uint64(p, &ep, 10, &error);
		port = (v > 0 && v < 65536 && *ep == '\0') ? v : 0;
	}

	if (port < 1 || port > 65535) {
		statusbar_gui_warning(15, _("Port must be between 1 and 65535"));
    } else {
		guc_adns_resolve(e, add_node_helper, GUINT_TO_POINTER((guint) port));
	}

    G_FREE_NULL(e);
}

/* vi: set ts=4 sw=4 cindent: */
