/************************************************************
 *
 * COPYRIGHT (C) HITACHI,LTD. 2002-2003 ALL RIGHTS RESERVED.
 * WRITTEN BY HITACHI SYSTEMS DEVELOPMENT LABORATORY,
 *            HITACHI CENTRAL RESEARCH LABORATORY.
 *
 * Created by K.Hatasaki <keisukeh@crl.hitachi.co.jp>
 * Updated by M.Hiramatsu <hiramatu@sdl.hitachi.co.jp>
 *
 ************************************************************/

#include <stdlib.h>
#include "command.h"
#include "buffer.h"

#define NAME	"lkstbuf"
#define VERSION "1.7"

void usage(void);

void call_usage(int *, char **);
void call_version(int*, char**);
void call_list(int*, char**);
void call_link(int*, char**);
void call_create(int*, char**);
void call_del(int*, char**);
void call_read(int*, char**);
void call_shift(int*, char**);
void call_jump(int*, char**);

void call_print(int*, char**);
int  nors_analyzer(char*);

#define NR_CMD  14

int main(int argc, char **argv)
{
	int i;

	struct command cmd[NR_CMD] = 
	{
		[0] = {"help", &call_usage},
		[1] = {"ver", &call_version},
		[2] = {"version", &call_version},
		[3] = {"list", &call_list},
		[4] = {"ls", &call_list},
		[5] = {"create", &call_create},
		[6] = {"delete", &call_del},
		[7] = {"del", &call_del},
		[8] = {"read", &call_read},
		[9] = {"shift", &call_shift},
		[10] = {"print", &call_print},
		[11] = {"link", &call_link},
		[12] = {"ln", &call_link},
		[13] = {"jump", &call_jump},
	};

	if (argc == 1) { /* no command */
		usage();
	} else {
		for (i = 0; i < NR_CMD; i++) {
			if (!strcmp(cmd[i].name, argv[1])) {
				cmd[i].func(&argc, argv);
				break;
			}
		}
		if (i == NR_CMD)
			badcommand(argv[1]);
	}

	exit(0);
}

void usage(void)
{
	printf("This controls kernel event buffers in LKST.\n");
	printf("Usage:\n\t%s command [option(s)]\n\n", NAME);
	printf("<COMMAND>\n");
	printf("shift\t\tChange currently selected buffer to next buffer.\n");
	printf("\t\t<OPTION>\n");
	printf("\t\t[-c cpu_id]\n");
	printf("jump\t\tChange currently selected buffer to specified buffer.\n");
	printf("\t\t<OPTION>\n");
	printf("\t\t[-c cpu_id] -b buffer_id\n");
	printf("create\t\tCreate a new kernel event buffer\n");
	printf("\t\t<OPTION>\n");
	printf("\t\t[-c cpu_id] -s size[K|M] [-b buffer_id] [-n next-buffer_id]\n");
	printf("delete/del\tDelete a kernel event buffer.\n");
	printf("\t\t<OPTION>\n");
	printf("\t\t[-c cpu_id] -b buffer_id\n");
	printf("link/ln\t\tSet the next buffer for the shift operation.\n");
	printf("\t\t<OPTION>\n");
	printf("\t\t[-c cpu_id] -b buffer_id [-n next_buffer_id]\n");	
	printf("list/ls\t\tOutput a list of all kernel event buffers.\n");
	printf("\t\t<OPTION>\n");
	printf("\t\t[-c cpu_id] [-v]\n");
	printf("read\t\tOutput a content of kernel event buffer(s).\n");
	printf("\t\t<OPTION>\n");
	printf("\t\t[-c cpu_id] [-b buffer_id] [-f output_file] [-n number]\n");
	printf("\t\t-S [-f output_file] [-n number]\n");
	printf("print\t\tOutput a content of kernel event buffer(s) infomation.\n");
	printf("\t\t<OPTION>\n");
	printf("\t\t-f log_file [-c cpu_id] [-e 'event_name,!eventname']\n");
	printf("\t\t\t[-n number] [-r] [-C] [-V] [-h]\n");
	printf("version/ver\tPrint version of this command.\n");
	printf("help\t\tPrint this message.\n");
	exit(0);
}

