/*
 * Copyright (C) CNRS, INRIA, Université Bordeaux 1, Télécom SudParis
 * See COPYING in top-level directory.
 */

#include <stdio.h>
#include <GTG.h>
#include "eztrace_convert.h"


#include "main_ev_codes.h"

int eztrace_convert_main_init();

int handle_main_events(eztrace_event_t *ev);
int handle_main_stats(eztrace_event_t *ev);
void print_main_stats();


void handleEZTRACE_bin_function_1() ;
void handleEZTRACE_bin_function_2() ;

void handleEZTRACE_lib_function_1() ;
void handleEZTRACE_lib_function_2() ;


/* thread-specific structure */
struct main_thread_info_t {
  struct thread_info_t *p_thread;

  /* TO COMPLETE: You can add per-thread counters here */
};

static struct main_thread_info_t *__register_thread_hook(
    struct thread_info_t *p_thread) {
  struct main_thread_info_t *ptr = (struct main_thread_info_t*) malloc(
      sizeof(struct main_thread_info_t));

  ptr->p_thread = p_thread;

  /* TO COMPLETE: If you added per-thread counters, initialize them here*/

  /* add the hook in the thread info structure */
  ezt_hook_list_add(&ptr->p_thread->hooks, ptr,
                   (uint8_t) main_EVENTS_ID);
  return ptr;
}

#define  INIT_main_THREAD_INFO(p_thread, var)                      \
  struct main_thread_info_t *var = (struct main_thread_info_t*)        \
    ezt_hook_list_retrieve_data(&p_thread->hooks, (uint8_t)main_EVENTS_ID); \
  if(!(var)) {                                                         \
    var = __register_thread_hook(p_thread);                            \
  }


/* Constructor of the plugin.
 * This function registers the current module to eztrace_convert
 */
struct eztrace_convert_module main_module;
void libinit(void) __attribute__ ((constructor));
void libinit(void)
{
  main_module.api_version = EZTRACE_API_VERSION;

  /* Specify the initialization function.
   * This function will be called once all the plugins are loaded
   * and the trace is started.
   * This function usually declared StateTypes, LinkTypes, etc.
   */
  main_module.init = eztrace_convert_main_init;

  /* Specify the function to call for handling an event
   */
  main_module.handle = handle_main_events;

  /* Specify the function to call for handling an event when
   * eztrace_stats is called
   */
  main_module.handle_stats = handle_main_stats;

  /* Specify the function to call for printinf statistics
   */
  main_module.print_stats = print_main_stats;

  /* Specify the module prefix */
  main_module.module_prefix = main_EVENTS_ID;

  asprintf(&main_module.name, "main");
  asprintf(&main_module.description, "Module for the main program");

  main_module.token.data = &main_module;

  /* Register the module to eztrace_convert */
  eztrace_convert_register_module(&main_module);

  //printf("module main loaded\n");
}

void libfinalize(void) __attribute__ ((destructor));
void libfinalize(void)
{
}



/*
 * This function will be called once all the plugins are loaded
 * and the trace is started.
 * This function usually declared StateTypes, LinkTypes, etc.
 */
int
eztrace_convert_main_init()
{
    if(get_mode() == EZTRACE_CONVERT) {
	addEntityValue("main_STATE_0", "ST_Thread", "Doing function lib_function", GTG_DARKGREY);
    }
    if(get_mode() == EZTRACE_CONVERT) {
	addEntityValue("main_STATE_1", "ST_Thread", "Doing function bin_function", GTG_DARKGREY);
    }
}


/* This function is called by eztrace_convert for each event to
 * handle.
 * It shall return 1 if the event was handled successfully or
 * 0 otherwise.
 */
int
handle_main_events(eztrace_event_t *ev)
{

  if(! CUR_TRACE->start)
    return 0;

  switch (LITL_READ_GET_CODE(ev)) {
  case EZTRACE_bin_function_1:
    handleEZTRACE_bin_function_1();
    break;
  case EZTRACE_bin_function_2:
    handleEZTRACE_bin_function_2();
    break;

  case EZTRACE_lib_function_1:
    handleEZTRACE_lib_function_1();
    break;
  case EZTRACE_lib_function_2:
    handleEZTRACE_lib_function_2();
    break;

  default:
    /* The event was not handled */
    return 0;
  }
  return 1;
}

/* This function is called by eztrace_stats for each event to
 * handle.
 * It shall return 1 if the event was handled successfully or
 * 0 otherwise.
 */
int
handle_main_stats(eztrace_event_t *ev)
{
  /* By default, use the same function as for eztrace_convert */
  return handle_main_events(ev);
}


void
print_main_stats()
{
  printf("\nmain:\n");
  printf("-------\n");

  int i;
  /* Browse the list of processes */
  for (i = 0; i < NB_TRACES; i++) {
    struct eztrace_container_t *p_process = GET_PROCESS_CONTAINER(i);
    int j;
    /* For each process, browse the list of threads */
    for(j=0; j<p_process->nb_children; j++) {
      struct eztrace_container_t *thread_container = p_process->children[j];
      struct thread_info_t *p_thread = (struct thread_info_t*) thread_container->container_info;
      if(!p_thread)
       continue;
      INIT_main_THREAD_INFO(p_thread, ptr);
      printf("\tThread %s\n", thread_container->name);

      /* TO COMPLETE: you can print per-thread counters here */
    }
  }

}

void handleEZTRACE_bin_function_1() {
    FUNC_NAME;
    DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
    DECLARE_CUR_THREAD(p_thread);
    INIT_main_THREAD_INFO(p_thread, ptr);
    CHANGE() pushState (CURRENT, "ST_Thread", thread_id, "main_STATE_1");
}
void handleEZTRACE_bin_function_2() {
    FUNC_NAME;
    DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
    DECLARE_CUR_THREAD(p_thread);
    INIT_main_THREAD_INFO(p_thread, ptr);
    CHANGE() popState (CURRENT, "ST_Thread", thread_id);
}



void handleEZTRACE_lib_function_1() {
    FUNC_NAME;
    DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
    DECLARE_CUR_THREAD(p_thread);
    INIT_main_THREAD_INFO(p_thread, ptr);
    CHANGE() pushState (CURRENT, "ST_Thread", thread_id, "main_STATE_0");
}
void handleEZTRACE_lib_function_2() {
    FUNC_NAME;
    DECLARE_THREAD_ID_STR(thread_id, CUR_INDEX, CUR_THREAD_ID);
    DECLARE_CUR_THREAD(p_thread);
    INIT_main_THREAD_INFO(p_thread, ptr);
    CHANGE() popState (CURRENT, "ST_Thread", thread_id);
}

