// @(#)root/meta:$Id$
// Author: Bianca-Cristina Cristescu   10/07/13

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

//////////////////////////////////////////////////////////////////////////
//                                                                      //
// The TEnum class implements the enum type.                            //
//                                                                      //
//////////////////////////////////////////////////////////////////////////

#include <iostream>

#include "TEnum.h"
#include "TEnumConstant.h"
#include "TInterpreter.h"
#include "TClass.h"
#include "TClassEdit.h"
#include "TClassTable.h"
#include "TProtoClass.h"
#include "TROOT.h"

#include "TListOfEnums.h"

ClassImp(TEnum)

//______________________________________________________________________________
TEnum::TEnum(const char *name, void *info, TClass *cls)
   : fInfo(info), fClass(cls)
{
   //Constructor for TEnum class.
   //It take the name of the TEnum type, specification if it is global
   //and interpreter info.
   //Constant List is owner if enum not on global scope (thus constants not
   //in TROOT::GetListOfGlobals).
   SetName(name);
   if (cls) {
      fConstantList.SetOwner(kTRUE);
   }

   // Determine fQualName
   if (0 != strcmp("",GetTitle())){ // It comes from a protoclass
      fQualName = std::string(GetTitle()) + "::" + GetName();
   }
   else if (GetClass()){ // It comes from a class/ns
      fQualName = std::string(GetClass()->GetName()) + "::" + GetName();
   }
   else { // it is in the global scope
      fQualName = GetName();
   }
}

//______________________________________________________________________________
TEnum::~TEnum()
{
   //Destructor
}

//______________________________________________________________________________
void TEnum::AddConstant(TEnumConstant *constant)
{
   //Add a EnumConstant to the list of constants of the Enum Type.
   fConstantList.Add(constant);
}

//______________________________________________________________________________
Bool_t TEnum::IsValid()
{
   // Return true if this enum object is pointing to a currently
   // loaded enum.  If a enum is unloaded after the TEnum
   // is created, the TEnum will be set to be invalid.

   // Register the transaction when checking the validity of the object.
   if (!fInfo && UpdateInterpreterStateMarker()) {
      DeclId_t newId = gInterpreter->GetEnum(fClass, fName);
      if (newId) {
         Update(newId);
      }
      return newId != 0;
   }
   return fInfo != 0;
}

//______________________________________________________________________________
Long_t TEnum::Property() const
{
   // Get property description word. For meaning of bits see EProperty.

   return kIsEnum;
}

//______________________________________________________________________________
void TEnum::Update(DeclId_t id)
{
   fInfo = (void *)id;
}

//______________________________________________________________________________
TEnum *TEnum::GetEnum(const std::type_info &ti, ESearchAction sa)
{
   int errorCode = 0;
   char *demangledEnumName = TClassEdit::DemangleName(ti.name(), errorCode);

   if (errorCode != 0) {
      if (!demangledEnumName) {
         free(demangledEnumName);
      }
      std::cerr << "ERROR TEnum::GetEnum - A problem occurred while demangling name.\n";
      return nullptr;
   }

   const char *constDemangledEnumName = demangledEnumName;
   TEnum *en = TEnum::GetEnum(constDemangledEnumName, sa);
   free(demangledEnumName);
   return en;

}

