// @(#)root/cont:$Id$
// Author: Fons Rademakers   12/11/95

/*************************************************************************
 * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TMap                                                                 //
//                                                                      //
// TMap implements an associative array of (key,value) pairs using a    //
// THashTable for efficient retrieval (therefore TMap does not conserve //
// the order of the entries). The hash value is calculated              //
// using the value returned by the keys Hash() function and the         //
// key comparison is done via the IsEqual() function.                   //
// Both key and value must inherit from TObject.                        //
//Begin_Html
/*
<img src=gif/tmap.gif>
*/
//End_Html
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TMap.h"
#include "THashTable.h"
#include "TROOT.h"
#include "TBrowser.h"
#include "TRegexp.h"

ClassImp(TMap)

//______________________________________________________________________________
TMap::TMap(Int_t capacity, Int_t rehashlevel)
{
   // TMap ctor. See THashTable for a description of the arguments.

   fSize  = 0;
   fTable = new THashTable(capacity, rehashlevel);
}

//______________________________________________________________________________
TMap::~TMap()
{
   // TMap dtor. Objects are not deleted unless the TMap is the
   // owner (set via SetOwner()).

   Clear();
   delete fTable;
}

//______________________________________________________________________________
void TMap::Add(TObject *)
{
   // This function may not be used (but we need to provide it since it is
   // a pure virtual in TCollection). Use Add(key,value) instead.

   MayNotUse("Add(TObject *obj)");
}

//______________________________________________________________________________
void TMap::Add(TObject *key, TObject *value)
{
   // Add a (key,value) pair to the map.

   if (IsArgNull("Add", key)) return;

   fTable->Add(new TPair(key, value));
   fSize++;
}

//______________________________________________________________________________
Float_t TMap::AverageCollisions() const
{
   // Return the ratio of entries vs occupied slots.

   return fTable->AverageCollisions();
}

//______________________________________________________________________________
Int_t TMap::Capacity() const
{
   // Return number of slots in the hashtable. Use GetSize() to get the
   // number of objects stored in the TMap.

   return fTable->Capacity();
}

//______________________________________________________________________________
void TMap::Clear(Option_t *option)
{
   // Remove all (key,value) pairs from the map. The keys/values are
   // deleted depending on the state of key-ownership (SetOwner()) and
   // value-ownership (SetOwnerValue()).
   //
   // To delete these objects regardless of the ownership state use:
   //  - Delete()       to delete only keys;
   //  - DeleteValues() to delete only values;
   //  - DeleteAll()    to delete both keys and values.

   if (IsOwner() && IsOwnerValue())
      DeleteAll();
   else if (IsOwner())
      Delete();
   else if (IsOwnerValue())
      DeleteValues();
   else {
      fTable->Delete(option);    // delete the TPair's
      fSize = 0;
   }
}

//______________________________________________________________________________
Int_t TMap::Collisions(const char *keyname) const
{
   // Returns the number of collisions for a key with a certain name
   // (i.e. number of objects in same slot in the hash table, i.e. length
   // of linked list).

   return fTable->Collisions(keyname);
}

//______________________________________________________________________________
Int_t TMap::Collisions(TObject *key) const
{
   // Returns the number of collisions for a key (i.e. number of objects
   // in same slot in the hash table, i.e. length of linked list).

   return fTable->Collisions(key);
}

//______________________________________________________________________________
void TMap::Delete(Option_t *option)
{
   // Remove all (key,value) pairs from the map AND delete the keys
   // when they are allocated on the heap.

   TIter next(fTable);
   TPair *a;

   while ((a = (TPair *)next()))
      if (a->Key() && a->Key()->IsOnHeap())
         TCollection::GarbageCollect(a->Key());

   fTable->Delete(option);   // delete the TPair's
   fSize = 0;
}

//______________________________________________________________________________
void TMap::DeleteValues()
{
   // Remove all (key,value) pairs from the map AND delete the values
   // when they are allocated on the heap.

   TIter next(fTable);
   TPair *a;

   while ((a = (TPair *)next()))
      if (a->Value() && a->Value()->IsOnHeap())
         TCollection::GarbageCollect(a->Value());

   fTable->Delete();   // delete the TPair's
   fSize = 0;
}

