// Copyright 2020 Siemens Digital Industries Software
// ==================================================
// Copyright 2010.
// Siemens Product Lifecycle Management Software Inc.
// All Rights Reserved.
// ==================================================
// Copyright 2020 Siemens Digital Industries Software
/**
    @file LoggerTree.hxx
    LoggerTree class manages the loggers, this class maintains and implements the
    required functionalities to be performed on the Tree. Tree class maintains
    the functionalities required to maintain the node class instances as children nodes.
    This class is a singleton class for which objects cannot be created directly, inorder
    to access the member functions of this class, use the static method getTree()
    which return a LoggerTree object.

    Example:
        LoggerTree::getTree()->printTree();
**/
/**
    Node class maintains the logger, parentnode pointers and a vector of node pointers for
    maintaining the children for each node. Node class instances are used to maintain the
    logger information.
**/

#ifndef TEAMCENTER_LOGGING_LOGGERTREE_HXX
#define TEAMCENTER_LOGGING_LOGGERTREE_HXX

#include <vector>
#include <map>
#include <fstream>

#include <mld/logging/Logger.hxx>
#include <mld/libmld_exports.h>

typedef std::string (*GetLogCorrelationIdFunction)();

namespace Teamcenter
{
    namespace Logging
    {
        class LoggerTree;
        class Logger;

        class MLD_API Node
        {
            public:
                friend class LoggerTree;

            private:
                /** Logger class pointer */
                Logger *logger;

                /** Store parent node pointer for the logger object */
                Node *parentNode;

                /** Vector to store the child nodes */
                 std::vector<Node*> childNodes;

                /** Default constructor, sets the logger and parent node to null */
                 Node();

                /**
                  * Constructor to set the logger.
                  *
                  * @param[in] logger     set the logger pointer.
                  */
                 explicit Node(Logger* logger);

                /**
                  * Constructor to set the logger
                  *
                  * @param[in] key    logger name
                  */
                 explicit Node(const std::string& key);

                 /**
                  * Destructor.  Deletes the allocated logger.
                  */
                 ~Node();

                /**
                  * Sets the default log level for the logger.
                  */
                 void setDefaultLevel();
        };

        class MLD_API LoggerTree
        {
        public:
            /**
             * Given a unique key this method returns the logger pointer,
             * if the logger exists for that key in the tree. 
             * <br/>Otherwise, it creates a logger object and returns a new logger pointer.
             *
             * @param[in] key         The logger name
             * @returns Logger         The logger pointer
             */
            Logger *getLogger(const std::string &key);
            
            /**
             *  Returns a tree pointer. 
             *  <br/>This method is defined as
             *  static so that only one tree is created/retrieved.
             *
             *  @returns Tree         return tree pointer.
             */
            static LoggerTree* getTree();
             
            /**
             * Returns the root node.
             *
             * @returns Node  the  root node
             */
            Node *getRootNode();
            
            /**
             * Returns the root logger.
             *
             * @returns Logger   root logger pointer
             */
            Logger *getRootLogger() const;
            
            /**
             * Traverses the tree and prints the Tree structure
             *
             * @param[in] node  node in tree
             */
            void printTree(Node *node);

            /** 
             * Returns the list of loggers in the tree, which are
             * children to the provided node key.
             * 
             * @param[in] key The node key used for traversing
             */
             std::vector<Logger *> getLoggersList ( const std::string& key );
                 
            /**
             * Sets the log level of the provided logger name to the same level 
             * as the parent log level.
             * @param[in] key The node key
             */
            void setParentLevel (const std::string& key);

            /*
             * Registers the function to retrieve the log correlation ID.
             * <br/>The correlation ID is a unique string that the client establishes for each server request.
             * <br/>It is propagated to each tier of the Teamcenter architecture and displayed on every log message.
             * <br/><br/>The function is implemented by the calling program and its function pointer is given to the
             * logging system upon initialization.
             *
             * @param[in] correlationIDFunction The correlation ID function
             */
            static void registerLogCorrelationId (GetLogCorrelationIdFunction correlationIDFunction);
            
            /**
             * Returns the Log Correlation ID
             */
            static std::string getLogCorrelationId();
            
            /**
             * Alphabetically sorts the new added loggers.
             *
             * @param[in] node    The added logger node
             * @param[in] parent  The parent logger node
             */
            void sortInsert (Node *node, Node *parent ) const;
            
            /**
             * Sets the immediate child log level for this logger and the non-immediate logger levels.
             *
             * @param[in] name  The logger name
             * @param[in] lvl   The logger level
            */
            void setLoglevels( const std::string& name, Logger::level lvl );
            
