/* Network-Integrated Multimedia Middleware (NMM)
 * http://www.motama.com/nmm.html
 *  
 * Copyright (C) 2006-2007
 * Motama GmbH, Saarbruecken, Germany
 * http://www.motama.com
 *  
 * This file is part of NMM. 
 * 
 * NMM is free software; you can redistribute it and/or modify it    
 * under the terms of the GNU General Public License as published by 
 * the Free Software Foundation; only version 2 of the License.      
 * 
 * NMM is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public 
 * License for more details.  
 * 
 * You should have received a copy of the GNU General Public License 
 * along with NMM; if not, write to the Free Software Foundation,    
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 */



#ifndef NMM_NAMEDOBJECT_HPP
#define NMM_NAMEDOBJECT_HPP

#include "nmm/nmm_prefix.hpp"

#include <string>
#include <iostream>

#include "visibility.hpp"

using namespace std;

/*
 * Macros for Message-System
 * These macros should be used in order to be able
 * to build a 'final' version of the system without messages
 *
 * NMM_*        should be used inside a node
 * NMM_NODE_*   should be used in static methods inside a node
 * NMM_GLOBAL_* should be used in main-functions of example programs
 */

#ifdef _NMM_FINAL_VERSION_
#define ERROR_STREAM(x)
#define DEBUG_STREAM(x)
#define WARNING_STREAM(x)
#define MESSAGE_STREAM(x)
#define NODE_ERROR_STREAM(n,x)
#define NODE_DEBUG_STREAM(n,x)
#define NODE_WARNING_STREAM(n,x)
#define NODE_MESSAGE_STREAM(n,x)
#define GLOBAL_ERROR_STREAM(x)
#define GLOBAL_DEBUG_STREAM(x)
#define GLOBAL_WARNING_STREAM(x)
#define GLOBAL_MESSAGE_STREAM(x)
#endif

#ifndef _NMM_FINAL_VERSION_
#define ERROR_STREAM(x) if(errorStream0()) *errorStream0() << errorString(0) << x << finalErrorString(0);
#define DEBUG_STREAM(x) if(debugStream0()) *debugStream0() << debugString(0) << x << finalDebugString(0);
#define WARNING_STREAM(x) if(warningStream0()) *warningStream0() << warningString(0) << x << finalWarningString(0);
#define MESSAGE_STREAM(x) if(messageStream0()) *messageStream0() << messageString(0) << x << finalMessageString(0);

#define GLOBAL_ERROR_STREAM(x) if (NMM::NamedObject::getGlobalInstance().errorStream0()) *NMM::NamedObject::getGlobalInstance().errorStream0()       << NMM::NamedObject::getGlobalInstance().errorString(0) << x << NMM::NamedObject::getGlobalInstance().finalErrorString(0);
#define GLOBAL_DEBUG_STREAM(x) if (NMM::NamedObject::getGlobalInstance().debugStream0()) *NMM::NamedObject::getGlobalInstance().debugStream0()       << NMM::NamedObject::getGlobalInstance().debugString(0) << x << NMM::NamedObject::getGlobalInstance().finalDebugString(0);
#define GLOBAL_WARNING_STREAM(x) if (NMM::NamedObject::getGlobalInstance().warningStream0()) *NMM::NamedObject::getGlobalInstance().warningStream0() << NMM::NamedObject::getGlobalInstance().warningString(0) << x << NMM::NamedObject::getGlobalInstance().finalWarningString(0);
#define GLOBAL_MESSAGE_STREAM(x) if (NMM::NamedObject::getGlobalInstance().messageStream0()) *NMM::NamedObject::getGlobalInstance().messageStream0() << NMM::NamedObject::getGlobalInstance().messageString(0) << x << NMM::NamedObject::getGlobalInstance().finalMessageString(0);

