#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <limits.h>
#include <assert.h>
#include <rk/rk.h>
#include <rk/rk_error.h>

#define DEBUG


void print_error(char *emsg)
{
	fprintf(stderr, "%s", emsg);
	exit(-1);
}

void print_usage(char *pname)
{
	fprintf(stderr, "Usage: %s [--rset n] [--newrset name]\n", pname);
	fprintf(stderr, "\t--rset n\t specifies existing resource set in hex.\n");
	fprintf(stderr, "\t--cpu C\t\t make CPU reservation with comp. time of C microsecs\n");
	fprintf(stderr, "\t--cpu_period T\t make CPU reservation with period of T microsecs\n");
	fprintf(stderr, "\t--cpu_deadline D\t make CPU reservation with deadline of D microsecs\n");
	fprintf(stderr, "\t--cpu_bind C\t bind CPU reservation to CPU C\n");
	fprintf(stderr, "\t[--cpu_soft]\t make reservation to be soft\n");
	fprintf(stderr, "\t[--cpu_hard]\t make reservation to be hard\n");
	fprintf(stderr, "\t--net B\t\t make NET reservation with rate of B bytes\n");
	fprintf(stderr, "\t--net_period T\t make NET reservation with period of T microsecs\n");
	fprintf(stderr, "\t--net_deadline D\t make NET reservation with deadline of D microsecs\n");
	fprintf(stderr, "\t[--net_soft]\t make reservation to be soft\n");
	fprintf(stderr, "\t[--net_hard]\t make reservation to be hard\n");
	fprintf(stderr, "\t--rnet B\t\t make NET reservation with rate of B bytes\n");
	fprintf(stderr, "\t--rnet_period T\t make NET reservation with period of T microsecs\n");
	fprintf(stderr, "\t--rnet_deadline D\t make NET reservation with deadline of D microsecs\n");
	fprintf(stderr, "\t[--rnet_soft]\t make reservation to be soft\n");
	fprintf(stderr, "\t[--rnet_hard]\t make reservation to be hard\n");
	fprintf(stderr, "\t[--hard]\t make reservation to be hard\n");
	fprintf(stderr, "\t--help\t\t this message\n\n");
	fprintf(stderr, "some examples...\n");
	fprintf(stderr, "\t %s --rset <rset in hex> --cpu 2500 --period 10000 --hard\n",
		pname);
	fprintf(stderr, "\t %s --rset <rset in hex> --cpu 5000 --period 10000 "
		"--deadline 8000 --hard\n",pname);
	exit(1);
}

/*
 * rkcontrol
 */
