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

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

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

#include "config.h"

char nodes_usage[]  = "usage: nrparms -nodes nodecall +|- ident quality count dev neighbour\n";
char routes_usage[] = "usage: nrparms -routes dev nodecall +|- pathquality\n";

void display_parms(int s)
{
	struct nr_parms_struct nr_parms;

	if (ioctl(s, SIOCNRGETPARMS, &nr_parms) != 0) {
		perror("get parms");
		close(s);
		exit(1);
	}

	printf("Default path quality                 : %d\n", nr_parms.quality);	
	printf("Obsolescence count initialiser       : %d\n", nr_parms.obs_count);

	printf("Network \"time-to-live\" initialiser   : %d\n", nr_parms.ttl);

	printf("Transport timeout (seconds)          : %d\n", nr_parms.timeout / 10);
	printf("Transport maximum tries              : %d\n", nr_parms.tries);
	printf("Transport acknowledge delay (seconds): %d\n", nr_parms.ack_delay / 10);
	printf("Transport busy delay (seconds)       : %d\n", nr_parms.busy_delay / 10);
	printf("Transport requested window size      : %d\n", nr_parms.window);
}

void assign_parms(int s, int argc, char *argv[])
{
	struct nr_parms_struct nr_parms;
	int i, n;

	if (ioctl(s, SIOCNRGETPARMS, &nr_parms) != 0) {
		perror("get parms");
		close(s);
		exit(1);
	}

	i = 2;
	
	while (i < argc) {
		if (strcmp(argv[i], "-qual") == 0) {
			n = atoi(argv[i + 1]);
			if (n < 0 || n > 255) {
				fprintf(stderr, "nrparms: parms: invalid default path quality value %d\n", n);
			} else {
				nr_parms.quality = n;
			}
			
			i++;
		}

		if (strcmp(argv[i], "-obs") == 0) {
			n = atoi(argv[i + 1]);
			if (n < 0 || n > 255) {
				fprintf(stderr, "nrparms: parms: invalid obsolescence count value %d\n", n);
			} else {
				nr_parms.obs_count = n;
			}
			
			i++;
		}

		if (strcmp(argv[i], "-ttl") == 0) {
			n = atoi(argv[i + 1]);
			if (n < 1 || n > 255) {
				fprintf(stderr, "nrparms: parms: invalid time-to-live value %d\n", n);
			} else {
				nr_parms.ttl = n;
			}
			
			i++;
		}

		if (strcmp(argv[i], "-tto") == 0) {
			n = atoi(argv[i + 1]);
			if (n < 5 || n > 600) {
				fprintf(stderr, "nrparms: parms: invalid transport timeout value %d\n", n);
			} else {
				nr_parms.timeout = n * 10;
			}
			
			i++;
		}

		if (strcmp(argv[i], "-tries") == 0) {
			n = atoi(argv[i + 1]);
			if (n < 2 || n > 127) {
				fprintf(stderr, "nrparms: parms: invalid transport maximum tries value %d\n", n);
			} else {
				nr_parms.tries = n;
			}
			
			i++;
		}

		if (strcmp(argv[i], "-ack") == 0) {
			n = atoi(argv[i + 1]);
			if (n < 1 || n > 60) {
				fprintf(stderr, "nrparms: parms: invalid transport ack delay value %d\n", n);
			} else {
				nr_parms.ack_delay = n * 10;
			}
			
			i++;
		}

		if (strcmp(argv[i], "-busy") == 0) {
			n = atoi(argv[i + 1]);
			if (n < 1 || n > 1000) {
				fprintf(stderr, "nrparms: parms: invalid transport busy delay value %d\n", n);
			} else {
				nr_parms.busy_delay = n * 10;
			}
			
			i++;
		}

		if (strcmp(argv[i], "-win") == 0) {
			n = atoi(argv[i + 1]);
			if (n < 1 || n > 127) {
				fprintf(stderr, "nrparms: parms: invalid transport requested window size value %d\n", n);
			} else {
				nr_parms.window = n;
			}
			
			i++;
		}

		i++;
	}

	if (ioctl(s, SIOCNRSETPARMS, &nr_parms) != 0) {
		perror("set parms");
		close(s);
		exit(1);
	}
}

