#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>

#include <linux/ax25.h>
#include <linux/netrom.h>

#include "config.h"
#include "netromd.h"

static int build_header(unsigned char *message)
{
	message[0] = 0xFF;
	
	strcpy(message + 1, nr_config_get_alias(0));
	strncat(message + 1, "       ", 6 - strlen(message + 1));

	return 7;
}

static void build_mine(int s, struct full_sockaddr_ax25 *dest, int dlen, int localval)
{
	unsigned char message[100];
	int len;
	int i;

	len = build_header(message);

	for (i = 1; i < nr_config_num_ports(); i++) {
		if (convert_call_entry(nr_config_get_addr(i), message + len) == -1) {
			fprintf(stderr, "invalid callsign in nrports\n");
			exit(1);
		}
		len += 7;

		strcpy(message + len, nr_config_get_alias(i));
		strncat(message + len, "       ", 6 - strlen(message + len));
		len += 6;

		convert_call_entry(nr_config_get_addr(0), message + len);
		len += 7;

		message[len] = localval;
		len += 1;
	}
	
	if (sendto(s, message, len, 0, (struct sockaddr *)dest, dlen) == -1) {
		perror("send");
		exit(1);
	}
}

static void build_others(int s, int min_obs, struct full_sockaddr_ax25 *dest, int dlen, int port)
{
	unsigned char message[300];
	FILE *fpnodes, *fpneigh;
	char nodes_buffer[90];
	char neigh_buffer[90];
	char *callsign, *mnemonic, *neighbour;
	int  quality, neigh_no, obs_count;
	int  olen, len, n;

	if ((fpnodes = fopen(NODES_FILE, "r")) == NULL) {
		fprintf(stderr, "error: cannot open %s\n", NODES_FILE);
		exit(1);
	}

	if ((fpneigh = fopen(NEIGH_FILE, "r")) == NULL) {
		fprintf(stderr, "error: cannot open %s\n", NEIGH_FILE);
		exit(1);
	}

	do {
		len = olen = build_header(message);

		n = 0;

		while (fgets(nodes_buffer, 90, fpnodes) != NULL && len < port_list[port].paclen) {
			callsign = strtok(nodes_buffer, " ");
			mnemonic = strtok(NULL, " ");
			strtok(NULL, " ");
			strtok(NULL, " ");
			quality   = atoi(strtok(NULL, " "));
			obs_count = atoi(strtok(NULL, " "));
			neigh_no  = atoi(strtok(NULL, " "));
			neighbour = NULL;
			
			if (obs_count < min_obs || quality == 0) continue;

			fseek(fpneigh, 0L, SEEK_SET);
			
			while (fgets(neigh_buffer, 90, fpneigh) != NULL) {
				if (atoi(strtok(neigh_buffer, " ")) == neigh_no) {
					neighbour = strtok(NULL, " ");
					break;
				}
			}

			if (neighbour == NULL) {
				fprintf(stderr, "error: corruption in nodes/neighbour matching\n");
				exit(1);
			}

			if (convert_call_entry(callsign, message + len) == -1) {
				fprintf(stderr, "invalid callsign in /proc/net/nr_nodes\n");
				exit(1);
			}
			len += 7;

			strcpy(message + len, mnemonic);
			strncat(message + len, "       ", 6 - strlen(message + len));
			len += 6;

			if (convert_call_entry(neighbour, message + len) == -1) {
				fprintf(stderr, "invalid callsign in /proc/net/nr_neigh\n");
				exit(1);
			}
			len += 7;

			message[len] = quality;		
			len += 1;

			n++;
		}
	
		if (len > olen) {
			if (sendto(s, message, len, 0, (struct sockaddr *)dest, dlen) == -1) {
				perror("send");
				exit(1);
			}
		}

	} while (n == 11);

	fclose(fpnodes);
	fclose(fpneigh);
}

void transmit_nodes(int localval)
{
	struct full_sockaddr_ax25 dest;
	struct full_sockaddr_ax25 src;
	int s, dlen, slen;
	char path[25], *addr;
	int i;

	dlen = convert_call("NODES", &dest);

	for (i = 0; i < port_count; i++) {

		addr = ax25_config_get_addr(port_list[i].port);

		if (addr == NULL) continue;

		if (debug)
			fprintf(stdout, "netromd: transmitting NODES broadcast on port %d dev %s\n", port_list[i].port, port_list[i].device);

		sprintf(path, "%s %s", nr_config_get_addr(0), addr);

		convert_call(path, &src);
		slen = sizeof(struct full_sockaddr_ax25);

		if ((s = socket(AF_AX25, SOCK_DGRAM, NETROM_PID)) < 0) {
			perror("tx socket");
			exit(1);
		}

		if (bind(s, (struct sockaddr *)&src, slen) == -1) {
			close(s);
			continue;
		}

		build_mine(s, &dest, dlen, localval);

		if (port_list[i].verbose)
			build_others(s, port_list[i].minimum_obs, &dest, dlen, i);

		close(s);
	}

	if ((s = socket(AF_NETROM, SOCK_SEQPACKET, 0)) < 0) {
		perror("ioctl socket");
		exit(1);
	}

	if (ioctl(s, SIOCNRDECOBS, &i) == -1) {
		perror("dec obs");
		exit(1);
	}

	close(s);
}
