#ifndef LOGGER_H
#define LOGGER_H

#include "log4cpp/Category.hh"
#include "log4cpp/Appender.hh"
#include "log4cpp/FileAppender.hh"
#include "log4cpp/OstreamAppender.hh"
#include "log4cpp/Layout.hh"
#include "log4cpp/BasicLayout.hh"
#include "log4cpp/Priority.hh"
#include "log4cpp/PropertyConfigurator.hh"

#define LOCATION_INFORMATION "     -     -     -     -     -     -     -     -     -     -      LINE: "<< __LINE__ << " FILE: "<<__FILE__

#define LOG_AG_DEBUG(message)  Log::AG.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_AG_INFO(message)   Log::AG.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_AG_WARN(message)   Log::AG.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_AG_ERROR(message)  Log::AG.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_BINDINGS_DEBUG(message)  Log::BINDINGS.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_BINDINGS_INFO(message)   Log::BINDINGS.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_BINDINGS_WARN(message)   Log::BINDINGS.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_BINDINGS_ERROR(message)  Log::BINDINGS.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_CONTEXT_CHILDREN_DEBUG(message)  Log::CONTEXT_CHILDREN.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_CHILDREN_INFO(message)   Log::CONTEXT_CHILDREN.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_CHILDREN_WARN(message)   Log::CONTEXT_CHILDREN.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_CHILDREN_ERROR(message)  Log::CONTEXT_CHILDREN.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_CORE_DEBUG(message)  Log::CORE.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_CORE_INFO(message)   Log::CORE.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_CORE_WARN(message)   Log::CORE.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_CORE_ERROR(message)  Log::CORE.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_CLIENTS_DEBUG(message)  Log::CLIENTS.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_CLIENTS_INFO(message)   Log::CLIENTS.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_CLIENTS_WARN(message)   Log::CLIENTS.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_CLIENTS_ERROR(message)  Log::CLIENTS.errorStream()<<message<< LOCATION_INFORMATION;


#define LOG_CONTEXT_DEBUG(message)  Log::CONTEXT.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_INFO(message)   Log::CONTEXT.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_WARN(message)   Log::CONTEXT.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_ERROR(message)  Log::CONTEXT.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_CONTEXT_ACTIONS_DEBUG(message)  Log::CONTEXT_ACTIONS.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_ACTIONS_INFO(message)   Log::CONTEXT_ACTIONS.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_ACTIONS_WARN(message)   Log::CONTEXT_ACTIONS.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_ACTIONS_ERROR(message)  Log::CONTEXT_ACTIONS.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_CONTEXT_FUNCTIONS_DEBUG(message)  Log::CONTEXT_FUNCTIONS.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_FUNCTIONS_INFO(message)   Log::CONTEXT_FUNCTIONS.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_FUNCTIONS_WARN(message)   Log::CONTEXT_FUNCTIONS.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_FUNCTIONS_ERROR(message)  Log::CONTEXT_FUNCTIONS.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_CONTEXT_VARIABLES_DEBUG(message)  Log::CONTEXT_VARIABLES.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_VARIABLES_INFO(message)   Log::CONTEXT_VARIABLES.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_VARIABLES_WARN(message)   Log::CONTEXT_VARIABLES.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_VARIABLES_ERROR(message)  Log::CONTEXT_VARIABLES.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_CONTEXT_EVENTS_DEBUG(message)  Log::CONTEXT_EVENTS.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_EVENTS_INFO(message)   Log::CONTEXT_EVENTS.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_EVENTS_WARN(message)   Log::CONTEXT_EVENTS.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_CONTEXT_EVENTS_ERROR(message)  Log::CONTEXT_EVENTS.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_COMMANDS_DEBUG(message)  Log::COMMANDS.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_COMMANDS_INFO(message)   Log::COMMANDS.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_COMMANDS_WARN(message)   Log::COMMANDS.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_COMMANDS_ERROR(message)  Log::COMMANDS.errorStream()<<message<< LOCATION_INFORMATION;


#define LOG_DATATABLE_DEBUG(message)  Log::DATATABLE.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_DATATABLE_INFO(message)   Log::DATATABLE.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_DATATABLE_WARN(message)   Log::DATATABLE.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_DATATABLE_ERROR(message)  Log::DATATABLE.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_DEVICE_AGENT_DEBUG(message)  Log::DEVICE_AGENT.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_DEVICE_AGENT_INFO(message)   Log::DEVICE_AGENT.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_DEVICE_AGENT_WARN(message)   Log::DEVICE_AGENT.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_DEVICE_AGENT_ERROR(message)  Log::DEVICE_AGENT.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_OPC_SERVER_DEBUG(message)  Log::OPC_SERVER.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_OPC_SERVER_INFO(message)   Log::OPC_SERVER.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_OPC_SERVER_WARN(message)   Log::OPC_SERVER.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_OPC_SERVER_ERROR(message)  Log::OPC_SERVER.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_PROTOCOL_DEBUG(message)  Log::PROTOCOL.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_PROTOCOL_INFO(message)   Log::PROTOCOL.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_PROTOCOL_WARN(message)   Log::PROTOCOL.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_PROTOCOL_ERROR(message)  Log::PROTOCOL.errorStream()<<message<< LOCATION_INFORMATION;