void nodes(int s, char *nodecall, char *op, char *ident, int quality, int count, char *dev, char *neighbour)
{
	struct nr_route_struct nr_node;
	char *p, *q;

	nr_node.type = NETROM_NODE;

	if (op[0] != '+' && op[0] != '-') {
		fprintf(stderr, "nrparms: nodes: invalid operation %s\n", op);
		close(s);
		exit(1);
	}

	if (quality < 1 || quality > 255) {
		fprintf(stderr, "nrparms: nodes: invalid quality %d\n", quality);
		close(s);
		exit(1);
	}

	if (count < 1 || count > 6) {
		fprintf(stderr, "nrparms: nodes: invalid obs count %d\n", count);
		close(s);
		exit(1);
	}

	if (convert_call_entry(nodecall, nr_node.callsign.ax25_call) != 0) {
		fprintf(stderr, "nrparms: nodes: invalid callsign %s\n", nodecall);
		close(s);
		exit(1);
	}

	if (strlen(ident) > 7) {
		fprintf(stderr, "nrparms: nodes: invalid length of mnemonic %s\n", ident);
		close(s);
		exit(1);
	}
		
	for (p = ident, q = nr_node.mnemonic; *p != '\0'; p++, q++)
		*q = toupper(*p);
	*q = '\0';

	if (convert_call_entry(neighbour, nr_node.neighbour.ax25_call) != 0) {
		fprintf(stderr, "nrparms: nodes: invalid callsign %s\n", nodecall);
		close(s);
		exit(1);
	}

	if (strlen(dev) > 5) {
		fprintf(stderr, "nrparms: nodes: invalid length of device %s\n", dev);
		close(s);
		exit(1);
	}

	strcpy(nr_node.device, dev);
	
	nr_node.quality   = quality;
	nr_node.obs_count = count;

	if (op[0] == '+') {
		if (ioctl(s, SIOCADDRT, &nr_node) == -1) {
			perror("add route");
			close(s);
			exit(1);
		}
	} else {
		if (ioctl(s, SIOCDELRT, &nr_node) == -1) {
			perror("del route");
			close(s);
			exit(1);
		}
	}
}

void routes(int s, char *dev, char *nodecall, char *op, int quality)
{
	struct nr_route_struct nr_neigh;

	nr_neigh.type = NETROM_NEIGH;

	if (op[0] != '+' && op[0] != '-') {
		fprintf(stderr, "nrparms: routes: invalid operation %s\n", op);
		close(s);
		exit(1);
	}

	if (quality < 1 || quality > 255) {
		fprintf(stderr, "nrparms: routes: invalid quality %d\n", quality);
		close(s);
		exit(1);
	}

	if (strlen(dev) > 5) {
		fprintf(stderr, "nrparms: routes: invalid length of device %s\n", dev);
		close(s);
		exit(1);
	}

	strcpy(nr_neigh.device, dev);

	if (convert_call_entry(nodecall, nr_neigh.callsign.ax25_call) != 0) {
		fprintf(stderr, "nrparms: routes: invalid callsign %s\n", nodecall);
		close(s);
		exit(1);
	}

	nr_neigh.quality   = quality;

	if (op[0] == '+') {
		if (ioctl(s, SIOCADDRT, &nr_neigh) == -1) {
			perror("add route");
			close(s);
			exit(1);
		}
	} else {
		if (ioctl(s, SIOCDELRT, &nr_neigh) == -1) {
			perror("del route");
			close(s);
			exit(1);
		}
	}
}

int main(int argc, char *argv[])
{
	int s;
	
	if ((s = socket(AF_NETROM, SOCK_SEQPACKET, 0)) < 0) {
		perror("socket");
		return 1;
	}
	
	if (strncmp(argv[1], "-n", 2) == 0) {
		if (argc < 9) {
			fprintf(stderr, nodes_usage);
			close(s);
			return 1;
		}
		nodes(s, argv[2], argv[3], argv[4], atoi(argv[5]), atoi(argv[6]), argv[7], argv[8]);
		close(s);
		return 0;
	}

	if (strncmp(argv[1], "-p", 2) == 0) {
		if (argc == 2) {
			display_parms(s);
		} else {
			assign_parms(s, argc, argv);
		}
		close(s);
		return 0;
	}

	if (strncmp(argv[1], "-r", 2) == 0) {
		if (argc < 6) {
			fprintf(stderr, routes_usage);
			close(s);
			return 1;
		}
		routes(s, argv[2], argv[3], argv[4], atoi(argv[5]));
		close(s);
		return 0;
	}

	fprintf(stderr, "usage: nrparms -nodes|-parms|-routes ...\n");
	
	close(s);
	
	return 1;
}