#define NODE_ERROR_STREAM(n,x) if(n) if(n->errorStream0()) *(n->errorStream0()) << n->errorString(0) << x    << n->finalErrorString(0);
#define NODE_DEBUG_STREAM(n,x) if(n) if(n->debugStream0()) *(n->debugStream0()) << n->debugString(0) << x    << n->finalDebugString(0);
#define NODE_WARNING_STREAM(n,x) if(n) if(n->warningStream0()) *(n->warningStream0()) << n->warningString(0) << x << n->finalWarningString(0);
#define NODE_MESSAGE_STREAM(n,x) if(n) if(n->messageStream0()) *(n->messageStream0()) << n->messageString(0) << x << n->finalMessageString(0);
#endif


#ifdef  _NMM_DISABLE_DEBUG_LEVEL1_
#define ERROR_STREAM1(x)
#define DEBUG_STREAM1(x)
#define WARNING_STREAM1(x)
#define MESSAGE_STREAM1(x)

#define NODE_ERROR_STREAM1(n,x)
#define NODE_DEBUG_STREAM1(n,x)
#define NODE_WARNING_STREAM1(n,x)
#define NODE_MESSAGE_STREAM1(n,x)

#define GLOBAL_ERROR_STREAM1(x)
#define GLOBAL_DEBUG_STREAM1(x)
#define GLOBAL_WARNING_STREAM1(x)
#define GLOBAL_MESSAGE_STREAM1(x)
#endif

#ifndef _NMM_DISABLE_DEBUG_LEVEL1_
#define ERROR_STREAM1(x) if(errorStream1()) *errorStream1() << errorString(1) << x << finalErrorString(1); 
#define DEBUG_STREAM1(x) if(debugStream1()) *debugStream1() << debugString(1) << x << finalDebugString(1); 
#define WARNING_STREAM1(x) if(warningStream1()) *warningStream1() << warningString(1) << x << finalWarningString(1); 
#define MESSAGE_STREAM1(x) if(messageStream1()) *messageStream1() << messageString(1) << x << finalMessageString(1); 
                                                                                           
#define GLOBAL_ERROR_STREAM1(x) if (NMM::NamedObject::getGlobalInstance().errorStream1()) *NMM::NamedObject::getGlobalInstance().errorStream1() << NMM::NamedObject::getGlobalInstance().errorString(1) << x         << NMM::NamedObject::getGlobalInstance().finalErrorString(1);
#define GLOBAL_DEBUG_STREAM1(x) if (NMM::NamedObject::getGlobalInstance().debugStream1()) *NMM::NamedObject::getGlobalInstance().debugStream1() << NMM::NamedObject::getGlobalInstance().debugString(1) << x         << NMM::NamedObject::getGlobalInstance().finalDebugString(1);
#define GLOBAL_WARNING_STREAM1(x) if (NMM::NamedObject::getGlobalInstance().warningStream1()) *NMM::NamedObject::getGlobalInstance().warningStream1() << NMM::NamedObject::getGlobalInstance().warningString(1) << x << NMM::NamedObject::getGlobalInstance().finalWarningString(1);
#define GLOBAL_MESSAGE_STREAM1(x) if (NMM::NamedObject::getGlobalInstance().messageStream1()) *NMM::NamedObject::getGlobalInstance().messageStream1() << NMM::NamedObject::getGlobalInstance().messageString(1) << x << NMM::NamedObject::getGlobalInstance().finalMessageString(1);
                                                                                                                                                                                                                        
#define NODE_ERROR_STREAM1(n,x) if(n) if(n->errorStream1()) *(n->errorStream1()) << n->errorString(1) << x     << n->finalErrorString(1);
#define NODE_DEBUG_STREAM1(n,x) if(n) if(n->debugStream1()) *(n->debugStream1()) << n->debugString(1) << x     << n->finalDebugString(1);
#define NODE_WARNING_STREAM1(n,x) if(n) if(n->warningStream1()) *(n->warningStream1()) << n->warningString(1) << x << n->finalWarningString(1) ;
#define NODE_MESSAGE_STREAM1(n,x) if(n) if(n->messageStream1()) *(n->messageStream1()) << n->messageString(1) << x << n->finalMessageString(1) ;
#endif                                                                                                         


