/* Copyright (C) 2021-2024 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.  */

// java.util.HashMap

#ifndef _DBE_HASHMAP_H
#define _DBE_HASHMAP_H

#include "vec.h"
#include "util.h"
#include "StringBuilder.h"
#include "Histable.h"
#include "MemObject.h"

template <typename Key_t> inline int get_hash_code (Key_t a);
template <typename Key_t> inline bool is_equals (Key_t a, Key_t b);
template <typename Key_t> inline Key_t copy_key (Key_t a);
template <typename Key_t> inline void delete_key (Key_t a);

// Specialization for char*
template<> inline int
get_hash_code (char *a)
{
  return (int) (crc64 (a, strlen (a)) & 0x7fffffff);
}

template<> inline bool
is_equals (char *a, char *b)
{
  return dbe_strcmp (a, b) == 0;
}

template<> inline char *
copy_key (char *a)
{
  return dbe_strdup (a);
}

template<> inline void
delete_key (char *a)
{
  return free (a);
}

template<> inline int
get_hash_code (uint64_t a)
{
  return (int) (a & 0x7fffffff);
}

template<> inline bool
is_equals (uint64_t a, uint64_t b)
{
  return a == b;
}

template<> inline uint64_t
copy_key (uint64_t a)
{
  return a;
}

template<> inline void
delete_key (uint64_t a)
{
  a = a;
}

template<> inline int
get_hash_code (Histable* a)
{
  return (int) (a->id & 0x7fffffff);
}

template<> inline bool
is_equals (Histable* a, Histable* b)
{
  return a == b;
}

template<> inline Histable*
copy_key (Histable* a)
{
  return a;
}

template<> inline void
delete_key (Histable* a)
{
  a->id = a->id;
}

template<> inline int
get_hash_code (MemObj* a)
{
  return (int) (a->id & 0x7fffffff);
}

template<> inline bool
is_equals (MemObj* a, MemObj* b)
{
  return a == b;
}

template<> inline MemObj*
copy_key (MemObj* a)
{
  return a;
}

template<> inline void
delete_key (MemObj* a)
{
  a->id = a->id;
}

template <typename Key_t, typename Value_t>
class HashMap
{
public:
  HashMap (int initialCapacity = 0);

  ~HashMap ()
  {
    clear ();
    delete vals;
    delete[] hashTable;
  }

  Value_t put (Key_t key, Value_t val);
  Value_t get (Key_t key);
  Value_t get (Key_t key, Value_t val); // Create a new map if key is not here
  void clear ();
  Value_t remove (Key_t);
  Vector<Value_t> *values (Key_t key);  // Return a list of values for 'key'

  bool
  containsKey (Key_t key)
  {
    Value_t p = get (key);
    return p != NULL;
  };

  Vector<Value_t> *
  values ()
  {
    return vals;
  }

  void
  reset ()
  {
    clear ();
  }

  int
  get_phaseIdx ()
  {
    return phaseIdx;
  }

  void
  set_phaseIdx (int phase)
  {
    if (phase == 0) clear ();
    phaseIdx = phase;
  }
  char *dump ();

private:

  enum
  {
    HASH_SIZE       = 511,
    MAX_HASH_SIZE   = 1048575
  };

  typedef struct Hash
  {
    Key_t key;
    Value_t val;
    struct Hash *next;
  } Hash_t;

  void resize ();

  int
  hashCode (Key_t key)
  {
    return get_hash_code (key) % hash_sz;
  }

  bool
  equals (Key_t a, Key_t b)
  {
    return is_equals (a, b);
  }

  Key_t
  copy (Key_t key)
  {
    return copy_key (key);
  }

  Hash_t **hashTable;
  Vector<Value_t> *vals;
  int phaseIdx;
  int hash_sz;
  int nelem;
};

template <typename Key_t, typename Value_t>
HashMap<Key_t, Value_t>
::HashMap (int initialCapacity)
{
  if (initialCapacity > 0)
    vals = new Vector<Value_t>(initialCapacity);
  else
    vals = new Vector<Value_t>();
  phaseIdx = 0;
  nelem = 0;
  hash_sz = HASH_SIZE;
  hashTable = new Hash_t*[hash_sz];
  for (int i = 0; i < hash_sz; i++)
    hashTable[i] = NULL;
}

template <typename Key_t, typename Value_t>
void
HashMap<Key_t, Value_t>::clear ()
{
  vals->reset ();
  phaseIdx = 0;
  nelem = 0;
  for (int i = 0; i < hash_sz; i++)
    {
      Hash_t *next;
      for (Hash_t *p = hashTable[i]; p; p = next)
	{
	  next = p->next;
	  delete_key (p->key);
	  delete p;
	}
      hashTable[i] = NULL;
    }
}

