/**
 * VampirTrace
 * http://www.tu-dresden.de/zih/vampirtrace
 *
 * Copyright (c) 2005-2008, ZIH, TU Dresden, Federal Republic of Germany
 *
 * Copyright (c) 1998-2005, Forschungszentrum Juelich, Juelich Supercomputing
 *                          Centre, Federal Republic of Germany
 *
 * See the file COPYING in the package base directory for details
 **/

#include <stdlib.h>
#include <string.h>
#include "vt_comp.h"
#include "vt_memhook.h"
#include "vt_pform.h"
#include "vt_trc.h"
#if (defined (VT_OMPI) || defined (VT_OMP))
#  include <omp.h>
#endif

/*
 *-----------------------------------------------------------------------------
 * Simple hash table to map function names to region identifier
 *-----------------------------------------------------------------------------
 */

typedef struct HN {
  long id;            /* hash code (address of function name) */
  uint32_t vtid;      /* associated region identifier  */
  struct HN* next;
} HashNode;

#define HASH_MAX 1021

static int phat_init = 1;       /* is initialization needed? */

static HashNode* htab[HASH_MAX];

/*
 * Stores region identifier `e' under hash code `h'
 */

static void hash_put(long h, uint32_t e) {
  long id = h % HASH_MAX;
  HashNode *add = (HashNode*)malloc(sizeof(HashNode));
  add->id = h;
  add->vtid = e;
  add->next = htab[id];
  htab[id] = add;
}

/*
 * Lookup hash code `h'
 * Returns region identifier if already stored, otherwise VT_NO_ID
 */

static uint32_t hash_get(long h) {
  long id = h % HASH_MAX;
  HashNode *curr = htab[id];
  while ( curr ) {
    if ( curr->id == h ) {
      return curr->vtid;
    }
    curr = curr->next;
  }
  return VT_NO_ID;
}

/*
 * Register new region
 * `str' is passed in from SUN compiler
 */

static uint32_t register_region(char *str) {
  uint32_t rid;

  /* -- register region and store region identifier -- */
  rid = vt_def_region(str, VT_NO_ID, VT_NO_LNO, VT_NO_LNO, VT_DEF_GROUP, VT_FUNCTION);
  hash_put((long) str, rid);
  return rid;
}

/*
 * This function is called at the entry of each function
 * The call is generated by the SUN f90 compilers
 */

void phat_enter(char *str, int *id) {
  uint64_t time;

  /* -- if not yet initialized, initialize VampirTrace -- */
  if ( phat_init ) {
    uint32_t main_id;
    VT_MEMHOOKS_OFF();
    phat_init = 0;
    vt_open();

    main_id = register_region("main");
    time = vt_pform_wtime();
    vt_enter(&time, main_id);
    VT_MEMHOOKS_ON();
  }

  /* -- if VampirTrace already finalized, return -- */
  if ( !vt_is_alive ) return;

  /* -- ignore SUN OMP runtime functions -- */
  if ( strchr(str, '$') != NULL ) return;

  VT_MEMHOOKS_OFF();

  time = vt_pform_wtime();

  /* -- get region identifier -- */
  if ( *id == -1 ) {
    /* -- region entered the first time, register region -- */
#   if defined (VT_OMPI) || defined (VT_OMP)
    if (omp_in_parallel()) {
#     pragma omp critical (vt_comp_phat_1)
      {
        if ( (*id = hash_get((long) str)) == VT_NO_ID ) {
          *id = register_region(str);
        }
      }
    } else {
      *id = register_region(str);
    }
#   else
    *id = register_region(str);
#   endif
  }

  /* -- write enter record -- */
  vt_enter(&time, *id);

  VT_MEMHOOKS_ON();
}


/*
 * This function is called at the exit of each function
 * The call is generated by the SUN F90 compilers
 */

void phat_exit(char *str, int *id) {
  uint64_t time;

  /* -- if VampirTrace already finalized, return -- */
  if ( !vt_is_alive ) return;

  if ( *id == -1 ) return;

  VT_MEMHOOKS_OFF();

  /* -- write exit record -- */
  time = vt_pform_wtime();
  vt_exit(&time);

  VT_MEMHOOKS_ON();
}
