/* Copyright (C) 2021 Free Software Foundation, Inc.
   Contributed by Oracle.

   This file is part of GNU Binutils.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, 51 Franklin Street - Fifth Floor, Boston,
   MA 02110-1301, USA.  */

#ifndef _PATH_TREE_H
#define _PATH_TREE_H

#include <vec.h>
#include <Map.h>

#include "dbe_structs.h"
#include "Hist_data.h"
#include "Histable.h"
#include "Metric.h"

typedef enum
{
  NORMAL = 0, CANCELED
} PtreePhaseStatus;

class PathTree
{
public:

  PathTree (DbeView *_dbev, int _indxtype = -1)
  {
    construct (_dbev, _indxtype, PATHTREE_MAIN);
  }

  ~PathTree ();

  static void make_deltas (int vtype, TValue *v1, TValue *v2);
  static void make_ratios (int vtype, TValue *v1, TValue *v2);

  typedef enum
  {
    COMPUTEOPT_NONE = 0,
    COMPUTEOPT_OMP_CALLEE
  } PtreeComputeOption;

  Hist_data *compute_metrics (MetricList *, Histable::Type,
			      Hist_data::Mode, Vector<Histable*>*,
			      Histable*, Vector<Histable*>* sel_objs = NULL,
			      PtreeComputeOption flag = COMPUTEOPT_NONE);
  // Get aggregated callstack data
  CStack_data *get_cstack_data (MetricList *);

  Vector<Histable*> *get_clr_instr (Histable *);
  Vector<void*> *get_cle_instr (Histable *, Vector<Histable*>*&);

  int
  get_status ()
  {
    return status;
  }

  int
  get_depth ()
  {
    return depth;
  }

  int
  getStackProp ()
  {
    return stack_prop;
  }

  typedef long NodeIdx;

  struct Node
  {
    inline void
    reset ()
    {
      ancestor = 0;
      descendants = NULL;
      instr = NULL;
      funclist = 0;
    }

    NodeIdx ancestor;
    Vector<NodeIdx> *descendants;
    Histable *instr;
    NodeIdx funclist;
  };

  static const int CHUNKSZ = 16384;

  inline Node *
  NODE_IDX (NodeIdx idx)
  {
    return idx ? &chunks[idx / CHUNKSZ][idx % CHUNKSZ] : NULL;
  }

  // queue for messages (statistics for pathtree processing)
  Emsg *fetch_stats (void);     // fetch the queue of comment messages
  void delete_stats (void);     // delete the queue of stats messages
  Emsg *fetch_warnings (void);  // fetch the queue of warnings messages
  void delete_warnings (void);  // delete the queue of warnings messages

  NodeIdx
  get_func_nodeidx (Function * func)
  {
    return fn_map == NULL ? (NodeIdx) 0 : fn_map->get (func);
  }

  void print (FILE *);
  void dumpNodes (FILE *, Histable *);

  // flame charts functions - get values from ftree_internal
  int get_ftree_depth ();   // Depth of tree
  Vector<void*>* get_ftree_level (BaseMetric *bm, int dpth);
  Vector<void*>* get_ftree_node_children (BaseMetric *bm, NodeIdx node_idx);
  Vector<Function*>* get_ftree_funcs ();
  Vector<Function*>* get_funcs ();      // Unique functions in tree

private:

  enum
  {
    MAX_DESC_HTABLE_SZ = 65535
  };

  typedef struct hash_node
  {
    NodeIdx nd;
    struct hash_node *next;
  } hash_node_t;

  int desc_htable_size;
  int desc_htable_nelem;
  hash_node_t **descHT;

  struct Slot
  {
    int id;
    ValueTag vtype;
    union
    {
      int **mvals;
      int64_t **mvals64;
    };
  };

  typedef enum
  {
    PATHTREE_MAIN = 0,
    PATHTREE_INTERNAL_OMP,
    PATHTREE_INTERNAL_FUNCTREE
  } PathTreeType;