#define LOG_PROTOCOL_CACHING_DEBUG(message)  Log::PROTOCOL_CACHING.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_PROTOCOL_CACHING_INFO(message)   Log::PROTOCOL_CACHING.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_PROTOCOL_CACHING_WARN(message)   Log::PROTOCOL_CACHING.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_PROTOCOL_CACHING_ERROR(message)  Log::PROTOCOL_CACHING.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_PERFORMANCE_DEBUG(message)  Log::PERFORMANCE.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_PERFORMANCE_INFO(message)   Log::PERFORMANCE.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_PERFORMANCE_WARN(message)   Log::PERFORMANCE.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_PERFORMANCE_ERROR(message)  Log::PERFORMANCE.errorStream()<<message<< LOCATION_INFORMATION;

#define LOG_FAT_RAT_DEBUG(message)  Log::FAT_RAT.debugStream()<<message<< LOCATION_INFORMATION;
#define LOG_FAT_RAT_INFO(message)   Log::FAT_RAT.infoStream()<<message<< LOCATION_INFORMATION;
#define LOG_FAT_RAT_WARN(message)   Log::FAT_RAT.warnStream()<<message<< LOCATION_INFORMATION;
#define LOG_FAT_RAT_ERROR(message)  Log::FAT_RAT.errorStream()<<message<< LOCATION_INFORMATION;


namespace Log
{
static log4cpp::Category &ROOT = log4cpp::Category::getRoot();
static log4cpp::Category &AG = log4cpp::Category::getInstance( std::string("ag") );
static log4cpp::Category &BINDINGS = log4cpp::Category::getInstance( std::string("ag.bindings") );
static log4cpp::Category &CORE = log4cpp::Category::getInstance( std::string("ag.core") );
static log4cpp::Category &CLIENTS = log4cpp::Category::getInstance( std::string("ag.clients") );
static log4cpp::Category &CONTEXT = log4cpp::Category::getInstance( std::string("ag.context") );
static log4cpp::Category &CONTEXT_ACTIONS = log4cpp::Category::getInstance( std::string("ag.context.actions") );
static log4cpp::Category &CONTEXT_CHILDREN = log4cpp::Category::getInstance( std::string("ag.context.children") );
static log4cpp::Category &CONTEXT_FUNCTIONS = log4cpp::Category::getInstance( std::string("ag.context.functions") );
static log4cpp::Category &CONTEXT_VARIABLES = log4cpp::Category::getInstance( std::string("ag.context.variables") );
static log4cpp::Category &CONTEXT_EVENTS = log4cpp::Category::getInstance( std::string("ag.context.events") );
static log4cpp::Category &COMMANDS = log4cpp::Category::getInstance( std::string("ag.commands") );
static log4cpp::Category &DATATABLE = log4cpp::Category::getInstance( std::string("ag.data_table") );
static log4cpp::Category &DEVICE_AGENT = log4cpp::Category::getInstance( std::string("ag.device.agent") );
static log4cpp::Category &PROTOCOL = log4cpp::Category::getInstance( std::string("ag.protocol") );
static log4cpp::Category &PROTOCOL_CACHING = log4cpp::Category::getInstance( std::string("ag.protocol.caching") );
static log4cpp::Category &PERFORMANCE = log4cpp::Category::getInstance( std::string("ag.performance") );

static log4cpp::Category &OPC_SERVER = log4cpp::Category::getInstance( std::string("ag.opc_server") );
static log4cpp::Category &FAT_RAT = log4cpp::Category::getInstance( std::string("ag.fat_rat") );

static  void configure(std::string configurationPath)
{
    try
    {
        log4cpp::PropertyConfigurator::configure(configurationPath.c_str());
    }
    catch( log4cpp::ConfigureFailure &e )
    {
        std::cout<< e.what()<< " [log4cpp::ConfigureFailure catched] while reading "<<configurationPath<<"/logging.log4cpp_properties"<< std::endl;
        exit(1);
    }

    std::vector< log4cpp::Category * > * c = log4cpp::Category::getCurrentCategories();
    std::vector< log4cpp::Category * >::iterator it;
    for(it = c->begin(); it != c->end(); ++it)
    {
        (*it)->setAdditivity(false);
    }
}
}
#endif /* LOGGER_H */