int main(int argc, char *argv[])
{
	int c,newrset=0;
	char cpu_reserve_mode = RSV_HARD;
	char net_reserve_mode = RSV_HARD;
	char rnet_reserve_mode = RSV_HARD;

	int cpu_period_us = 100000;		  /* 100 ms by default */
	int cpu_deadline_us = 100000;	  /* 100 ms by default */
	int cpu_start_us = 0;
	int cpu_us = -1;
	

	int net_period_us = 100000;		  /* 100 ms by default */
	int net_deadline_us = 100000;	  /* 100 ms by default */
	int net_change_flag = -1;
	int net_start_us = 0;
	int net_us = -1;
	
	int rnet_period_us = 100000;		  /* 100 ms by default */
	int rnet_deadline_us = 100000;	  /* 100 ms by default */
	int rnet_start_us = -1;
	int rnet_us = -1;

	rk_reserve_t cpu_rsv;
	rk_reserve_t net_rsv;
	rk_reserve_t rnet_rsv;
	
	rk_resource_set_t rset = NULL;

	while (1) {
		int option_index = 0;
		static struct option long_options[] = {
			{"rset", 1, 0, 0},
			{"cpu", 1, 0, 0},
			{"cpu_period", 1, 0, 0},
			{"cpu_deadline", 1, 0, 0},
			{"help", 0, 0, 0},
			{"cpu_hard", 0, 0, 0},
			{"cpu_soft", 0, 0, 0},
			{"newrset", 1, 0, 0},
			{"cpu_bind", 1, 0, 0},
			{"net", 1, 0, 0},
			{"net_period", 1, 0, 0},
			{"net_deadline", 1, 0, 0},
			{"net_hard", 0, 0, 0},
			{"net_soft", 0, 0, 0},
			{"rnet", 1, 0, 0},
			{"rnet_period", 1, 0, 0},
			{"rnet_deadline", 1, 0, 0},
			{"rnet_hard", 0, 0, 0},
			{"rnet_soft", 0, 0, 0},
		{0, 0, 0, 0}
		};

#define OPT_RSET 	0
#define OPT_CPU		1
#define OPT_CPU_PERIOD	2
#define OPT_CPU_DEADLINE	3
#define OPT_CPU_STARTTIME  4 
#define OPT_HELP	5
#define OPT_CPU_HARD	6	
#define OPT_CPU_FIRM	7
#define OPT_CPU_SOFT	8
#define OPT_NEWRSET 9
#define OPT_CPU_BIND     10
#define OPT_NET		 11
#define OPT_NET_PERIOD	12
#define OPT_NET_DEADLINE	13   
#define OPT_NET_START	14   
#define OPT_NET_HARD    15
#define OPT_NET_FIRM	16
#define OPT_NET_SOFT	17
#define OPT_RNET		18
#define OPT_RNET_PERIOD	19
#define OPT_RNET_DEADLINE	 20  
#define OPT_RNET_START	 21  
#define OPT_RNET_HARD    22
#define OPT_RNET_FIRM	 23
#define OPT_RNET_SOFT	24
 

		c = getopt_long_only(argc, argv, "", long_options, &option_index);

		/* bunch of option handling... */
		if (c == -1)
			break;
		switch (c) {
		case 0:

#ifdef DEBUG
			printf("option[%d] %s", option_index,
				long_options[option_index].name);
			if (optarg)
				printf(" with arg %s", optarg);
			printf("\n");
#endif

			switch (option_index) {
			case OPT_RSET:
				/* we want base 16 */
				rset = (rk_resource_set_t) strtoul(optarg, NULL, 16);
				if (rset == (rk_resource_set_t) ULONG_MAX) {
					perror("ULONG_MAX");
					exit(-1);
				}
				break;

			case OPT_CPU:
				cpu_us = atoi(optarg);
				break;

			case OPT_CPU_PERIOD:
				cpu_period_us = atoi(optarg);
				break;

			case OPT_CPU_DEADLINE:
				cpu_deadline_us = atoi(optarg);
				break;
			
			case OPT_CPU_STARTTIME:
				cpu_start_us = atoi(optarg);
				break;

			case OPT_HELP:
				print_usage(argv[0]);
				break;

			case OPT_CPU_HARD:
				cpu_reserve_mode = RSV_HARD;
				break;

			case OPT_CPU_FIRM:
				cpu_reserve_mode = RSV_FIRM;
				break;

			case OPT_CPU_SOFT:
				cpu_reserve_mode = RSV_SOFT;
				break;
			
			case OPT_NEWRSET:
				rset = rk_resource_set_create(optarg);
				newrset = 1;
				if (rset==(rk_resource_set_t)-1) {
					perror("rk_resource_set_create");
					exit(-1);
				}
				break;
			
			case OPT_CPU_BIND:
				
				break;
			
			case OPT_NET:
				net_us = atoi(optarg);
#ifdef DEBUG
				printf("net_us %d\n",net_us);
#endif
				break;
			
			case OPT_NET_PERIOD:
				net_period_us = atoi(optarg);
				net_change_flag = 0;
				break;
			
			case OPT_NET_DEADLINE:
				net_deadline_us = atoi(optarg);
				net_change_flag = 0;
				break;
			
			case OPT_NET_START:
				net_start_us = atoi(optarg);
				break;
			
			case OPT_NET_HARD:
				net_reserve_mode = RSV_HARD;
				break;
			
			case OPT_NET_FIRM:
				net_reserve_mode = RSV_FIRM;
				break;
		
			case OPT_NET_SOFT:
				net_reserve_mode = RSV_SOFT;
				break;
			
			case OPT_RNET:
				rnet_us = atoi(optarg);
				break;
			
			case OPT_RNET_PERIOD:
				rnet_period_us = atoi(optarg);
				break;
			
			case OPT_RNET_DEADLINE:
				rnet_deadline_us = atoi(optarg);
				break;
			
			case OPT_RNET_START:
				rnet_start_us = atoi(optarg);
				break;
			
			case OPT_RNET_HARD:
				rnet_reserve_mode = RSV_HARD;
				break;
			
			case OPT_RNET_FIRM:
				rnet_reserve_mode = RSV_FIRM;
				break;
		
			case OPT_RNET_SOFT:
				rnet_reserve_mode = RSV_SOFT;
				break;
			}
		case '?':
			break;
		default:
			fprintf(stderr, "?? getopt returned character code 0%o ??\n", c);
			print_usage(argv[0]);
		}
	}

	if (optind < argc) {
		fprintf(stderr, "undefined arguments....ignoring   ");
		while (optind < argc)
			fprintf(stderr, "%s ", argv[optind++]);
		fprintf(stderr, "\n");
	}

	if( cpu_deadline_us > cpu_period_us)
		cpu_deadline_us = cpu_period_us;

	if( net_deadline_us > net_period_us)
		net_deadline_us = net_period_us;

	if( rnet_deadline_us > rnet_period_us)
		rnet_deadline_us = rnet_period_us;


	if (rset == NULL)
		print_usage(argv[0]);

	if(cpu_us != -1) {
	/* CPU reserve */
		rk_reserve_param_data_t p;
		cpu_reserve_attr_data_t attr;
		long int csec = cpu_us / 1000000;
		long int tsec = cpu_period_us / 1000000;
		long int dsec = cpu_deadline_us / 1000000;

		assert(rset != NULL);

		p.enf_mode = cpu_reserve_mode;
		p.sch_mode = cpu_reserve_mode;
		p.rep_mode = cpu_reserve_mode;

		attr.compute_time.tv_sec = csec;
		attr.compute_time.tv_nsec = (cpu_us - (csec * 1000000)) * 1000;
		attr.period.tv_sec = tsec;
		attr.period.tv_nsec = (cpu_period_us - (tsec * 1000000)) * 1000;
		attr.deadline.tv_sec = dsec;
		attr.deadline.tv_nsec = (cpu_deadline_us - (dsec * 1000000)) * 1000;
		attr.blocking_time.tv_sec = (long int) 0;
		attr.blocking_time.tv_nsec = (long int) 0;
		attr.start_time.tv_sec = (long int) 0;
		attr.start_time.tv_nsec = (long int) 0;
		attr.reserve_type = p;
		

		if ((!newrset)  
			) {

			if (rk_cpu_reserve_ctl(rset, &attr) != RK_SUCCESS) {
				fprintf(stderr, "Could not modify cpu reserve, quitting...\n");
				exit(-2);
			}
		} else {
			if (!rk_cpu_reserve_create(rset, &attr)) {
				fprintf(stderr, "Could not create cpu reserve, quitting...\n");
				rk_resource_set_destroy(rset);
				exit(-2);
			}
		}
	}
	
	if(net_us != -1) {
	/* NET reserve */
		rk_reserve_param_data_t p;
		net_reserve_attr_data_t net_attr;
		
		long int tsec = net_period_us / 1000000;
		long int dsec = net_deadline_us / 1000000;
		
		bzero(&net_attr, sizeof(net_attr));
		
		assert(rset != NULL);

		p.enf_mode = net_reserve_mode;
		p.sch_mode = net_reserve_mode;
		p.rep_mode = net_reserve_mode;

		net_attr.amount = net_us;

		net_attr.period.tv_sec = tsec;
  		net_attr.period.tv_nsec = (net_period_us - (tsec * 1000000)) * 1000; 
  		net_attr.deadline.tv_sec = dsec;
  		net_attr.deadline.tv_nsec = (net_deadline_us - (tsec * 1000000)) * 1000 ; 
  		net_attr.start_time.tv_sec = 0;
  		net_attr.start_time.tv_nsec = 0;
  		net_attr.blocking_time.tv_sec = 0;
  		net_attr.blocking_time.tv_nsec = 0;
  		net_attr.reserve_type= p;
	
#ifdef DEBUG
		printf("net_reserve_mode %x\n",net_reserve_mode);
#endif
		if ((!newrset)) {

			if (rk_net_reserve_ctl(rset, &net_attr) != RK_SUCCESS) {
				fprintf(stderr, "Could not modify net reserve, quitting...\n");
				exit(-2);
			}
		} else {
			if (rk_net_reserve_create(rset, &net_attr)) {
				fprintf(stderr, "Could not create net reserve, quitting...\n");
				rk_resource_set_destroy(rset);
				exit(-2);
			}
		}
	}

	if(rnet_us != -1) {
	/* RNET reserve */
		rk_reserve_param_data_t p;
		net_reserve_attr_data_t rnet_attr;
		
		long int tsec = rnet_period_us / 1000000;
		long int dsec = rnet_deadline_us / 1000000;
		
		bzero(&rnet_attr, sizeof(rnet_attr));

		p.enf_mode = rnet_reserve_mode;
		p.sch_mode = rnet_reserve_mode;
		p.rep_mode = rnet_reserve_mode;

		rnet_attr.amount = rnet_us;
		rnet_attr.period.tv_sec = tsec;
  		rnet_attr.period.tv_nsec =  (rnet_period_us - (tsec * 1000000)) * 1000; 
  		rnet_attr.deadline.tv_sec = dsec;
  		rnet_attr.deadline.tv_nsec = (rnet_deadline_us - (tsec * 1000000)) * 1000;
  		rnet_attr.start_time.tv_sec = 0;
  		rnet_attr.start_time.tv_nsec = 0;
  		rnet_attr.blocking_time.tv_sec = 0;
  		rnet_attr.blocking_time.tv_nsec = 0;
  		rnet_attr.reserve_type= p;
	
		if (!newrset) {

			if (rk_rnet_reserve_ctl(rset, &rnet_attr) != RK_SUCCESS) {
				fprintf(stderr, "Could not modify rnet reserve, quitting...\n");
				exit(-2);
			}
		} else {
			if(cpu_us == -1) {
				fprintf(stderr, "Could not create rnet reserve, please create cpu first...\n");
				exit(-2);
			}
			if (rk_rnet_reserve_create(rset,&rnet_attr)) {
				fprintf(stderr, "Could not create rnet reserve, quitting...\n");
				rk_resource_set_destroy(rset);
				exit(-2);
			}
		}
	}

	return 0;
}