//______________________________________________________________________________
void TMap::DeleteAll()
{
   // Remove all (key,value) pairs from the map AND delete the keys AND
   // values when they are allocated on the heap.

   TIter next(fTable);
   TPair *a;

   while ((a = (TPair *)next())) {
      if (a->Key()   && a->Key()->IsOnHeap())
         TCollection::GarbageCollect(a->Key());
      if (a->Value() && a->Value()->IsOnHeap())
         TCollection::GarbageCollect(a->Value());
   }

   fTable->Delete();   // delete the TPair's
   fSize = 0;
}

//______________________________________________________________________________
Bool_t TMap::DeleteEntry(TObject *key)
{
   // Remove (key,value) pair with key from the map. Returns true
   // if the key was found and removed, false otherwise.
   // The key and value objects are deleted if map is the owner
   // of keys and values respectively.

   if (!key) return kFALSE;

   TPair *a;
   if ((a = (TPair *)fTable->FindObject(key))) {
      if (fTable->Remove(key)) {
         if (IsOwner() && a->Key() && a->Key()->IsOnHeap())
            TCollection::GarbageCollect(a->Key());
         if (IsOwnerValue() && a->Value() && a->Value()->IsOnHeap())
            TCollection::GarbageCollect(a->Value());
         delete a;
         fSize--;
         return kTRUE;
      }
   }
   return kFALSE;
}

//______________________________________________________________________________
TObject *TMap::FindObject(const char *keyname) const
{
   // Check if a (key,value) pair exists with keyname as name of the key.
   // Returns a TPair* (need to downcast from TObject). Use Key() and
   // Value() to get the pointers to the key and value, respectively.
   // Returns 0 if not found.

   return fTable->FindObject(keyname);
}

//______________________________________________________________________________
TObject *TMap::FindObject(const TObject *key) const
{
   // Check if a (key,value) pair exists with key as key.
   // Returns a TPair* (need to downcast from TObject). Use Key() and
   // Value() to get the pointers to the key and value, respectively.
   // Returns 0 if not found.

   if (IsArgNull("FindObject", key)) return 0;

   return fTable->FindObject(key);
}

//______________________________________________________________________________
TObject *TMap::GetValue(const char *keyname) const
{
   // Returns a pointer to the value associated with keyname as name of the key.

   TPair *a = (TPair *)fTable->FindObject(keyname);
   if (a) return a->Value();
   return 0;
}

//______________________________________________________________________________
TObject *TMap::GetValue(const TObject *key) const
{
   // Returns a pointer to the value associated with key.

   if (IsArgNull("GetValue", key)) return 0;

   TPair *a = (TPair *)fTable->FindObject(key);
   if (a) return a->Value();
   return 0;
}

//______________________________________________________________________________
TIterator *TMap::MakeIterator(Bool_t dir) const
{
   // Create an iterator for TMap.

   return new TMapIter(this, dir);
}

//______________________________________________________________________________
void TMap::PrintCollectionEntry(TObject* entry, Option_t* option, Int_t recurse) const
{
   // Print the collection entry.

   TObject* val = GetValue(entry);

   TROOT::IndentLevel();
   printf("Key:   ");
   entry->Print();
   TROOT::IndentLevel();
   printf("Value: ");
   TCollection* coll = dynamic_cast<TCollection*>(val);
   if (coll) {
     coll->Print(option, recurse);
   } else {
     val->Print(option);
   }
}

//______________________________________________________________________________
void TMap::Rehash(Int_t newCapacity, Bool_t checkObjValidity)
{
   // Rehash the underlaying THashTable (see THashTable::Rehash()).

   fTable->Rehash(newCapacity, checkObjValidity);
}