void call_usage(int *argc, char **argv)
{
	usage();
}

void call_version(int *argc, char **argv)
{
	version(NAME, VERSION);
}

void call_list(int *argc, char **argv)
{
	int c,cpu=-1,v=0;
	while((c = getopt(*argc, argv, "c:v")) != EOF) {
		switch(c) {
		case 'c' :
			cpu = STR2CPU(optarg);
			break;
		case 'v' :
			v=1;
			break;
		default :
			usage();
		}
	}
	if(v)
		buffer_list(cpu,0);
	else
		classical_buffer_list(cpu);
}

void call_link(int *argc, char **argv)
{
	int c;
	int cpu = -1;
	lkst_buffer_id id = LKST_BUFFER_ID_VOID;
	lkst_buffer_id next = LKST_BUFFER_ID_VOID;

	while((c = getopt(*argc, argv, "b:c:n:")) != EOF) {
		switch(c) {
		case 'b' :
			id = STR2BID(optarg);
			break;
		case 'n' :
			next = STR2BID(optarg);
			break;
		case 'c' :
			cpu = STR2CPU(optarg);
			break;
		default :
			usage();
		}
	}

	if (id==LKST_BUFFER_ID_VOID)
		needopt();

	buffer_link(cpu, id, next);
}

void call_create(int *argc, char **argv)
{
	int c;
	int size_set = 0;
	int cpu = -1;
	lkst_buffer_id id = LKST_BUFFER_ID_VOID;
	lkst_buffer_id nxid = LKST_BUFFER_ID_VOID;
	size_t size;
	char * tmp;

	while((c = getopt(*argc, argv, "b:c:s:n:")) != EOF) {
		switch(c) {
		case 'b' :
			id = STR2BID(optarg);
			break;
		case 'c' :
			cpu = STR2CPU(optarg);
			break;
		case 's' :
			size_set = 1;
			size = strtol(optarg,&tmp,0);
			if(tmp[0] == 'M' ) {
				tmp ++;
				size *= 1024*1024;
			}else if(tmp[0] == 'K') {
				tmp ++;
				size *= 1024;
			}
			if (tmp[0]!='\0') usage();
			break;
		case 'n':
			nxid = STR2BID(optarg);
			break;
		default :
			usage();
		}
	}

	if (!size_set)
		needopt();

	buffer_create(cpu, id, size, nxid);
}

void call_del(int *argc, char **argv)
{
	int c;
	lkst_buffer_id id=LKST_BUFFER_ID_VOID;
	int cpu=-1;

	while((c = getopt(*argc, argv, "b:c:")) != EOF) {
		switch(c) {
		case 'b' :
			id = STR2BID(optarg);
			break;
		case 'c' :
			cpu = STR2CPU(optarg);
			break;
		default :
			usage();
		}
	}
	if(id == LKST_BUFFER_ID_VOID)
		needopt();

	buffer_delete(cpu, id);
}

void call_read(int *argc, char **argv)
{
	int c;
	int fd = -1;
	int cpu = -1, online = 0;
	int file_set = 0;
	int static_buffer = 0;
	lkst_buffer_id id = LKST_BUFFER_ID_VOID;
	char file_name[255];
	unsigned int num = 0;

	while((c = getopt(*argc, argv, "c:b:n:f:S")) != EOF) {
		switch(c) {
		case 'c':
			if(static_buffer)usage();
			cpu = STR2CPU(optarg);
			break;
		case 'b' :
			if(static_buffer)usage();
			id = STR2BID(optarg);
			break;
		case 'n' :
			num = strtol(optarg,NULL,0);
			break;
		case 'f' :
			file_set = 1;
			strncpy(file_name, optarg, 255);
			break;
		case 'S':
			if(cpu!=-1||id!=LKST_BUFFER_ID_VOID)usage();
			static_buffer = 1;
			break;
		default :
			usage();
		}
	}

	if(cpu != -1 && static_buffer!=0)
		needopt();

	if (file_set) {
		if (0 == strncmp("-", file_name, 2))
			fd = 1;/* stdout */
		else if ((fd = open(file_name, O_WRONLY|O_CREAT|O_TRUNC,0644)) < 0){
			perror("output");
			exit(1);
		}
	}

	if ( static_buffer ) {
		buffer_read(-1, LKST_BUFFER_ID_VOID, num, fd);
		return ;
	}
	
	if(cpu == -1 ) { // then, fork per processor.
		struct lkst_status_param sparam;
		if ( lkst_get_status(&sparam) < 0){
			perror("get status");
			exit(1);
		}
		online = sparam.online_cpu;
	}else 
		online = 1UL << cpu;

	for_each_online_cpus(cpu, online){
		if( id == LKST_BUFFER_ID_VOID)
			buffer_read_all(cpu, num, fd);
		else 
			buffer_read(cpu, id, num, fd);
	}
}