            /**
             * Determines whether a stack trace should be written.
             *
             * @return true if a stack trace is desired for warn, error, fatal, and note.
             */
            bool askDoStackTrace();

            /**
             * Publishes the log message line to the message broker, if configured.
             *
             * @param line The whole message line.
             *
             */
            void publish( std::string line );

            /**
             * Class to temporariy disable the logging facility during the current scope;
             * this does not (yet) affect legacy logging, although it probably should.
             * @note Caution: do not change the logger setting while an instance of
             * class LogDisabler is active, otherwise the affects are unpredictable.
             */
            
            class MLD_API LogDisabler
            {
            public:
                /** Temporarily disable all logging */
                LogDisabler();
                /** Sets the logging infrastructure back to the previous state */
                ~LogDisabler();
                
            private:
                typedef std::map<std::string, Teamcenter::Logging::Logger::level> LoggerSettings;
                LoggerSettings m_loggerSettings;
            };
            
            friend class LoggerTree::LogDisabler;
            
            /**
             * Class to temporarily disable stack trace generation, if it was enabled
             * via the TC_TRACEBACK variable.
             * This class allows to generate warning or error messages in a context where
             * the stack trace is not of any interest to the user and would eventually slow
             * down the log generation
             */

            class MLD_API LogStacktraceDisabler
            {
            public:
                /** Temporarily disable stack trace generation if enabled */
                LogStacktraceDisabler();
                /** Sets the logging infrastructure back to the previous state */
                ~LogStacktraceDisabler();
            private:
                bool m_enableStackTrace;
            };

            friend class LoggerTree::LogStacktraceDisabler;

        private:
            /**
             * The root node
             */
            Node *root;
            
            /**
             * The tree pointer to store the tree.
             */
            static LoggerTree *tree;
            
            bool doStackTrace;
            int countStackTrace;

            /**
             * Indicates whether to publish log message lines to the message broker
             */
            bool publishing;
            
            
            /** Copy constructor */
            LoggerTree( const LoggerTree& );

            /** Assignment operator */
            LoggerTree& operator=( const LoggerTree& );
            
            /** 
             * Default constructor is private and unused.
             * Use getTree() to obtain the singleton instance.
             */
            LoggerTree();
            
            /** Destructor */
            ~LoggerTree();
            
            /**
             * Tree constructor creates root node and initializes root logger.
             */
            explicit LoggerTree(const std::string& initialLoggerName);
            
            /**
             * given a key this method returns parent logger name.
             *
             * @param[in] key         logger name
             */
            std::string findParent(const std::string& key) const;
            
            /**
             * Retrieves the child node of a given root node and child logger name.
             *
             * @param[in] tempRoot              Node in the tree
             * @param[in] key                   Logger name
             * @param[out] node                 Node in the tree
             */
            void traverse(Node *tempRoot, const std::string& key, Node **node);

            /**
             * This method inserts a child node in the tree
             * whose logger name is same as key
             *
             * @param[in] key                   logger name
             * @param[in] parent                parent logger name
             */
            Node *insertNode(const std::string& key,const std::string& parent);
            
            /**
             * This method sets the log level for the node in the tree.
             *
             * @param[in] key                 logger name
             * @param[in] node                child node
             */
            void setLevel(const std::string& key, Node *node) const;
            
            /**
             * This method checks in the tree whether logger
             * is already created with this name.
             *
             * @param[in] key          logger name
             */
            Node* doesLoggerExists(const std::string &key);
            
            /**
             * Initializes the logger tree using a config file with the given name.
             *
             * @returns true if the file was found and used, false otherwise.
             */
            bool useLoggerConfigFile( const std::string& fileName );
            
            /**
             * Reads each line until end of file, initializing the loggers.
             */
            void readInputFile(std::ifstream &infile);
            
            /**
             * Retrieves the loggers recursively.
             */
            void getLoggers( Node *,std::vector<Logger *> &);
            
            
            bool checkWhetherLevelSetIsValidLevel(const std::string& key) const;
            
            /**
             * Sets log levels for children's children for the same logger.
             */
            void setDeeperChildLoglevels( Node *n );
            
            /**
             * Initializes the tree, reads the config file,
             * creates the default logger objects and sets the
             * log level for the default loggers.
             */
            void initialize();
            
            /**
             * Allows LogDisabler to access members of Node class.
             * <br/>Required due to picky access restrictions of IBM compiler
             */
            Logger* getLoggerOfNode(Node* node) { return node->logger; }
            const std::vector<Node*>& getChildrenOfNode(Node* node) { return node->childNodes; }

        };
    }// namespace Logging
}// namespace Teamcenter

#include <mld/libmld_undef.h>
#endif
