/** \file
 *
 *  Contains the SyslogComponent class implementation.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 */
#include "SyslogComponent.h"

#include "data/DataWriter.h"
#include "data/Slate.h"
#include "missionScript/MissionItem.h"
#include "missionScript/MissionNode.h"
#include "missionScript/ValueClause.h"
#include "supervisor/Supervisor.h"
#include "supervisor/SendData.h"

SyslogComponent* SyslogComponent::Instance( MissionNode* node, const Str& itemName, MissionItem* parent, const Module* module, Logger& logger )
{
    Syslog::Severity severity = Syslog::INFO;
    const char* severityStr = node->getAttribute( "Severity" );
    if( severityStr )
    {
        if( strncmp( severityStr, "None", 4 ) == 0 )
        {
            severity = Syslog::NONE;
        }
        if( strncmp( severityStr, "Debug", 5 ) == 0 )
        {
            severity = Syslog::DEBUG;
        }
        if( strncmp( severityStr, "Info", 4 ) == 0 )
        {
            severity = Syslog::INFO;
        }
        if( strncmp( severityStr, "Error", 5 ) == 0 )
        {
            severity = Syslog::ERROR;
        }
        if( strncmp( severityStr, "Important", 9 ) == 0 )
        {
            severity = Syslog::IMPORTANT;
        }
        if( strncmp( severityStr, "Critical", 7 ) == 0 )
        {
            severity = Syslog::CRITICAL;
        }
        if( strncmp( severityStr, "Fault", 5 ) == 0 )
        {
            severity = Syslog::FAULT;
        }
    }
    Service::ServiceType service = Service::SERVICE_UNSPECIFIED;
    const char* serviceStr = node->getAttribute( "Service" );
    if( strncmp( node->getName(), "SendStr", 7 ) == 0 )
    {
        service = Service::SERVICE_DIRECT;
    }
    else if( serviceStr )
    {
        service = Service::Str2ServiceType( serviceStr );
    }
    SendDestination* destination = NULL;
    if( service == Service::SERVICE_DIRECT )
    {
        Str destinationStr = node->getAttribute( "Destination" );
        if( destinationStr == Str::EMPTY_STR )
        {
            logger.syslog( "Missing Destination attribute with Service=\"Direct\"", Syslog::CRITICAL );
        }
        destination = new SendDestination( destinationStr, true, logger, Syslog::CRITICAL );
    }
    SyslogComponent* syslog = new SyslogComponent( itemName, severity, service, destination );
    parent->setComponent( syslog ); // temporary assigment for configuring DataReaders
    MissionNode* partNode = node->getFirstChild( false );
    while( partNode )
    {
        if( partNode->isElement() )
        {
            Str name;
            bool isArg( false );
            if( strncmp( partNode->getName(), "CustomUri", 9 ) == 0 )
            {
                name = partNode->getAttribute( "Uri" );
            }
            else if( strncmp( partNode->getName(), "Arg", 9 ) == 0 )
            {
                name = partNode->getAttribute( "Name" );
                isArg = true;
            }
            else
            {
                name = ValueClause::StripPastColon( partNode->getName() );
            }

            unsigned short code = Slate::GetElementURICode( name.cStr() );
            if( !isArg && code == ElementURI::NO_CODE && name.length() > 1 && name.cStr()[ 0 ] == '_' && name.cStr()[ 1 ] == '.' )
            {
                // This is a new scratchpad variable
                ElementURI uri( "", name );
                const Unit* unit = NULL;
                MissionNode* unitNode = partNode->getNextSibling( true );
                if( NULL != unitNode )
                {
                    unit = ValueClause::ReadUnit( unitNode, logger );
                }
                unit = unit == NULL ? &uri.getUnit() : unit;
                DataElement* dataElement = new SimpleDataElement( uri, unit->dataValue( uri.getBinaryType() ), 0 );
                Slate::MapDataElement( &uri, dataElement );
                ElementURI* regURI = Slate::FindElementURI( uri );
                code = regURI == NULL ? ElementURI::NO_CODE : regURI->getCode();
            }
            if( isArg || code != ElementURI::NO_CODE )
            {
                ElementURI uri( "", name );
                partNode = partNode->getNextSibling( false );
                const Unit* unit( NULL );
                if( NULL != partNode )
                {
                    unit = ValueClause::ReadUnit( partNode, logger );
                    if( NULL != unit )
                    {
                        DataReader* dataReader;
                        if( !isArg )
                        {
                            dataReader = Slate::NewReader( uri, syslog, ( *unit )() );
                        }
                        else
                        {
                            dataReader = parent->findArgReader( name );
                        }
                        syslog->addPart( new SyslogPart( dataReader, unit ) );
                    }
                    else
                    {
                        logger.syslog( Str( "Valid unit must immediately follow URI, not " ) + partNode->getName(), Syslog::ERROR );
                    }
                }
                else
                {
                    logger.syslog( "Valid unit must immediately follow URI.", Syslog::ERROR );
                }
            }
            else
            {
                logger.syslog( "Slate does not contain " + name, Syslog::ERROR );
            }
        }
        else if( partNode->isText() )
        {
            syslog->addPart( new SyslogPart( new Str( partNode->getTextValue() ) ) );
        }
        if( NULL != partNode )
        {
            partNode = partNode->getNextSibling( false );
        }
    }
    return syslog;
}

SyslogComponent::~SyslogComponent()
{
    // syslogParts_ takes care of itself
}

void SyslogComponent::addPart( SyslogPart* syslogPart )
{
    syslogParts_.push( syslogPart );
}

void SyslogComponent::initialize()
{

}

void SyslogComponent::run()
{
    //logger_.syslog( "Run", Syslog::INFO );
    Str outputStr;
    for( unsigned int i = 0; i < syslogParts_.size(); ++i )
    {
        SyslogPart* syslogPart = syslogParts_[i];

        switch( syslogPart->getSyslogPartType() )
        {
        case SYSLOG_PART_TYPE_STR:
            outputStr += *( syslogPart->getStrPart() );
            break;
        case SYSLOG_PART_TYPE_DATAREADER:
            outputStr += " ";
            if( NULL != syslogPart->getDataReaderPart() )
            {
                outputStr += syslogPart->getDataReaderPart()->asString( *( syslogPart->getUnit() ) ); // TODO: implement sensible rounding and display precision here
            }
            else
            {
                outputStr += "<Undefined>";
            }
            outputStr += " ";
            outputStr += syslogPart->getUnit()->getAbbreviation();
            outputStr += " ";
            break;
        }
    }
    logger_.syslog( outputStr, severity_, serviceType_ );
    if( NULL  != destination_ )
    {
        StrValue* dv = new StrValue( outputStr );
        SendData::Push( destination_, dv, NULL, true );
    }
}

bool SyslogComponent::runIfUnsatisfied()
{
    //logger_.syslog( "RunIfUnatisfied", Syslog::INFO );
    run();
    return true;
}

void SyslogComponent::uninitialize()
{

}

SyslogComponent::SyslogComponent( const Str& name, Syslog::Severity severity, Service::ServiceType serviceType, SendDestination* destination )
    : Behavior( name,  NULL, true, true ),
      syslogParts_( true ),
      severity_( severity ),
      serviceType_( serviceType ),
      destination_( destination )
{}
