// @(#)root/base:$Id$
// Author: Rene Brun   11/11/99

/*************************************************************************
 * 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.             *
 *************************************************************************/


//////////////////////////////////////////////////////////////////////////
//                                                                      //
// TMessageHandler                                                      //
//                                                                      //
// Handle messages that might be generated by the system.               //
// By default a handler only keeps track of the different messages      //
// generated for a specific class. By deriving from this class and      //
// overriding Notify() one can implement custom message handling.       //
// In Notify() one has access to the message id and the object          //
// generating the message. One can install more than one message        //
// handler per class. A message handler can be removed or again         //
// added when needed.                                                   //
// All Root "Warnings"  are logged as message 1001                      //
// All Root "Errors"    are logged as message 1002                      //
// All Root "SysErrors" are logged as message 1003                      //
// All Root "Fatals"    are logged as message 1004                      //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include "TMessageHandler.h"
#include "TClass.h"
#include "TROOT.h"
#include "TVirtualMutex.h"

ClassImp(TMessageHandler)

//______________________________________________________________________________
TMessageHandler::TMessageHandler(const TClass *cl, Bool_t derived)
{
   // Create a new message handler for class cl and add it to the list
   // of message handlers.

   fClass   = cl;
   fMessObj = 0;
   fMessId  = 0;
   fSize    = 0;
   fCnts    = 0;
   fMessIds = 0;
   fDerived = derived;

   if (fClass)
      SetName(fClass->GetName());
   else
      SetName("DefaultMessageHandler");

   Add();
}

//______________________________________________________________________________
TMessageHandler::TMessageHandler(const char *cl, Bool_t derived)
{
   // Create a new message handler for class named cl and add it to the list
   // of message handlers.

   fClass   = TClass::GetClass(cl);
   fMessObj = 0;
   fMessId  = 0;
   fSize    = 0;
   fCnts    = 0;
   fMessIds = 0;
   fDerived = derived;

   SetName(cl);

   SetName(fClass->GetName());
   Add();
}

//______________________________________________________________________________
TMessageHandler:: ~TMessageHandler()
{
   // Clean up the messagehandler.

   Remove();
   if (fSize <= 0) return;
   delete [] fCnts;
   delete [] fMessIds;
}

//______________________________________________________________________________
void TMessageHandler::Add()
{
   // Add this message handler to the list of messages handlers.

   R__LOCKGUARD2(gROOTMutex);
   gROOT->GetListOfMessageHandlers()->Add(this);
   if (fClass) {
      // don't emit signal when the default message handler is added
      // as this happens in the TROOT ctor and the TQObject stuff is
      // not yet properly initialized on some platforms
      Added();  // emit Added() signal
   }
}

//______________________________________________________________________________
Int_t TMessageHandler::GetMessageCount(Int_t messId) const
{
   // Return counter for message with ID=messid.

   if (fSize <= 0) return 0;
   for (Int_t i = 0; i < fSize; i++) {
      if (fMessIds[i] == messId) return fCnts[i];
   }
   return 0;
}

//______________________________________________________________________________
Int_t TMessageHandler::GetTotalMessageCount() const
{
   // Return total number of messages.

   if (fSize <= 0) return 0;
   Int_t count = 0;
   for (Int_t i = 0; i < fSize; i++) {
      count += fCnts[i];
   }
   return count;
}

//______________________________________________________________________________
void TMessageHandler::HandleMessage(Int_t id, const TObject *obj)
{
   // Store message origin, keep statistics and call Notify().

   // check if message must be managed by this message handler
   if (fClass) {
      if (fDerived) {
         if(!obj->InheritsFrom(fClass)) return;
      } else {
         if (obj->IsA() != fClass) return;
      }
   }

   fMessId  = id;
   fMessObj = obj;

   Notify();

   // increment statistics
   Int_t i;
   // first message
   if (fSize <= 0) {
      fSize    = 1;
      fCnts    = new Int_t[fSize];
      fMessIds = new Int_t[fSize];
   } else {
      // already existing message
      for (i = 0; i < fSize; i++) {
         if (fMessIds[i] == fMessId) {
            fCnts[i]++;
            return;
         }
      }
      // new message
      fSize++;
      Int_t *newCnts    = new Int_t[fSize];
      Int_t *newMessIds = new Int_t[fSize];
      for (i = 0; i < fSize-1; i++) {
         newCnts[i]    = fCnts[i];
         newMessIds[i] = fMessIds[i];
      }
      delete [] fCnts;
      delete [] fMessIds;
      fCnts    = newCnts;
      fMessIds = newMessIds;
   }
   fCnts[fSize-1]    = 1;
   fMessIds[fSize-1] = fMessId;
}

//______________________________________________________________________________
Bool_t TMessageHandler::Notify()
{
   // This method must be overridden to handle object notifcation.

   if (fClass) return kFALSE;
   // case of default handler
   // encode class number in message id
   if (!fMessObj) return kFALSE;
   Int_t uid = Int_t(fMessObj->IsA()->GetUniqueID());
   fMessId += 10000*uid;
   fMessId = -fMessId;
   Notified();  // emit Notified() signal
   return kFALSE;
}

//______________________________________________________________________________
void TMessageHandler::Print(Option_t *) const
{
   // Print statistics for this message handler.

   printf("\n ****** Message Handler: %s has a total of %d messages\n",GetName(),GetTotalMessageCount());
   if (fSize <= 0) return;
   Int_t id, uid;
   const TClass *cl;
   TIter next(gROOT->GetListOfClasses());
   for (Int_t i = 0; i < fSize; i++) {
      id = fMessIds[i];
      cl = fClass;
      if (id < 0) {
         id = -id;
         uid = id/10000;
         id = id%10000;
         next.Reset();
         while ((cl = (TClass*)next())) {
            if (cl->GetUniqueID() == UInt_t(uid)) break;
         }
      }
      if (!cl) cl = gROOT->IsA();
      if (id == 1001) {
         printf("  Class: %-20s WARNINGs       has %d counts\n",cl->GetName(),fCnts[i]);
         continue;
      }
      if (id == 1002) {
         printf("  Class: %-20s ERRORs         has %d counts\n",cl->GetName(),fCnts[i]);
         continue;
      }
      printf("  Class: %-20s MessID = %5d has %d counts\n",cl->GetName(),id,fCnts[i]);
   }
}

//______________________________________________________________________________
void TMessageHandler::Remove()
{
   // Remove this message handler from the list of messages handlers.

   R__LOCKGUARD2(gROOTMutex);
   gROOT->GetListOfMessageHandlers()->Remove(this);
   Removed();  // emit Removed() signal
}