  DbeView *dbev;
  int indxtype;
  int stack_prop;
  Expression *indx_expr;
  Histable *total_obj;
  Map<Function*, NodeIdx> *fn_map;
  Map<uint64_t, NodeIdx> *pathMap;
  Map<uint64_t, uint64_t> *hideMap;
  int status;
  NodeIdx root_idx;
  Node *root;
  int depth;
  long nodes;
  long dnodes;
  long nchunks;
  Node **chunks;
  int nslots;
  Slot *slots;
  int phaseIdx;
  int nexps;
  Emsgqueue *statsq;
  Emsgqueue *warningq;
  Hist_data *hist_data;
  int percent;
  int ndone;
  Histable **obj_list;
  Node **node_list;
  int *xlate;
  bool cancel_ok;
  PathTreeType pathTreeType;
  PathTree *ptree_internal;
  PathTree *ftree_internal;             // function-based pathtree
  bool ftree_needs_update;
  Vector<Vector<NodeIdx>*> *depth_map; // for each depth level, list of nodes

  void init ();
  void fini ();
  PtreePhaseStatus reset ();
  PtreePhaseStatus add_experiment (int);
  PtreePhaseStatus process_packets (Experiment*, DataView*, int);
  DataView *get_filtered_events (int exp_index, int data_type);
  void construct (DbeView *_dbev, int _indxtype, PathTreeType _pathTreeType);

  PathTree (DbeView *_dbev, int _indxtype, PathTreeType _pathTreeType)
  {
    construct (_dbev, _indxtype, _pathTreeType);
  }

  inline int *
  allocate_chunk (int **p, NodeIdx idx)
  {
    int *res = new int[CHUNKSZ];
    for (int i = 0; i < CHUNKSZ; i++)
      res[i] = 0;
    p[idx] = res;
    return res;
  };

  inline int64_t *
  allocate_chunk (int64_t **p, NodeIdx idx)
  {
    int64_t *res = new int64_t[CHUNKSZ];
    for (int i = 0; i < CHUNKSZ; i++)
      res[i] = 0;
    p[idx] = res;
    return res;
  };

  inline Node *
  allocate_chunk (Node **p, NodeIdx idx)
  {
    Node *res = new Node[CHUNKSZ];
    for (int i = 0; i < CHUNKSZ; i++)
      res[i].reset ();
    p[idx] = res;
    return res;
  };

  inline bool
  IS_MVAL_ZERO (Slot& slot, NodeIdx idx)
  {
    if (slot.vtype == VT_LLONG || slot.vtype == VT_ULLONG)
      {
	int64_t *tmp = slot.mvals64[idx / CHUNKSZ];
	return tmp ? tmp[idx % CHUNKSZ] == 0 : true;
      }
    else
      {
	int *tmp = slot.mvals[idx / CHUNKSZ];
	return tmp ? tmp[idx % CHUNKSZ] == 0 : true;
      }
  }

  inline void
  ASN_METRIC_VAL (TValue& v, Slot& slot, NodeIdx idx)
  {
    if (slot.vtype == VT_LLONG)
      {
	int64_t *tmp = slot.mvals64[idx / CHUNKSZ];
	if (tmp)
	  v.ll = tmp[idx % CHUNKSZ];
      }
    else if (slot.vtype == VT_ULLONG)
      {
	uint64_t *tmp = (uint64_t *) slot.mvals64[idx / CHUNKSZ];
	if (tmp)
	  v.ull = tmp[idx % CHUNKSZ];
      }
    else
      {
	int *tmp = slot.mvals[idx / CHUNKSZ];
	if (tmp)
	  v.i = tmp[idx % CHUNKSZ];
      }
  }

  inline void
  ADD_METRIC_VAL (TValue& v, Slot& slot, NodeIdx idx)
  {
    if (slot.vtype == VT_LLONG)
      {
	int64_t *tmp = slot.mvals64[idx / CHUNKSZ];
	if (tmp)
	  v.ll += tmp[idx % CHUNKSZ];
      }
    else if (slot.vtype == VT_ULLONG)
      {
	uint64_t *tmp = (uint64_t *) slot.mvals64[idx / CHUNKSZ];
	if (tmp)
	  v.ull += tmp[idx % CHUNKSZ];
      }
    else
      {
	int *tmp = slot.mvals[idx / CHUNKSZ];
	if (tmp) v.i += tmp[idx % CHUNKSZ];
      }
  }

