/* -*- c-file-style: "GNU" -*- */
/*
 * Copyright (C) CNRS, INRIA, Universite Bordeaux 1, Telecom SudParis
 * See COPYING in top-level directory.
 */

#include <stdio.h>

#include "ev_codes.h"
#include "eztrace_list.h"
#include "eztrace_convert.h"
#include "eztrace_convert_core.h"
#include "eztrace_hook.h"

#include "GTG.h"

/* name of the output file */
static char *output_filename = "eztrace_output";
static struct eztrace_archive_info *arch_info = NULL, *arch_info_head = NULL;

static void usage(int argc __attribute__((unused)), char **argv) {
  fprintf(
      stderr,
      "Usage : %s [-v] [-t PAJE|OTF] [-z] [-o output_filename] input_filename input_filename ... \n",
      argv[0]);
}

int compress = 0;

static void parse_args(int argc, char **argv) {
  int i;
  litl_size_t j;
  litl_read_trace_t* arch;
  struct eztrace_archive_info *tmp;

  for (i = 1; i < argc; i++) {
    if (strcmp(argv[i], "-o") == 0)
      output_filename = argv[++i];
    else if (strcmp(argv[i], "-v") == 0)
      VERBOSE = 1;
    else if (strcmp(argv[i], "-z") == 0)
      compress = 1;
    else if (strcmp(argv[i], "-t") == 0) {
      char* trace_type = argv[++i];
      if (strcmp(trace_type, "PAJE") == 0) {
	printf("Setting trace type to PAJE\n");
	setTraceType(PAJE);
      } else if (strcmp(trace_type, "OTF") == 0) {
	printf("Setting trace type to OTF\n");
	setTraceType(OTF);
      } else {
	fprintf(stderr, "Unknown trace type: '%s'\n", trace_type);
	exit(-1);
      }
    } else if (strcmp(argv[i], "-h") == 0) {
      usage(argc, argv);
      exit(-1);
    } else if (argv[i][0] == '-') {
      fprintf(stderr, "Unknown option %s\n", argv[i]);
      usage(argc, argv);
      exit(-1);
    } else {
      // count the number of traces including those in archives

      // open the trace file. Do this here to deal with archives of traces
      arch = litl_read_open_trace(argv[i]);
      if (!arch) {
	perror("litl_read_open_trace:");
	exit(-1);
      }

      tmp = (struct eztrace_archive_info *) malloc(sizeof(struct eztrace_archive_info));
      tmp->block = arch;
      tmp->filename = argv[i];
      tmp->next = NULL;

      if (arch_info == NULL ) {
	arch_info = tmp;
	arch_info_head = arch_info;
      } else {
	arch_info->next = tmp;
	arch_info = arch_info->next;
      }

      NB_TRACES += tmp->block->nb_processes;
    }
  }

  if (NB_TRACES < 1) {
    usage(argc, argv);
    exit(-1);
  }

  // create and initialize traces

  j = 0;
  arch_info = arch_info_head;
  eztrace_convert_init(NB_TRACES);
  while (arch_info != NULL ) {
    tmp = arch_info;
    arch_info = arch_info->next;
    arch = tmp->block;
    // init traces
    for (i = 0; i < tmp->block->nb_processes; i++) {
      get_traces(i + j)->input_filename = tmp->filename;
      get_traces(i + j)->block = tmp->block;
      get_traces(i + j)->trace_index = i;
    }
    litl_read_init_processes(arch);
    j += tmp->block->nb_processes;
  }
}

static void __clean_up() {
  while (arch_info_head != NULL) {
    arch_info = arch_info_head;
    arch_info_head = arch_info_head->next;
    free(arch_info);
  }
}

/*
 * This program should be used to parse the log file generated by LiTL
 */
int main(int argc, char **argv) {
  int ret;

  load_modules(1);

  setTraceType(PAJE);

  /* parse the arguments passed to this program */
  parse_args(argc, argv);

#ifdef GTG_OUT_OF_ORDER
  ret = initTrace(output_filename, 0, GTG_FLAG_OUTOFORDER);
#else
  ret = initTrace(output_filename, 0, GTG_FLAG_NONE);
#endif
  if (ret != TRACE_SUCCESS) {
    fprintf(stderr, "fail to initialize GTG\n");
    return 1;
  }

  if (compress)
    if (setCompress(9) != TRACE_SUCCESS)
      fprintf(stderr, "Fail to enable trace compression\n");

  eztrace_initialize_gtg();

  __init_modules();

  int i;
  /* initialize the traces array */
  for (i = 0; i < NB_TRACES; i++) {
    /* open the trace file */

    struct trace_t* cur_trace = get_traces(i);

    cur_trace->delay = 0;
    cur_trace->rank = i;
    cur_trace->id = i;
    cur_trace->done = 0;
    cur_trace->skip = 0;
    cur_trace->line_number = 0;

    eztrace_create_containers(i);

    /* if several traces are loaded, this means that MPI was used,
     * so let's skip all the first events until MPI_Init is detected
     */
    if (NB_TRACES > 1) {
      cur_trace->start = 0;
      cur_trace->trace_id = NULL;
    } else {
      CREATE_TRACE_ID_STR(cur_trace->trace_id, 0);
      cur_trace->start = 1;
      NB_START = 1;
      addContainer(0.00000, cur_trace->trace_id, "CT_Process", "C_Prog",
		   cur_trace->trace_id, "0");
      eztrace_create_ids(cur_trace->rank);
    }

    ezt_litl_read_next_event(cur_trace);
    if (!cur_trace->ev.event) {
      fprintf(stderr, "no more block ...\n");
      break;
    }

    cur_trace->start_time = LITL_READ_GET_TIME(&cur_trace->ev);
  }

  set_cur_trace(get_traces(0));
  set_cur_ev(&get_traces(0)->ev);

  struct eztrace_event_handler* h_info = NULL;
  h_info = get_handler_info();

  h_info->cur_trace_nb = 0;
  h_info->nb_done = 0;
  h_info->nb_handled = 0;
  sem_init(&h_info->events_processed, 0, 0);

  /* create the handler thread and wait until it completes */
  create_main_thread();
  wake_up_handler_thread();
  sem_wait(&h_info->events_processed);

  /* finalize the trace and close the file */
  endTrace();
  eztrace_convert_finalize();
  printf("%d events handled\n", h_info->nb_handled);

  __clean_up();

  return 0;
}
