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

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

SendDataComponent* SendDataComponent::Instance( MissionNode* node, const Str& itemName, MissionItem* parent, const Module* module, Logger& logger )
{
    SendDataComponent* sendDataComp = new SendDataComponent( itemName );
    parent->setComponent( sendDataComp ); // temporary assigment for configuring DataReaders
    const char* serviceStr = node->getAttribute( "Service" );
    if( NULL == serviceStr )
    {
        logger.syslog( "Must specify Service attribute of SendData element", Syslog::CRITICAL );
        return sendDataComp;
    }
    sendDataComp->serviceType_ = Service::Str2ServiceType( serviceStr );
    if( Service::SERVICE_UNSPECIFIED == sendDataComp->serviceType_ )
    {
        logger.syslog( "Invalid Service attribute of SendData element: ", serviceStr, Syslog::CRITICAL );
        return sendDataComp;
    }
    if( sendDataComp->serviceType_ == Service::SERVICE_DIRECT )
    {
        Str destinationStr = node->getAttribute( "Destination" );
        if( destinationStr == Str::EMPTY_STR )
        {
            logger.syslog( "Missing Destination attribute in SendData element with Service=\"Direct\"", Syslog::CRITICAL );
            return sendDataComp;
        }
        sendDataComp->destination_ = new SendDestination( destinationStr, true, logger, Syslog::CRITICAL );
        if( sendDataComp->destination_->hasError() )
        {
            return sendDataComp;
        }
    }
    MissionNode* partNode = node->getFirstChild( true );
    while( NULL != partNode && 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 )
        {
            ElementURI uri( "", name );
            if( !isArg )
            {
                MissionNode* unitNode = partNode->getNextSibling( false );
                const Unit* unit( NULL );
                if( NULL != unitNode )
                {
                    partNode = unitNode;
                    unit = ValueClause::ReadUnit( unitNode, logger );
                    if( NULL != unit )
                    {
                        sendDataComp->dataReaders_.push( Slate::NewReader( uri, sendDataComp, ( *unit )() ) );
                    }
                    else
                    {
                        logger.syslog( Str( "Valid unit must immediately follow URI, not " ) + unitNode->getName(), Syslog::CRITICAL );
                    }
                }
                else
                {
                    logger.syslog( Str( "Valid unit must immediately follow " ) + partNode->getName(), Syslog::CRITICAL );
                }
            }
            else
            {
                DataReader* dataReader = parent->findArgReader( name );
                if( NULL == dataReader )
                {
                    logger.syslog( "No mission argument named " + name, Syslog::CRITICAL );
                }
                else
                {
                    sendDataComp->dataReaders_.push( dataReader );
                }
            }
        }
        else
        {
            logger.syslog( "Slate does not contain " + name, Syslog::CRITICAL );
        }
        partNode = partNode->getNextSibling( true );
    }
    return sendDataComp;
}

SendDataComponent::~SendDataComponent()
{
    if( NULL != destination_ )
    {
        delete destination_;
    }
}

void SendDataComponent::initialize()
{

}

void SendDataComponent::run()
{
    Timestamp now( Timestamp::Now() );
    for( unsigned int i = 0; i < dataReaders_.size(); ++i )
    {
        DataReader* reader = dataReaders_.get( i );

        if( NULL != reader )
        {
            DataEntry dataEntry( reader, reader->getElement().isBest(), true );
            dataEntry.setTimestamp( now );
            if( NULL != destination_ )
            {
                SendDestination* destination = new SendDestination( *destination_ );
                DataValue* dataVal = dataEntry.getDataValue();
                if( NULL != dataVal )
                {
                    logger_.syslog( "Queuing up send data for " + reader->getUri() +
                                    " with destination: " + destination->getScheme() +
                                    ":" + Str( destination->getHostId() ) +
                                    ":" + destination->getPath(), Syslog::INFO );

                    SendData::Push( destination, dataVal->copy(), reader->getDefaultUnit(), true, &logger_ );
                }
                else
                {
                    logger_.syslog( "Failed to send data. " + reader->getUri() + " has no value.", Syslog::ERROR );
                }
            }
            else
            {
                Supervisor::SendData( &dataEntry, serviceType_, destination_ );
            }
        }
    }
}

bool SendDataComponent::runIfUnsatisfied()
{
    run();
    return true;
}

void SendDataComponent::uninitialize()
{

}

SendDataComponent::SendDataComponent( const Str& name )
    : Behavior( name,  NULL, true, true ),
      dataReaders_( false ),
      serviceType_( Service::SERVICE_BULK ),
      destination_( NULL )
{}
