/**
 * Copyright (C) Mellanox Technologies Ltd. 2016.  ALL RIGHTS RESERVED.
 *
 * See file LICENSE for terms.
 */

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <unistd.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/time.h>
#include <api/sharp.h>
#include "common/sharp_common.h"

static char *ib_dev_list;
static int verbose_level = 2;

enum {
	PARSE_ARGS_OK		= 0,
	PARSE_ARGS_HELP		= 1,
	PARSE_ARGS_VERSION	= 2,
	PARSE_ARGS_INVALID	= 3,
};

static struct option long_options[] = {
	{"help", no_argument, NULL, 'h'},
	{"ib-dev", required_argument, NULL, 'd'},
	{"verbose", required_argument, NULL, 'v'},
	{"version", no_argument, NULL, 'V'},
	{0, 0, 0, 0}
};
static const char usage_string[] =
		"usage:  sharp_hello <-d | --ib_dev> <device> [OPTIONS]\n"
		"OPTIONS:\n"
		"\t[-d | --ib_dev]      - HCA to use\n"
		"\t[-v | --verbose]     - libsharp coll verbosity level(default :2)\n"
		"\t\t\t\t  Levels: (0-fatal 1-err 2-warn 3-info 4-debug 5-trace)\n"
		"\t[-V | --version]     - print program version\n"
		"\t[-h | --help]        - show this usage\n\n";

static int oob_bcast(void *comm_context, void *buf, int size, int root)
{
	return 0;
}

static int oob_barrier(void *comm_context)
{
	return 0;
}

static int oob_gather(void *comm_context, int root, void *sbuf, void *rbuf, int len)
{
	memcpy(rbuf, sbuf, len);
	return 0;
}

static char *get_host_name(void)
{
    static char hostname[256] = {0};

    if (*hostname == 0) {
        gethostname(hostname, sizeof(hostname));
        strtok(hostname, ".");
    }
    return hostname;
}

int parse_opts(int argc, char **argv)
{
	int opt, option_index;
	int res = PARSE_ARGS_OK;
	char *endptr;

	while (res == PARSE_ARGS_OK) {
		option_index = 0;
		endptr = "\0";
		opt = getopt_long(argc, argv, "Vhd:v:", long_options, &option_index);
		if (opt == -1)
			break;
		switch (opt) {
		case 'h':
			return PARSE_ARGS_HELP;
		case 'd':
			free(ib_dev_list);
			ib_dev_list = strdup(optarg);
			break;
		case 'v':
			verbose_level = strtol(optarg, &endptr, 10);
			if (verbose_level >=0 && verbose_level <= 5) {
				setenv("SHARP_COLL_LOG_LEVEL", optarg, 1);
			} else {
				fprintf(stderr, "invalid sharp coll verbose level\n");
				return PARSE_ARGS_HELP;
			}

			break;
		case 'V':
			sharp_print_version(stdout, "sharp_hello");
			return PARSE_ARGS_VERSION;
		default:
			res = PARSE_ARGS_INVALID;
		}
	}

	if ((opt == -1 && argv[optind] != NULL))
		return PARSE_ARGS_INVALID;

	if (ib_dev_list == NULL) {
		fprintf(stderr, "please specify ib device to use\n");
		return PARSE_ARGS_HELP;
	}

	return res;
}

int main(int argc, char **argv)
{
	int ret = 0, rc;
	struct sharp_coll_init_spec init_spec = {0};
	struct sharp_coll_context *sharp_coll_context;
	struct sharp_coll_comm *sharp_coll_comm;
	struct sharp_coll_comm_init_spec comm_spec;
	struct timeval tval;

	rc = parse_opts(argc, argv);
	if (rc != PARSE_ARGS_OK) {
		if (rc == PARSE_ARGS_HELP) {
			fprintf(stdout, "%s", usage_string);
			goto exit;
		}
		if (rc == PARSE_ARGS_VERSION) {
			goto exit;
		}
		ret = -rc;
		goto exit;
	}

	gettimeofday(&tval, NULL);
	srand((int) tval.tv_usec);

	init_spec.progress_func  = NULL;
	/* coverity[dont_call] */
	init_spec.job_id = (gethostid() << 32) | rand();
	init_spec.hostlist = get_host_name();
	init_spec.world_rank = 0;
	init_spec.world_size = 1;
	init_spec.oob_colls.barrier = oob_barrier;
	init_spec.oob_colls.bcast = oob_bcast;
	init_spec.oob_colls.gather = oob_gather;
	init_spec.config = sharp_coll_default_config;
	init_spec.config.ib_dev_list = ib_dev_list;

	/* initialize sharp coll */
	ret = sharp_coll_init(&init_spec, &sharp_coll_context);
	if (ret < 0) {
		fprintf(stderr, "sharp_coll_init failed: %s\n", sharp_coll_strerror(ret));
		goto exit;
	}


	/* create sharp group */
	comm_spec.rank = 0;
	comm_spec.size = 1;
	comm_spec.is_comm_world = 1;
	comm_spec.oob_ctx = NULL;
	ret = sharp_coll_comm_init(sharp_coll_context, &comm_spec, &sharp_coll_comm);
	if (ret < 0) {
		fprintf(stderr, "sharp communicator creation failed: %s\n", sharp_coll_strerror(ret));
		goto coll_finalize;
	}

	/* run Barrier */
	ret = sharp_coll_do_barrier(sharp_coll_comm);
	if (ret == SHARP_COLL_SUCCESS) {
		fprintf(stdout, "Test Passed.\n");
	} else {
		fprintf(stderr, "Test Failed: %s\n", sharp_coll_strerror(ret));
	}

	/* destroy group */
	sharp_coll_comm_destroy(sharp_coll_comm);

coll_finalize:
	/* finalize sharp coll */
	sharp_coll_finalize(sharp_coll_context);
exit:
	free(ib_dev_list);
	return ret;
}