//______________________________________________________________________________
TEnum *TEnum::GetEnum(const char *enumName, ESearchAction sa)
{
   // Static function to retrieve enumerator from the ROOT's typesystem.
   // It has no side effect, except when the load flag is true. In this case,
   // the load of the library containing the scope of the enumerator is attempted.
   // There are two top level code paths: the enumerator is scoped or isn't.
   // If it is not, a lookup in the list of global enums is performed.
   // If it is, two lookups are carried out for its scope: one in the list of
   // classes and one in the list of protoclasses. If a scope with the desired name
   // is found, the enum is searched. If the scope is not found, and the load flag is
   // true, the aforementioned two steps are performed again after an autoload attempt
   // with the name of the scope as key is tried out.
   // If the interpreter lookup flag is false, the ListOfEnums objects are not treated
   // as such, but rather as THashList objects. This prevents any flow of information
   // from the interpreter into the ROOT's typesystem: a snapshot of the typesystem
   // status is taken.

   // Potential optimisation: reduce number of branches using partial specialisation of
   // helper functions.

   TEnum *theEnum = nullptr;

   // Wrap some gymnastic around the enum finding. The special treatment of the
   // ListOfEnums objects is located in this routine.
   auto findEnumInList = [](const TCollection * l, const char * enName, ESearchAction sa_local) {
      TObject *obj;
      if (sa_local & kInterpLookup) {
         obj = l->FindObject(enName);
      } else {
         auto enumTable = dynamic_cast<const TListOfEnums *>(l);
         obj = enumTable->GetObject(enName);
      }
      return static_cast<TEnum *>(obj);
   };

   // Helper routine to look fo the scope::enum in the typesystem.
   // If autoload and interpreter lookup is allowed, TClass::GetClass is called.
   // If not, the list of classes and the list of protoclasses is inspected.
   auto searchEnum = [&theEnum, findEnumInList](const char * scopeName, const char * enName, ESearchAction sa_local) {
      // Check if the scope is a class
      if (sa_local == (kALoadAndInterpLookup)) {
         auto scope = TClass::GetClass(scopeName, true);
         TEnum *en = nullptr;
         if (scope) en = findEnumInList(scope->GetListOfEnums(), enName, sa_local);
         return en;
      }

      // Lock need for gROOT->GetListOfClasses() and the later update/modification to
      // the autoparsing state.
      R__LOCKGUARD(gInterpreterMutex);
      if (auto tClassScope = static_cast<TClass *>(gROOT->GetListOfClasses()->FindObject(scopeName))) {
         // If this is a class, load only if the user allowed interpreter lookup
         // If this is a namespace and the user did not allow for interpreter lookup, load but before disable
         // autoparsing if enabled.
         bool canLoadEnums (sa_local & kInterpLookup);
         const bool scopeIsNamespace (tClassScope->Property() & kIsNamespace);

         const bool autoParseSuspended = gInterpreter->IsAutoParsingSuspended();
         const bool suspendAutoParse = autoParseSuspended || scopeIsNamespace;

         TInterpreter::SuspendAutoParsing autoParseRaii(gInterpreter, suspendAutoParse);

         if (scopeIsNamespace && !autoParseSuspended){
            canLoadEnums=true;
         }

         auto listOfEnums = tClassScope->GetListOfEnums(canLoadEnums);

         // Previous incarnation of the code re-enabled the auto parsing,
         // before executing findEnumInList
         theEnum = findEnumInList(listOfEnums, enName, sa_local);
      }
      // Check if the scope is still a protoclass
      else if (auto tProtoClassscope = static_cast<TProtoClass *>((gClassTable->GetProtoNorm(scopeName)))) {
         auto listOfEnums = tProtoClassscope->GetListOfEnums();
         if (listOfEnums) theEnum = findEnumInList(listOfEnums, enName, sa_local);
      }
      return theEnum;
   };

   const char *lastPos = TClassEdit::GetUnqualifiedName(enumName);

   if (strchr(lastPos,'<')) {
      // The unqualified name has template syntax, it can't possibly be an
      // enum.
      return nullptr;
   }

   if (lastPos != enumName) {
      // We have a scope
      // All of this C gymnastic is to avoid allocations on the heap (see TClingLookupHelper__ExistingTypeCheck)
      const auto enName = lastPos;
      const auto scopeNameSize = ((Long64_t)lastPos - (Long64_t)enumName) / sizeof(decltype(*lastPos)) - 2;
      char scopeName[scopeNameSize + 1]; // on the stack, +1 for the terminating character '\0'
      strncpy(scopeName, enumName, scopeNameSize);
      scopeName[scopeNameSize] = '\0';
      // Three levels of search
      theEnum = searchEnum(scopeName, enName, kNone);
      if (!theEnum && (sa & kAutoload)) {
         const auto libsLoaded = gInterpreter->AutoLoad(scopeName);
         // It could be an enum in a scope which is not selected
         if (libsLoaded == 0){
            gInterpreter->AutoLoad(enumName);
         }
         theEnum = searchEnum(scopeName, enName, kAutoload);
      }
      if (!theEnum && (sa & kALoadAndInterpLookup)) {
         if (gDebug > 0) {
            printf("TEnum::GetEnum: Header Parsing - The enumerator %s is not known to the typesystem: an interpreter lookup will be performed. This can imply parsing of headers. This can be avoided selecting the numerator in the linkdef/selection file.\n", enumName);
         }
         theEnum = searchEnum(scopeName, enName, kALoadAndInterpLookup);
      }
   } else {
      // We don't have any scope: this is a global enum
      theEnum = findEnumInList(gROOT->GetListOfEnums(), enumName, kNone);
      if (!theEnum && (sa & kAutoload)) {
         gInterpreter->AutoLoad(enumName);
         theEnum = findEnumInList(gROOT->GetListOfEnums(), enumName, kAutoload);
      }
      if (!theEnum && (sa & kALoadAndInterpLookup)) {
         if (gDebug > 0) {
            printf("TEnum::GetEnum: Header Parsing - The enumerator %s is not known to the typesystem: an interpreter lookup will be performed. This can imply parsing of headers. This can be avoided selecting the numerator in the linkdef/selection file.\n", enumName);
         }
         theEnum = findEnumInList(gROOT->GetListOfEnums(), enumName, kALoadAndInterpLookup);
      }
   }

   return theEnum;
}
 TEnum.cxx:1
 TEnum.cxx:2
 TEnum.cxx:3
 TEnum.cxx:4
 TEnum.cxx:5
 TEnum.cxx:6
 TEnum.cxx:7
 TEnum.cxx:8
 TEnum.cxx:9
 TEnum.cxx:10
 TEnum.cxx:11
 TEnum.cxx:12
 TEnum.cxx:13
 TEnum.cxx:14
 TEnum.cxx:15
 TEnum.cxx:16
 TEnum.cxx:17
 TEnum.cxx:18
 TEnum.cxx:19
 TEnum.cxx:20
 TEnum.cxx:21
 TEnum.cxx:22
 TEnum.cxx:23
 TEnum.cxx:24
 TEnum.cxx:25
 TEnum.cxx:26
 TEnum.cxx:27
 TEnum.cxx:28
 TEnum.cxx:29
 TEnum.cxx:30
 TEnum.cxx:31
 TEnum.cxx:32
 TEnum.cxx:33
 TEnum.cxx:34
 TEnum.cxx:35
 TEnum.cxx:36
 TEnum.cxx:37
 TEnum.cxx:38
 TEnum.cxx:39
 TEnum.cxx:40
 TEnum.cxx:41
 TEnum.cxx:42
 TEnum.cxx:43
 TEnum.cxx:44
 TEnum.cxx:45
 TEnum.cxx:46
 TEnum.cxx:47
 TEnum.cxx:48
 TEnum.cxx:49
 TEnum.cxx:50
 TEnum.cxx:51
 TEnum.cxx:52
 TEnum.cxx:53
 TEnum.cxx:54
 TEnum.cxx:55
 TEnum.cxx:56
 TEnum.cxx:57
 TEnum.cxx:58
 TEnum.cxx:59
 TEnum.cxx:60
 TEnum.cxx:61
 TEnum.cxx:62
 TEnum.cxx:63
 TEnum.cxx:64
 TEnum.cxx:65
 TEnum.cxx:66
 TEnum.cxx:67
 TEnum.cxx:68
 TEnum.cxx:69
 TEnum.cxx:70
 TEnum.cxx:71
 TEnum.cxx:72
 TEnum.cxx:73
 TEnum.cxx:74
 TEnum.cxx:75
 TEnum.cxx:76
 TEnum.cxx:77
 TEnum.cxx:78
 TEnum.cxx:79
 TEnum.cxx:80
 TEnum.cxx:81
 TEnum.cxx:82
 TEnum.cxx:83
 TEnum.cxx:84
 TEnum.cxx:85
 TEnum.cxx:86
 TEnum.cxx:87
 TEnum.cxx:88
 TEnum.cxx:89
 TEnum.cxx:90
 TEnum.cxx:91
 TEnum.cxx:92
 TEnum.cxx:93
 TEnum.cxx:94
 TEnum.cxx:95
 TEnum.cxx:96
 TEnum.cxx:97
 TEnum.cxx:98
 TEnum.cxx:99
 TEnum.cxx:100
 TEnum.cxx:101
 TEnum.cxx:102
 TEnum.cxx:103
 TEnum.cxx:104
 TEnum.cxx:105
 TEnum.cxx:106
 TEnum.cxx:107
 TEnum.cxx:108
 TEnum.cxx:109
 TEnum.cxx:110
 TEnum.cxx:111
 TEnum.cxx:112
 TEnum.cxx:113
 TEnum.cxx:114
 TEnum.cxx:115
 TEnum.cxx:116
 TEnum.cxx:117
 TEnum.cxx:118
 TEnum.cxx:119
 TEnum.cxx:120
 TEnum.cxx:121
 TEnum.cxx:122
 TEnum.cxx:123
 TEnum.cxx:124
 TEnum.cxx:125
 TEnum.cxx:126
 TEnum.cxx:127
 TEnum.cxx:128
 TEnum.cxx:129
 TEnum.cxx:130
 TEnum.cxx:131
 TEnum.cxx:132
 TEnum.cxx:133
 TEnum.cxx:134
 TEnum.cxx:135
 TEnum.cxx:136
 TEnum.cxx:137
 TEnum.cxx:138
 TEnum.cxx:139
 TEnum.cxx:140
 TEnum.cxx:141
 TEnum.cxx:142
 TEnum.cxx:143
 TEnum.cxx:144
 TEnum.cxx:145
 TEnum.cxx:146
 TEnum.cxx:147
 TEnum.cxx:148
 TEnum.cxx:149
 TEnum.cxx:150
 TEnum.cxx:151
 TEnum.cxx:152
 TEnum.cxx:153
 TEnum.cxx:154
 TEnum.cxx:155
 TEnum.cxx:156
 TEnum.cxx:157
 TEnum.cxx:158
 TEnum.cxx:159
 TEnum.cxx:160
 TEnum.cxx:161
 TEnum.cxx:162
 TEnum.cxx:163
 TEnum.cxx:164
 TEnum.cxx:165
 TEnum.cxx:166
 TEnum.cxx:167
 TEnum.cxx:168
 TEnum.cxx:169
 TEnum.cxx:170
 TEnum.cxx:171
 TEnum.cxx:172
 TEnum.cxx:173
 TEnum.cxx:174
 TEnum.cxx:175
 TEnum.cxx:176
 TEnum.cxx:177
 TEnum.cxx:178
 TEnum.cxx:179
 TEnum.cxx:180
 TEnum.cxx:181
 TEnum.cxx:182
 TEnum.cxx:183
 TEnum.cxx:184
 TEnum.cxx:185
 TEnum.cxx:186
 TEnum.cxx:187
 TEnum.cxx:188
 TEnum.cxx:189
 TEnum.cxx:190
 TEnum.cxx:191
 TEnum.cxx:192
 TEnum.cxx:193
 TEnum.cxx:194
 TEnum.cxx:195
 TEnum.cxx:196
 TEnum.cxx:197
 TEnum.cxx:198
 TEnum.cxx:199
 TEnum.cxx:200
 TEnum.cxx:201
 TEnum.cxx:202
 TEnum.cxx:203
 TEnum.cxx:204
 TEnum.cxx:205
 TEnum.cxx:206
 TEnum.cxx:207
 TEnum.cxx:208
 TEnum.cxx:209
 TEnum.cxx:210
 TEnum.cxx:211
 TEnum.cxx:212
 TEnum.cxx:213
 TEnum.cxx:214
 TEnum.cxx:215
 TEnum.cxx:216
 TEnum.cxx:217
 TEnum.cxx:218
 TEnum.cxx:219
 TEnum.cxx:220
 TEnum.cxx:221
 TEnum.cxx:222
 TEnum.cxx:223
 TEnum.cxx:224
 TEnum.cxx:225
 TEnum.cxx:226
 TEnum.cxx:227
 TEnum.cxx:228
 TEnum.cxx:229
 TEnum.cxx:230
 TEnum.cxx:231
 TEnum.cxx:232
 TEnum.cxx:233
 TEnum.cxx:234
 TEnum.cxx:235
 TEnum.cxx:236
 TEnum.cxx:237
 TEnum.cxx:238
 TEnum.cxx:239
 TEnum.cxx:240
 TEnum.cxx:241
 TEnum.cxx:242
 TEnum.cxx:243
 TEnum.cxx:244
 TEnum.cxx:245
 TEnum.cxx:246
 TEnum.cxx:247
 TEnum.cxx:248
 TEnum.cxx:249
 TEnum.cxx:250
 TEnum.cxx:251
 TEnum.cxx:252
 TEnum.cxx:253
 TEnum.cxx:254