#ifndef _NMM_DISABLE_DEBUG_LEVEL2_
#define ERROR_STREAM2(x) if(errorStream2()) *errorStream2() << errorString(2) << x << finalErrorString(2);
#define DEBUG_STREAM2(x) if(debugStream2()) *debugStream2() << debugString(2) << x << finalDebugString(2);
#define WARNING_STREAM2(x) if(warningStream2()) *warningStream2() << warningString(2) << x << finalWarningString(2);
#define MESSAGE_STREAM2(x) if(messageStream2()) *messageStream2() << messageString(2) << x << finalMessageString(2);
                                                                                           
#define GLOBAL_ERROR_STREAM2(x) if (NMM::NamedObject::getGlobalInstance().errorStream2()) *NMM::NamedObject::getGlobalInstance().errorStream2() << NMM::NamedObject::getGlobalInstance().errorString(2) << x          << NMM::NamedObject::getGlobalInstance().finalErrorString(2);
#define GLOBAL_DEBUG_STREAM2(x) if (NMM::NamedObject::getGlobalInstance().debugStream2()) *NMM::NamedObject::getGlobalInstance().debugStream2() << NMM::NamedObject::getGlobalInstance().debugString(2) << x          << NMM::NamedObject::getGlobalInstance().finalDebugString(2);
#define GLOBAL_WARNING_STREAM2(x) if (NMM::NamedObject::getGlobalInstance().warningStream2()) *NMM::NamedObject::getGlobalInstance().warningStream2() << NMM::NamedObject::getGlobalInstance().warningString(2) << x  << NMM::NamedObject::getGlobalInstance().finalWarningString(2);
#define GLOBAL_MESSAGE_STREAM2(x) if (NMM::NamedObject::getGlobalInstance().messageStream2()) *NMM::NamedObject::getGlobalInstance().messageStream2() << NMM::NamedObject::getGlobalInstance().messageString(2) << x  << NMM::NamedObject::getGlobalInstance().finalMessageString(2);

#define NODE_ERROR_STREAM2(n,x) if(n) if(n->errorStream2()) *(n->errorStream2()) << n->errorString(2) << x        << n->finalErrorString(2);
#define NODE_DEBUG_STREAM2(n,x) if(n) if(n->debugStream2()) *(n->debugStream2()) << n->debugString(2) << x        << n->finalDebugString(2);
#define NODE_WARNING_STREAM2(n,x) if(n) if(n->warningStream2()) *(n->warningStream2()) << n->warningString(2) << x << n->finalWarningString(2) ;
#define NODE_MESSAGE_STREAM2(n,x) if(n) if(n->messageStream2()) *(n->messageStream2()) << n->messageString(2) << x << n->finalMessageString(2) ;
#endif                                                                                                                

#ifdef  _NMM_DISABLE_DEBUG_LEVEL2_
#define ERROR_STREAM2(x)
#define DEBUG_STREAM2(x)
#define WARNING_STREAM2(x)
#define MESSAGE_STREAM2(x)

#define NODE_ERROR_STREAM2(n,x)
#define NODE_DEBUG_STREAM2(n,x)
#define NODE_WARNING_STREAM2(n,x)
#define NODE_MESSAGE_STREAM2(n,x)

#define GLOBAL_ERROR_STREAM2(x)
#define GLOBAL_DEBUG_STREAM2(x)
#define GLOBAL_WARNING_STREAM2(x)
#define GLOBAL_MESSAGE_STREAM2(x)
#endif


namespace NMM {
  /** Wrapper for writing formated messages
   */
  class DLLEXPORT NamedObject {
  public:
    enum Color{DEFAULT_COLOR = 0, RED_COLOR = 1, GREEN_COLOR = 2, ORANGE_COLOR = 3, BLUE_COLOR =4, PINK_COLOR = 5, LBLUE_COLOR = 6, GREY_COLOR = 7, NOCOLOR = 8 };
    enum MessageLevel{LEVEL0 = 0, LEVEL1, LEVEL2, ALL_LEVELS};
    /** Constructor
     */
    NamedObject(const char*);