void call_shift(int *argc, char **argv)
{
	int c;
	int cpu = -1;

	if ( (c = getopt(*argc, argv, "c:")) != EOF) {
		switch(c) {
		case 'c' :
			cpu = strtol(optarg,NULL,0);
			break;
		default :
			usage();
			break;
		}
	}

	buffer_shift(cpu);
}

void call_jump(int *argc, char **argv)
{
	int c;
	lkst_buffer_id dst=LKST_BUFFER_ID_VOID;
	int cpu=-1;

	while((c = getopt(*argc, argv, "b:c:")) != EOF) {
		switch(c) {
		case 'b' :
			dst = strtol(optarg,NULL,0);
			break;
		case 'c' :
			cpu = strtol(optarg,NULL,0);
			break;
		default :
			usage();
		}
	}
	if(dst == LKST_BUFFER_ID_VOID)
		needopt();

	buffer_jump(cpu, dst);
}

void call_print(int *argc, char **argv)
{
	int  c;
	int  file_set        = 0;
	int  arg_cpu_id      = -1;
	int  arg_filter_flag = 0;
	int  arg_entry_num   = -1;
	int  arg_sort_flag   = 0;
	int  help_flag       = 0;
	int  csv_form_flag   = 0;
	int  verbose = 0;
	char* program_name;
	char arg_inputfile[255];
	char event_work[255];
	lkst_buffer_id id = LKST_BUFFER_ID_VOID;

	program_name = argv[0];

	while ((c = getopt(*argc, argv, "b:f:c:e:n:rhCV")) != EOF) {
		switch (c) {
		case 'b' :
			if (nors_analyzer(optarg)) {
				id = STR2BID(optarg);
			} else {
				usage();
			}
			break;
		case 'f' :
			file_set = 1;
			strncpy(arg_inputfile, optarg, 255);
			break;
		case 'c' :
			if (nors_analyzer(optarg)) {
				arg_cpu_id = STR2CPU(optarg);
			} else {
				usage();
			}
			break;
		case 'e' :
			arg_filter_flag = 1;
			strncpy(event_work, optarg, 255);
			break;
		case 'n' :
			if (nors_analyzer(optarg)) {
				arg_entry_num = strtol(optarg,NULL,0);
			} else {
				usage();
			}
			break;
		case 'r' :
			arg_sort_flag = 1;
			break;
		case 'h' :
			help_flag = 1;
			break;
		case 'C':
			csv_form_flag = 1;
			break;
		case 'V':
			verbose ++;
			break;
		default :
			usage();
			break;
		}
	}

	if (help_flag) {
		help_viewer(program_name);
		exit(1);
	}
	if (file_set) {
		buffer_print_wonly(program_name, arg_cpu_id, arg_filter_flag,
				   arg_entry_num, arg_sort_flag, 
				   csv_form_flag,arg_inputfile, 
				   event_work, verbose);
	}else
		needopt();
}

int nors_analyzer(char* str)
{
	int i;
	for (i=0;i<(signed int)strlen(str);i++) {
		if ( !(( str[i]>='0' )&&( str[i]<='9' )) ) {
			return 0;
		}
	}
	return 1;
}
