    // @(#)root/html:$Name:  $:$Id: THtml.h,v 1.12 2004/01/28 22:31:11 brun Exp $
    // Author: Nenad Buncic   18/10/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.             *
     *************************************************************************/

    #ifndef ROOT_THtml
    #define ROOT_THtml

    ////////////////////////////////////////////////////////////////////////////
    //                                                                        //
    // THtml                                                                  //
    //                                                                        //
    // Html makes a documentation for all ROOT classes                        //
    // using Hypertext Markup Language 2.0                                    //
    //                                                                        //
    ////////////////////////////////////////////////////////////////////////////

    #ifndef __CINT__
    #include "TCint.h"
    #include "Api.h"
    #undef G__FPROTO_H
    #include "fproto.h"
    #endif
    //#include "Type.h"
    //#include "G__ci.h"
    //#include "Typedf.h"
    //#include "Class.h"

    #ifndef ROOT_TROOT
    #include "TROOT.h"
    #endif
    #ifndef ROOT_TDictionary
    #include "TDictionary.h"
    #endif
    #ifndef ROOT_TMap
    #include "TMap.h"
    #endif
    #ifndef ROOT_THashList
    #include "THashList.h"
    #endif

    class TClass;
    class TVirtualPad;
    class TPaveText;

    class THtml : public TObject {

    public:
    //_____________________________________________________
    // doc for TDocElement
    //
       class TDocElement: public TObject {
       public:
          inline TDocElement(const TString doc,
             const TString sourceLink="", const TString docLink=""):
          fDoc(doc), fSourceLink(sourceLink),
             fDocLink(docLink) {};

          inline const TString& GetDoc() const {
             return fDoc;}
          inline void AddToDoc(const char* doc) {
             fDoc+=doc;
          }
          inline const TString& GetDocLink() const {
             return fDocLink;}
          inline const TString& GetSourceLink() const {
             return fSourceLink;}

          inline TDocElement& SetDoc(const TString doc) {
             fDoc=doc;
             return *this; }
          inline TDocElement& SetDocLink(const TString docLink) {
             fDocLink=docLink;
             return *this; }

       private:
          TString fDoc; // documentation for the element
          TString fSourceLink; // link to source where this element is defined
          TString fDocLink; // link to documentation of this element
          ClassDef(TDocElement, 0)
       };

       //______________________________________________________
       //
       // Addendum to TROOT's list of classes, containing
       // * additional, non-linkdef'ed classes
       // * enums
       // * structs
       // * typedefs
       class TLocalType: public TDictionary {
       public:
          enum ESpec {
             kUndefined,
             kClass,
             kEnum,
             kStruct,
             kTypedef,
             kNumSpecs
          };

          inline TLocalType(const char* nameFQI,
             ESpec spec,
             const char* declFileName, Int_t declFileLine,
             const char* realTypdefName = 0):
             fName(nameFQI),
             fDeclName(declFileName),
    	 fTypedefReal(realTypdefName),
             fSpec(spec), fDeclLine(declFileLine) {}

          inline virtual ~TLocalType() {}
          inline const char* GetName() const {
             return fName; }
          inline const char* GetDeclFileName() const {
             return fDeclName;
          }
          inline Int_t GetDeclFileLine() const {
             return fDeclLine;
          }
          inline const char* RealTypedefName() const {
             return fTypedefReal; }
          inline ESpec Spec() const {
             return fSpec;
          }
          inline int Compare(const TObject *obj) const {
             return strcmp(fName, ((TDictionary*)obj)->GetName());
          }
          const char *GetTitle() const {
             return 0;
          }
          ULong_t Hash() const {
             return fName.Hash();
          }
          Long_t Property() const {
             return 0;
          }

       private:
          TString fName; // type name
          TString fDeclName; // file name containing declaration
          TString fTypedefReal; // "A" for "typedef A B;"
          ESpec fSpec; // type of type
          Int_t fDeclLine; // line number in file containing declaration
          ClassDef(TLocalType,0) // additional types found while parsing
       };

    //_____________________________________________________
    // doc for TParseStack
    //
       class TParseStack{
       public:
          enum EContext {
             kTop, // topmost - i.e. no - context
             kComment, // within comment /* ... */
             kBlock, // { ... }
             kParameter, // ( ... )
             kTemplate, // < ... >
             kArray, // [ ... ]
             kString, // " ... "
             kUndefined
          };
          enum EBlockSpec {
             kClassDecl, // class ...{ (or enum or struct)
             kMethodDef, // some method is defined here
             kMethodDefCand, // some method might be defined here (not sure, e.g. unknown param type)
             kNamespace, // within namespace ...{
             kBlkUndefined
          };

          //____________________________________________
          // doc for TParseElement
          //
          class TParseElement: public TNamed {
          public:
             inline TParseElement(EContext ctx=kUndefined, EBlockSpec bsp=kBlkUndefined,
                const char* fName=0, const char* fTitle=0, TDictionary* dict=0):
             TNamed(fName, fTitle), fCtx(ctx), fBsp(bsp), fPStrUsing(0), fDict(dict) {};
             inline TParseElement(char cStart, EBlockSpec bsp=kBlkUndefined,
                const char* fName=0, const char* fTitle=0, TDictionary* dict=0):
             TNamed(fName, fTitle), fBsp(bsp), fPStrUsing(0), fDict(dict) {
                if(cStart=='/') fCtx=kComment;
                else if (cStart=='{') fCtx=kBlock;
                else if (cStart=='(') fCtx=kParameter;
                else if (cStart=='[') fCtx=kArray;
                else if (cStart=='<') fCtx=kTemplate;
                else if (cStart=='"') fCtx=kString;
             }

             virtual ~TParseElement() {
                if (fPStrUsing) delete fPStrUsing;
             }

             inline void AddUsing(const char* cIdentifier) {
                if (!fPStrUsing) fPStrUsing=new TString(" ");
                *fPStrUsing+=cIdentifier;
                *fPStrUsing+=" "; // as delimiter
             }
             inline EContext Context() const {
                return fCtx; }
             inline EBlockSpec BlockSpec() const {
                return fBsp; }
             inline TDictionary* Dict() const {
                return fDict; }
             inline TParseElement& SetContext(EContext ctx) {
                fCtx=ctx;
                return *this;
             }
             inline Bool_t IsUsing(const char* cIdentifier) const {
                if (strcmp(cIdentifier, fName)==0) return kTRUE;
                if (!fPStrUsing) return kFALSE;
                TString strSearch(" ");
                strSearch+=cIdentifier;
                strSearch+=" ";
                return (fPStrUsing->Contains(strSearch));
             }
             inline TString* GetUsing() const {
                return fPStrUsing;
             }

             inline const char* GetCloseTag() const {
                switch (fCtx) {
                case kComment: return "*/";
                case kBlock: return "}";
                case kParameter: return ")";
                case kTemplate: return ">";
                case kArray: return "]";
                case kString: return "\"";
    	    default: return NULL;
                }
             }

          private:
             EContext     fCtx;
             EBlockSpec   fBsp;
             TString*     fPStrUsing;
             TDictionary* fDict;
             ClassDef(TParseElement, 0)
          };

          inline TParseStack(){
             Push(kTop);

             // fill fTypes
             // start with classes
             G__ClassInfo clinfo;
             while (clinfo.Next())
                fTypes.Add(new TNamed(clinfo.Fullname(), clinfo.Title()));

             G__TypedefInfo typeinfo;
             while (typeinfo.Next())
                fTypedefs.Add(new TNamed(typeinfo.Name(), typeinfo.TrueName()));
          };
          virtual ~TParseStack();
          inline TParseElement& Push(EContext ctx, EBlockSpec bsp=kBlkUndefined,
             const char* name="", const char* title="") {
             return Push(new TParseElement(ctx, bsp, name, title));
          }
          inline TParseElement& Push(char cStart, EBlockSpec bsp=kBlkUndefined,
             const char* name="", const char* title="") {
             return Push(new TParseElement(cStart, bsp, name, title));
          }
          inline TParseElement& Push(TParseElement* pe) {
             fStack.AddLast(pe);
             return *pe;
          }
          inline TParseElement* Pop() {
             if (fStack.GetSize()==1) {
    //printf("Warning in <TParseStack::Pop>: Stack is already empty!\n");
                return 0;
             } else
                return (TParseElement*) fStack.Remove(fStack.LastLink());
          }
          inline void PopAndDel() {
             delete Pop();
          }
          inline TParseElement& Top() const{
             return *((TParseElement*) fStack.Last());
          }
          inline TList* Stack() {
             return &fStack;
          }

          inline EContext Context() const {
             return Top().Context();
          }

          inline EBlockSpec BlockSpec() const {
             return Top().BlockSpec();
          }

          inline TDictionary* Dict() const {
             return Top().Dict();
          }

          inline void GetFQI(TString& fqi) const{
             // prepend the surrounding class and namepace names
             // (separated by "::") to whatever is in fqi
             TIter psi(&fStack, kIterBackward);
             TParseElement* pe;
             while ((pe=(TParseElement*) psi()))
                if (pe->Context()==kBlock &&
                   (pe->BlockSpec()==kClassDecl || pe->BlockSpec()==kNamespace)){
                fqi.Prepend("::");
                fqi.Prepend(pe->GetName());
                }
          }

          inline const char* IsUsing(const char* cIdentifier) const{
          // returns the part of cIdentifier that is not covered by a using directive
             static char cID[1024];
             char* cFindNext=cID;
             strcpy(cID, cIdentifier);

             TIter psi(&fStack);
             TParseElement* pe;

             while (cFindNext) {
                // only look for next part of identifier
                // this doesn't work if there's a using namespace::class directive...
                char* cCol=strchr(cFindNext, ':');
                if (cCol) *cCol=0;

                while ((pe=(TParseElement*) psi()) && !pe->IsUsing(cFindNext));
                if (pe && cCol) cFindNext=&cCol[2];
                if (!pe) return &cIdentifier[cFindNext-cID];
             }
             return 0;
          }

          inline Bool_t FindTypeFQI(TString& type, TDictionary*& dict) {
             // first try local type
             TString fqi(type);
             GetFQI(fqi);
             // if they are the same this doesn't get us anywhere
             if (strcmp(fqi, type)) {
                if (fTypes.FindObject(fqi)) {
                   dict=THtml::GetType(fqi);
                   type=fqi;
                   return kTRUE;
                }
                if (fTypedefs.FindObject(fqi)) {
                   type=fqi;
                   return kTRUE;
                }
             }

             dict=THtml::GetType(type);
             // maybe THtml found its own type
             if (dict)
                return kTRUE;

             return (fTypedefs.FindObject(type)) ;
          }

          inline Bool_t FindType(TString& strClassName){
             TDictionary* dict;
             return FindType(strClassName, dict);
          }

          inline Bool_t FindType(TString& strClassName, TDictionary*& dict){
             // find a class named strClassName, if necessary add
             // "using"-used or surrounding classes / namespaces
             Bool_t bFound=FindTypeFQI(strClassName, dict);

             // iterate through parse stack, try to find FQI
             TParseElement* pe=0;
             TIter iPE(&fStack, kFALSE);
             while (!bFound && (pe=(TParseElement*) iPE())) {
                TString *strUsing=pe->GetUsing();
                if (strUsing) {
                   const char* pos=strUsing->Data();
    	       Int_t end;
                   TString strClassUsing;
                   ParseWord(pos, end, strClassUsing, ":");
                   strClassUsing+=strClassName;
                   bFound=FindTypeFQI(strClassUsing, dict);
                   if (bFound) strClassName=strClassUsing;
                }
             } // for (parse elements in stack)
             return bFound;
          }

          Bool_t IsInStack(EContext ctx, EBlockSpec bsp=kBlkUndefined) const {
             TIter psi(&fStack, kIterBackward);
             TParseElement* pe;
             while ((pe=(TParseElement*) psi()))
                if (pe->Context()==ctx &&
                   (bsp==kBlkUndefined || pe->BlockSpec()==bsp))
                   return kTRUE;
             return kFALSE;
          }

          inline void AddCustomType(const char* type) {
             fTypes.Add(new TNamed(type,0));
          }

       private:
          TList fStack;
          THashList fTypes; // hashed list of known types
          THashList fTypedefs; // hashed list of known typedefs

          ClassDef(TParseStack, 0);
       };

    protected:
        TString      fXwho;            // by default http://xwho.cern.ch/WHO/people?
      const char    *fSourcePrefix;    // prefix to relative source path
      const char    *fSourceDir;       // source path
      const char    *fOutputDir;       // output directory
            char    *fLine;            // current line
            Int_t    fLen;             // maximum line length
            char    *fCounter;         // counter string
            Bool_t   fEscFlag;         // Flag to mark the symbol must be written "as is"
            char     fEsc;             // The special symbol ("backslash" by default) to mark "the next symbol should not be converted
            TMap    *fMapDocElements;  // map of <TDictionary*, TDocElement*> for all objects for which doc was parsed
            static   THashList fgLocalTypes; // list of types that are not in TROOT::GetClass
            TList    fFilesParsed;     // list of files on which ExtractDocumentatoin was run
            Int_t    fHierarchyLines; // counter for no. lines in hierarchy

            enum ETraverse {
              kUp, kDown, kBoth        // direction to traverse class tree in ClassHtmlTree()
            };

            void    Class2Html(TClass *classPtr, Bool_t force=kFALSE);
            void    ClassDescription(ofstream &out, TClass *classPtr, Bool_t &flag);
            void    ClassHtmlTree(ofstream &out, TClass *classPtr, ETraverse dir=kBoth, int depth=1);
            void    ClassTree(TVirtualPad *canvas, TClass *classPtr, Bool_t force=kFALSE);
            Bool_t  CopyHtmlFile(const char *sourceName, const char *destName="");
            void    CreateIndex(const char **classNames, Int_t numberOfClasses);
            void    CreateIndexByTopic(char **filenames, Int_t numberOfNames, Int_t maxLen);
            void    CreateHierarchy(const char **classNames, Int_t numberOfClasses);
            void    CreateListOfTypes();
            void    DescendHierarchy(ofstream &out, TClass* basePtr,
                      const char **classNames, Int_t numberOfClasses,
                      Int_t maxLines=0, Int_t depth=1);
            void    ExpandKeywords(ofstream &out, char *text, TClass *ptr2class, Bool_t &flag, const char *dir="");
            void    ExpandPpLine(ofstream &out, char *line);
          TClass   *GetClass(const char *name, Bool_t load=kTRUE);
      const char   *GetFileName(const char *filename);
            char   *GetSourceFileName(const char *filename);
            char   *GetHtmlFileName(TClass *classPtr);
            Bool_t  IsModified(TClass *classPtr, const Int_t type);
            static Bool_t  IsName(Int_t c);
            static Bool_t  IsWord(Int_t c);
    		void	NameSpace2FileName(char *name);
            void    ReplaceSpecialChars(ofstream &out, const char c);
            void    ReplaceSpecialChars(ofstream &out, const char *string);
            void    SortNames(const char **strings, Int_t num, Bool_t type=0);
            char   *StrDup(const char *s1, Int_t n = 1);

       friend Int_t CaseSensitiveSort(const void *name1, const void *name2);
       friend Int_t CaseInsensitiveSort(const void *name1, const void *name2);

       TClass* ParseClassDecl(char* &cfirstLinePos,
          const TParseStack& parseStack, TString& strClassName);
       TDocElement* AddDocElement(TDictionary* dict, TString& strDoc, const char* filename);
       TDocElement* GetDocElement(TDictionary* dict) const {
          return (TDocElement*)(fMapDocElements?fMapDocElements->GetValue(dict):0);
       }

       Bool_t FindMethodImpl(TString strMethFullName, TList& listMethodSameName,
          TList& listArgs, TParseStack& parseStack, Bool_t done=kFALSE) const;

    public:
                     THtml();
           virtual   ~THtml();
              void   Convert(const char *filename, const char *title, const char *dirname = "");
        const char  *GetSourceDir()  { return fSourceDir; }
        const char  *GetOutputDir()  { return fOutputDir; }
        const char  *GetXwho() const { return fXwho.Data(); }
              void   MakeAll(Bool_t force=kFALSE, const char *filter="*");
              void   MakeClass(const char *className, Bool_t force=kFALSE);
              void   MakeIndex(const char *filter="*");
              void   MakeTree(const char *className, Bool_t force=kFALSE);
              void   SetEscape(char esc='\\') { fEsc = esc; }
              void   SetSourcePrefix(const char *prefix) { fSourcePrefix = prefix; }
              void   SetSourceDir(const char *dir) { fSourceDir = dir; }
              void   SetOutputDir(const char *dir) { fOutputDir = dir; }
              void   SetXwho(const char *xwho) { fXwho = xwho; }
       virtual void  WriteHtmlHeader(ofstream & out, const char *title, TClass *cls=0);
       virtual void  WriteHtmlFooter(ofstream &out, const char *dir="", const char *lastUpdate="",
                                     const char *author="", const char *copyright="");

       void ExtractDocumentation(const char* cFileName, TList* listClassesFound);
       void ExtractClassDocumentation(const TClass* classPtr);
       static Bool_t ParseWord(const char* begin, Int_t &step,
          const char* allowedChars=0);
       static Bool_t ParseWord(const char* begin, Int_t &step,
          TString &strWord, const char* allowedChars=0);

       static inline TDictionary* GetType(const char* type) {
          TDictionary* dict=(TDictionary*) gROOT->GetClass(type);
          if (!dict) return (TDictionary*) fgLocalTypes.FindObject(type);
          else return dict;
       }

       const char* GetDoc(TDictionary* dict) const {
          TDocElement* de=GetDocElement(dict);
          return de ? de->GetDoc().Data() : 0;
       }
       TMap* MakeHelp(TClass* cl);
       TPaveText* GetDocPave(TDictionary* dict);

       ClassDef(THtml,0)  //Convert class(es) into HTML file(s)
    };

    R__EXTERN THtml *gHtml;

    #endif