#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)
{
    printf("Usage: %s [--rset n] [--newrset name] [--period T] [--cpu C] "
	   "[--exec 'progname arguments']\n\n", pname);
    printf("\t--rset n\t specifies existing resource set in hex.\n");
    printf
	("\t--cpu C\t\t make CPU reservation with comp. time of C microsecs\n");
    printf
	("\t--period T\t make CPU reservation with period of T microsecs\n");
    printf
	("\t--deadline D\t make CPU reservation with deadline of D microsecs\n");
    printf("\t[--soft]\tmake reservation to be soft\n");
    printf("\t[--hard]\tmake reservation to be hard\n");
    printf("\t--help\t\t this message\n\n");
    printf("some examples...\n");
    printf("\t %s --rset <rset in hex> --cpu 2500 --period 10000 --hard\n",
	   pname);
    printf("\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;
    int period_us = 100000;	/* 100 ms by default */
    int deadline_us = 100000;	/* 100 ms by default */
    int cpu_us = 0;

    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},
	    {"period", 1, 0, 0},
	    {"deadline", 1, 0, 0},
	    {"help", 0, 0, 0},
	    {"hard", 0, 0, 0},
	    {"firm", 0, 0, 0},
	    {"soft", 0, 0, 0},
	    {"newrset", 1, 0, 0},
	    {0, 0, 0, 0}
	};

#define OPT_RSET 	0
#define OPT_CPU		1
#define OPT_PERIOD	2
#define OPT_DEADLINE	3
#define OPT_HELP	4
#define OPT_HARD	5
#define OPT_FIRM	6
#define OPT_SOFT	7
#define OPT_NEWRSET 8


	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_PERIOD:
		period_us = atoi(optarg);
		break;

	    case OPT_DEADLINE:
		deadline_us = atoi(optarg);
		break;

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

	    case OPT_HARD:
		cpu_reserve_mode = RSV_HARD;
		break;

	    case OPT_FIRM:
		cpu_reserve_mode = RSV_FIRM;
		break;

	    case OPT_SOFT:
		cpu_reserve_mode = RSV_SOFT;
		break;

	    case OPT_NEWRSET:
		rset = rk_resource_set_create(optarg);
		printf("resource_set(%x)\n",rset);
		newrset = 1;
		if (rset == (rk_resource_set_t) - 1) {
		    perror("rk_resource_set_create");
		    exit(-1);
		}
		break;
	    }
	    break;

	case '?':
	    break;
	default:
	    printf("?? getopt returned character code 0%o ??\n", c);
	    print_usage(argv[0]);
	}
    }

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


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

    /* CPU reserve */
    {
	rk_reserve_param_data_t p;
	cpu_reserve_attr_data_t attr;
	long int csec = cpu_us / 1000000;
	long int tsec = period_us / 1000000;
	long int dsec = 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 = (period_us - (tsec * 1000000)) * 1000;
	attr.deadline.tv_sec = dsec;
	attr.deadline.tv_nsec = (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);
	    }
	}
    }

    return 0;
}