    /** Constructor
     */
    NamedObject(const string &);

    /** Destructor
     */
    virtual ~NamedObject();


    /** Assign a name to this object
     * @param name of the object
     */
    virtual void setName(const string& name);

    /** Returns the name of this object
     * @return name of the object
     */
    virtual string getName()        const   {return node_name;}

    /** This method returns a pointer to the error stream with message level 0
     * \return ostream* pointer to the level 0 error stream
     */
    inline ostream* errorStream0()   const {return error_streams[0];}

    /** This method returns a pointer to the error stream with message level 1
     * \return ostream* pointer to the level 1 error stream
     */
    inline ostream* errorStream1()   const {return error_streams[1];}

    /** This method returns a pointer to the error stream with message level 2
     * \return ostream* pointer to the level 2 error stream
     */
    inline ostream* errorStream2()   const {return error_streams[2];}


    /** This method returns a pointer to the debug stream with message level 0
     * \return ostream* pointer to the level 0 debug stream
     */
    inline ostream* debugStream0()   const {return debug_streams[0];}

    /** This method returns a pointer to the debug stream with message level 1
     * \return ostream* pointer to the level 1 debug stream
     */
    inline ostream* debugStream1()   const {return debug_streams[1];}

    /** This method returns a pointer to the debug stream with message level 2
     * \return ostream* pointer to the level 2 debug stream
     */
    inline ostream* debugStream2()   const {return debug_streams[2];}


    /** This method returns a pointer to the message stream with message level 0
     * \return ostream* pointer to the level 0 message stream
     */
    inline ostream* messageStream0() const {return message_streams[0];}

    /** This method returns a pointer to the message stream with message level 1
     * \return ostream* pointer to the level 1 message stream
     */
    inline ostream* messageStream1() const {return message_streams[1];}

    /** This method returns a pointer to the message stream with message level 2
     * \return ostream* pointer to the level 2 message stream
     */
    inline ostream* messageStream2() const {return message_streams[2];}


    /** This method returns a pointer to the warning stream with message level 0
     * \return ostream* pointer to the level 0 warning stream
     */
    inline ostream* warningStream0() const {return warning_streams[0];}

    /** This method returns a pointer to the warning stream with message level 1
     * \return ostream* pointer to the level 1 warning stream
     */
    inline ostream* warningStream1() const {return warning_streams[1];}

    /** This method returns a pointer to the warning stream with message level 2
     * \return ostream* pointer to the level 2 warning stream
     */
    inline ostream* warningStream2() const {return warning_streams[2];}

    /** This method returns the error prefix for a specific message level.
     *
     * The string contains the node name and the color
     * \param i The debug level( value between 0..2 ).
     * \return string& returns the error-output prefix for a given message level
     */
    inline const string& errorString(unsigned int i) const {return error_string[i];}

    /** This method returns the debug prefix for a specific message level.
     *
     * The string contains the node name and the color
     * \param i The debug level( value between 0..2 ).
     * \return string& returns the debug-output prefix for a given message level
     */
    inline const string& debugString(unsigned int i) const {return debug_string[i];}

    /** This method returns the message prefix for a specific message level.
     *
     * The string contains the node name and the color
     * \param i The debug level( value between 0..2 ).
     * \return string& returns the message-output prefix for a given message level
     */
    inline const string& messageString(unsigned int i) const {return message_string[i];}

    /** This method returns the warning prefix for a specific message level.
     *
     * The string contains the node name and the color
     * \param i The debug level( value between 0..2 ).
     * \return string& returns the warning-output prefix for a given message level
     */
    inline const string& warningString(unsigned int i) const {return warning_string[i];}