//______________________________________________________________________________
TObject *TMap::Remove(TObject *key)
{
   // Remove the (key,value) pair with key from the map. Returns the
   // key object or 0 in case key was not found. If map is the owner
   // of values, the value is deleted.

   if (!key) return 0;

   TPair *a;
   if ((a = (TPair *)fTable->FindObject(key))) {
      if (fTable->Remove(key)) {
         if (IsOwnerValue() && a->Value() && a->Value()->IsOnHeap())
            TCollection::GarbageCollect(a->Value());
         TObject *kobj = a->Key();
         delete a;
         fSize--;
         return kobj;
      }
   }
   return 0;
}

//______________________________________________________________________________
TPair *TMap::RemoveEntry(TObject *key)
{
   // Remove (key,value) pair with key from the map. Returns the
   // pair object or 0 in case the key was not found.
   // It is caller's responsibility to delete the pair and, eventually,
   // the key and value objects.

   if (!key) return 0;

   TPair *a;
   if ((a = (TPair *)fTable->FindObject(key))) {
      if (fTable->Remove(key)) {
         fSize--;
         return a;
      }
   }
   return 0;
}

//______________________________________________________________________________
void TMap::SetOwnerValue(Bool_t enable)
{
   // Set whether this map is the owner (enable==true)
   // of its values.  If it is the owner of its contents,
   // these objects will be deleted whenever the collection itself
   // is deleted. The objects might also be deleted or destructed when Clear
   // is called (depending on the collection).

   if (enable)
      SetBit(kIsOwnerValue);
   else
      ResetBit(kIsOwnerValue);
}

//______________________________________________________________________________
void TMap::SetOwnerKeyValue(Bool_t ownkeys, Bool_t ownvals)
{
   // Set ownership for keys and values.

   SetOwner(ownkeys);
   SetOwnerValue(ownvals);
}

//______________________________________________________________________________
void TMap::Streamer(TBuffer &b)
{
   // Stream all key/value pairs in the map to or from the I/O buffer.

   TObject *obj=0;
   UInt_t R__s, R__c;

   if (b.IsReading()) {
      Int_t    nobjects;
      TObject *value=0;

      Version_t v = b.ReadVersion(&R__s, &R__c);
      if (v > 2)
         TObject::Streamer(b);
      if (v > 1)
         fName.Streamer(b);
      b >> nobjects;
      for (Int_t i = 0; i < nobjects; i++) {
         b >> obj;
         b >> value;
         if (obj) Add(obj, value);
      }
      b.CheckByteCount(R__s, R__c,TMap::IsA());
   } else {
      R__c = b.WriteVersion(TMap::IsA(), kTRUE);
      TObject::Streamer(b);
      fName.Streamer(b);
      b << GetSize();
      TIter next(fTable);
      TPair *a;
      while ((a = (TPair*) next())) {
         b << a->Key();
         b << a->Value();
      }
      b.SetByteCount(R__c, kTRUE);
   }
}

//______________________________________________________________________________
Int_t TMap::Write(const char *name, Int_t option, Int_t bsize) const
{
   // Write all objects in this map. By default all objects in
   // the collection are written individually (each object gets its
   // own key). Note, this is recursive, i.e. objects in collections
   // in the collection are also written individually. To write all
   // objects using a single key specify a name and set option to
   // TObject::kSingleKey (i.e. 1).

   if ((option & kSingleKey)) {
      return TObject::Write(name, option, bsize);
   } else {
      option &= ~kSingleKey;
      Int_t nbytes = 0;
      TIter next(fTable);
      TPair *a;
      while ((a = (TPair*) next())) {
         if (a->Key())
            nbytes += a->Key()->Write(name, option, bsize);
         if (a->Value())
            nbytes += a->Value()->Write(name, option, bsize);
      }
      return nbytes;
   }
}

//______________________________________________________________________________
Int_t TMap::Write(const char *name, Int_t option, Int_t bsize)
{
   // Write all objects in this map. By default all objects in
   // the collection are written individually (each object gets its
   // own key). Note, this is recursive, i.e. objects in collections
   // in the collection are also written individually. To write all
   // objects using a single key specify a name and set option to
   // TObject::kSingleKey (i.e. 1).

   return ((const TMap*)this)->Write(name,option,bsize);
}