  inline void
  SUB_METRIC_VAL (TValue& v, Slot& slot, NodeIdx idx)
  {
    if (slot.vtype == VT_LLONG)
      {
	int64_t *tmp = slot.mvals64[idx / CHUNKSZ];
	if (tmp)
	  v.ll -= tmp[idx % CHUNKSZ];
      }
    else if (slot.vtype == VT_ULLONG)
      {
	uint64_t *tmp = (uint64_t *) slot.mvals64[idx / CHUNKSZ];
	if (tmp)
	  v.ull -= tmp[idx % CHUNKSZ];
      }
    else
      {
	int *tmp = slot.mvals[idx / CHUNKSZ];
	if (tmp)
	  v.i -= tmp[idx % CHUNKSZ];
      }
  }

  inline void
  INCREMENT_METRIC (Slot *slot, NodeIdx idx, int64_t val)
  {
    if (slot->vtype == VT_LLONG)
      {
	int64_t *tmp = slot->mvals64[idx / CHUNKSZ];
	if (tmp == NULL)
	  tmp = allocate_chunk (slot->mvals64, idx / CHUNKSZ);
	tmp[idx % CHUNKSZ] += val;
      }
    else if (slot->vtype == VT_ULLONG)
      {
	uint64_t *tmp = (uint64_t *) slot->mvals64[idx / CHUNKSZ];
	if (tmp == NULL)
	  tmp = (uint64_t *) allocate_chunk (slot->mvals64, idx / CHUNKSZ);
	tmp[idx % CHUNKSZ] += val;
      }
    else
      {
	int *tmp = slot->mvals[idx / CHUNKSZ];
	if (tmp == NULL)
	  tmp = allocate_chunk (slot->mvals, idx / CHUNKSZ);
	tmp[idx % CHUNKSZ] += (int) val;
      }
  }

  inline Slot *
  SLOT_IDX (int idx)
  {
    if (idx < 0 || idx >= nslots)
      return NULL;
    return &slots[idx];
  }

  int allocate_slot (int id, ValueTag vtype);
  void allocate_slots (Slot *slots, int nslots);
  int find_slot (int);
  NodeIdx new_Node (NodeIdx, Histable*, bool);
  NodeIdx find_path (Experiment*, DataView*, long);
  NodeIdx find_desc_node (NodeIdx, Histable*, bool);
  NodeIdx find_in_desc_htable (NodeIdx, Histable*, bool);
  Histable *get_hist_obj (Node *, Histable* = NULL);
  Histable *get_hist_func_obj (Node *);
  Histable *get_compare_obj (Histable *obj);
  void get_metrics (NodeIdx, int);
  void get_metrics (Vector<Function*> *, Histable *);
  void get_clr_metrics (Vector<Histable*>*, NodeIdx, int, int);
  void get_clr_metrics (Vector<Histable*>*);
  void get_cle_metrics (Vector<Histable*>*, NodeIdx, int, int, int);
  void get_cle_metrics (Vector<Histable*>*, NodeIdx, int);
  void get_cle_metrics (Vector<Histable*>*);
  void get_self_metrics (Vector<Histable*>*, NodeIdx, bool, int);
  void get_self_metrics (Vector<Histable*>*);
  void get_self_metrics (Histable *, Vector<Function*> *funclist,
			 Vector<Histable*>* sel_objs = NULL);
  void get_cstack_list (CStack_data *, NodeIdx, int);

  // Generate PathTree based on Functions instead of Instructions // Used for flame chart
  void ftree_reset ();
  void ftree_build (PathTree *mstr);
  void ftree_build (PathTree *mstr, NodeIdx mstr_node_idx, NodeIdx local_node_idx);
  void depth_map_build ();
  void depth_map_build (NodeIdx node_idx, int depth);
  Vector<void*>* get_level (BaseMetric *bm, int dpth);
  Vector<void*>* get_nodes (BaseMetric *bm, Vector<NodeIdx> *node_idxs);
  Vector<void*>* get_node_children (BaseMetric *bm, NodeIdx node_idx);
  bool ftree_debug_match_hist_data (Hist_data *data, Hist_data *data_tmp);
  void ftree_dump ();

  // Debugging functions
  void print (FILE *, PathTree::Node*, int);
  void printn (FILE *);
  int dbg_nodes (PathTree::Node*);
};

#endif /* _PATH_TREE_H */