template <typename Key_t, typename Value_t>
void
HashMap<Key_t, Value_t>::resize ()
{
  int old_hash_sz = hash_sz;
  hash_sz = old_hash_sz * 2 + 1;
  Hash_t **old_hash_table = hashTable;
  hashTable = new Hash_t*[hash_sz];
  for (int i = 0; i < hash_sz; i++)
    hashTable[i] = NULL;
  nelem = 0;
  for (int i = 0; i < old_hash_sz; i++)
    {
      if (old_hash_table[i] != NULL)
	{
	  Hash_t *old_p;
	  Hash_t *p = old_hash_table[i];
	  while (p != NULL)
	    {
	      put (p->key, p->val);
	      old_p = p;
	      p = p->next;
	      delete old_p;
	    }
	}
    }
  delete[] old_hash_table;
}

template <typename Key_t, typename Value_t>
Value_t
HashMap<Key_t, Value_t>::get (Key_t key)
{
  int hash_code = hashCode (key);
  for (Hash_t *p = hashTable[hash_code]; p; p = p->next)
    if (equals (key, p->key))
      return p->val;
  return NULL;
}

template <typename Key_t, typename Value_t>
Vector<Value_t> *
HashMap<Key_t, Value_t>::values (Key_t key)
{
  Vector<Value_t> *list = new Vector<Value_t>();
  int hash_code = hashCode (key);
  for (Hash_t *p = hashTable[hash_code]; p; p = p->next)
    {
      if (equals (key, p->key))
	list->append (p->val);
    }
  return list;
}

template <typename Key_t, typename Value_t>
Value_t
HashMap<Key_t, Value_t>::get (const Key_t key, Value_t val)
{
  int hash_code = hashCode (key);
  Hash_t *p, *first = NULL;
  for (p = hashTable[hash_code]; p; p = p->next)
    {
      if (equals (key, p->key))
	{
	  if (first == NULL)
	    first = p;
	  if (val == p->val)
	    return first->val; // Always return the first value
	}
    }
  vals->append (val);
  p = new Hash_t ();
  p->val = val;
  p->key = copy (key);
  if (first)
    {
      p->next = first->next;
      first->next = p;
      return first->val; // Always return the first value
    }
  else
    {
      p->next = hashTable[hash_code];
      hashTable[hash_code] = p;
      return val;
    }
}

template <typename Key_t, typename Value_t>
Value_t
HashMap<Key_t, Value_t>::remove (Key_t key)
{
  int hash_code = hashCode (key);
  Value_t val = NULL;
  for (Hash_t *prev = NULL, *p = hashTable[hash_code]; p != NULL;)
    {
      if (equals (key, p->key))
	{
	  if (prev == NULL)
	    hashTable[hash_code] = p->next;
	  else
	    prev->next = p->next;
	  if (val == NULL)
	    val = p->val;
	  delete_key (p->key);
	  delete p;
	}
      else
	{
	  prev = p;
	  p = p->next;
	}
    }
  return val;
}

template <typename Key_t, typename Value_t>
Value_t
HashMap<Key_t, Value_t>::put (Key_t key, Value_t val)
{
  int hash_code = hashCode (key);
  vals->append (val);
  for (Hash_t *p = hashTable[hash_code]; p != NULL; p = p->next)
    {
      if (equals (key, p->key))
	{
	  Value_t v = p->val;
	  p->val = val;
	  return v;
	}
    }
  Hash_t *p = new Hash_t ();
  p->val = val;
  p->key = copy (key);
  p->next = hashTable[hash_code];
  hashTable[hash_code] = p;
  nelem++;
  if (nelem == hash_sz)
    resize ();
  return val;
}

template <typename Key_t, typename Value_t>
char *
HashMap<Key_t, Value_t>::dump ()
{
  StringBuilder sb;
  char buf[128];
  snprintf (buf, sizeof (buf), "HashMap: size=%d ##########\n", vals->size ());
  sb.append (buf);
  for (int i = 0; i < hash_sz; i++)
    {
      if (hashTable[i] == NULL)
	continue;
      snprintf (buf, sizeof (buf), "%3d:", i);
      sb.append (buf);
      char *s = NTXT (" ");
      for (Hash_t *p = hashTable[i]; p; p = p->next)
	{
	  sb.append (s);
	  s = NTXT ("     ");
	  sb.append (p->key);
	  snprintf (buf, sizeof (buf), " --> 0x%p '%s'\n",
		    p->val, p->val->get_name ());
	  sb.append (buf);
	}
    }
  return sb.toString ();
}

#endif // _DBE_HASHMAP_H