//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TPair                                                                //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

//______________________________________________________________________________
void TPair::Browse(TBrowser *b)
{
   // Browse the pair.

   if (b) {
      if (fKey)   b->Add(fKey);
      if (fValue) b->Add(fValue);
   } else {
      if (fKey)   fKey->Browse(b);
      if (fValue) fValue->Browse(b);
   }
}

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TMapIter                                                             //
//                                                                      //
// Iterator of map.                                                     //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

ClassImp(TMapIter)

//______________________________________________________________________________
TMapIter::TMapIter(const TMap *m, Bool_t dir)
{
   // Create a map iterator. Use dir to specify the desired iteration direction.

   fMap        = m;
   fDirection  = dir;
   fCursor     = 0;
}

//______________________________________________________________________________
TMapIter::TMapIter(const TMapIter &iter) : TIterator(iter)
{
   // Copy ctor.

   fMap       = iter.fMap;
   fDirection = iter.fDirection;
   fCursor    = 0;
   if (iter.fCursor) {
      fCursor = (THashTableIter *)iter.fCursor->GetCollection()->MakeIterator();
      if (fCursor)
         fCursor->operator=(*iter.fCursor);
   }
}

//______________________________________________________________________________
TIterator &TMapIter::operator=(const TIterator &rhs)
{
   // Overridden assignment operator.

   if (this != &rhs && rhs.IsA() == TMapIter::Class()) {
      const TMapIter &rhs1 = (const TMapIter &)rhs;
      fMap       = rhs1.fMap;
      fDirection = rhs1.fDirection;
      if (rhs1.fCursor) {
         fCursor = (THashTableIter *)rhs1.fCursor->GetCollection()->MakeIterator();
         if (fCursor)
            fCursor->operator=(*rhs1.fCursor);
      }
   }
   return *this;
}

//______________________________________________________________________________
TMapIter &TMapIter::operator=(const TMapIter &rhs)
{
   // Overloaded assignment operator.

   if (this != &rhs) {
      fMap       = rhs.fMap;
      fDirection = rhs.fDirection;
      if (rhs.fCursor) {
         fCursor = (THashTableIter *)rhs.fCursor->GetCollection()->MakeIterator();
         if (fCursor)
            fCursor->operator=(*rhs.fCursor);
      }
   }
   return *this;
}

//______________________________________________________________________________
TMapIter::~TMapIter()
{
   // Map iterator dtor.

   Reset();
}

//______________________________________________________________________________
TObject *TMapIter::Next()
{
   // Returns the next key from a map. Use TMap::GetValue() to get the value
   // associated with the key. Returns 0 when no more items in map.

   if (!fCursor)
      fCursor = new THashTableIter(fMap->fTable, fDirection);

   TPair *a = (TPair *)fCursor->Next();
   if (a) return a->Key();
   return 0;
}

//______________________________________________________________________________
void TMapIter::Reset()
{
   // Reset the map iterator.

   SafeDelete(fCursor);
}

//______________________________________________________________________________
Bool_t TMapIter::operator!=(const TIterator &aIter) const
{
   // This operator compares two TIterator objects.

   if (nullptr == (&aIter))
      return fCursor->operator*();

   if (aIter.IsA() == TMapIter::Class()) {
      const TMapIter &iter(dynamic_cast<const TMapIter &>(aIter));
      return (fCursor->operator*() != iter.fCursor->operator*());
   }
   return false; // for base class we don't implement a comparison
}

//______________________________________________________________________________
Bool_t TMapIter::operator!=(const TMapIter &aIter) const
{
   // This operator compares two TMapIter objects.

   if (nullptr == (&aIter))
      return fCursor->operator*();

   return (fCursor->operator*() != aIter.fCursor->operator*());
}

//______________________________________________________________________________
TObject *TMapIter::operator*() const
{
   // Return pointer to current object (a TPair) or nullptr.

   return (fCursor ? fCursor->operator*() : nullptr);
}