    /** This method returns the postfix string for the i'th warning stream. This string resets the
     * stream to the default_color
     * \param i i'th warning_stream
     * \return string to reset the warning stream
     */
    inline const string& finalWarningString(unsigned int i) const { return warning_final_string[i]; }

    /** This method returns the postfix string for the i'th error stream. This string resets the
     * stream to the default_color
     * \param i i'th error_stream
     * \return string to reset the warning stream
     */
    inline const string& finalErrorString(unsigned int i)   const { return error_final_string[i]; }

    /** This method returns the postfix string for the i'th debug stream. This string resets the
     * stream to the default_color
     * \param i i'th debug_stream
     * \return string to reset the debug stream
     */
    inline const string& finalDebugString(unsigned int i)   const { return debug_final_string[i]; }

    /** This method returns the postfix string for the i'th message stream. This string resets the
     * stream to the default_color
     * \param i i'th debug_stream
     * \return string to message the debug stream
     */
    inline const string& finalMessageString(unsigned int i) const { return message_final_string[i]; }

    /** This method sets the error stream for a given message level
     * \param stream Pointer to the new ostream.
     * \param level The message level
     */
    void setErrorStream(ostream* stream, MessageLevel level)  ;

    /** This method sets the error stream for a given message level
     * \param stream Pointer to the new ostream.
     * \param level The message level
     */
    void setDebugStream(ostream* stream, MessageLevel level)  ;

    /** This method sets the error stream for a given message level
     * \param stream Pointer to the new ostream.
     * \param level The message level
     */
    void setMessageStream(ostream* stream, MessageLevel level);

    /** This method sets the error stream for a given message level
     * \param stream Pointer to the new ostream.
     * \param level The message level
     */
    void setWarningStream(ostream* stream, MessageLevel level);

    /** sets the color for error streams.
     * \param c The new output color
     * \param level The message level
     */
    void setErrorColor(Color c, MessageLevel level)  ;

    /** sets the color for debug streams.
     * \param c The new output color
     * \param level The message level
     */
    void setDebugColor(Color c, MessageLevel level)  ;

    /** sets the color for message streams.
     * \param c The new output color
     * \param level The message level
     */
    void setMessageColor(Color c, MessageLevel level);

    /** sets the color for warning streams.
     * \param c The new output color
     * \param level The message level
     */
    void setWarningColor(Color c, MessageLevel level);
    
    /** returns a reference to the static NamedObject instance
     */
    static NamedObject& getGlobalInstance();

    /** returns the array of error streams
     *
     * @return array of error streams
     */
    inline ostream** getErrorArray()   const {return error_streams;}

    /** returns the array of debug streams
     *
     * @return array of debug streams
     */
    inline ostream** getDebugArray()   const {return debug_streams;}

    /** returns the array of message streams
     *
     * @return array of message streams
     */
    inline ostream** getMessageArray() const {return message_streams;}

    /** returns the array of warning streams
     *
     * @return array of warning streams
     */
    inline ostream** getWarningArray() const {return warning_streams;}

    static const char* color_array[];
  private:

    /** Private constructor for the global instance.
     */
    NamedObject();

    /** configures the prefix string from output message
     */
    void updateStrings();
    
    /** Check if a stream supports text coloring
     * \param stream Pointer to the stream
     * \return True, iff the stream supports text coloring
     */
    static bool isColoredStream(ostream* stream);
    
    string node_name;

    mutable ostream** error_streams;
    mutable ostream** debug_streams;
    mutable ostream** message_streams;
    mutable ostream** warning_streams;

    string* error_color;
    string* debug_color;
    string* warning_color;
    string* message_color;
    
    string* debug_string;
    string* warning_string;
    string* message_string;
    string* error_string;

    string* debug_final_string;
    string* warning_final_string;
    string* message_final_string;
    string* error_final_string;

    bool is_error_owner, is_debug_owner, is_message_owner, is_warning_owner;
    
    static const unsigned int max_debug_level = 3;
    static NamedObject* global_object;
  };
}

#include "nmm/nmm_suffix.hpp"

#endif
