/** \file
 *
 *  Contains the Maintainer class implementation.
 *
 *  Copyright (c) 2007,2008,2009 MBARI
 *  MBARI Proprietary Information.  All Rights Reserved
 *
 */

#include "Maintainer.h"

#include "component/ComponentRegistry.h"
#include "data/ElementURI.h"
#include "data/Slate.h"
#include "units/Units.h"

// Constructor
Maintainer::Maintainer()
    : maintainArray_( false )
{}

// add a uriCode for the maintainer to maintain
void Maintainer::addMaintain( unsigned short int uriCode, DataValue* dataValue,
                              SyncComponent::CycleOrder cycleOrder, bool unavailable, bool invalid )
{
    MutexLocker mutexLocker( mutex_ );
    MaintainItem *mi;
    unsigned int i;

    if( ElementURI::NO_CODE == uriCode )
    {
        return;
    }

    // see if this uriCode already exist, if so
    // then we will change the MaintainItem accordinglyCYCLE_MAINTAINER
    for( i = 0; i < maintainArray_.size(); ++i )
    {
        mi = maintainArray_.get( i );
        if( uriCode == mi->getUriCode() )
        {
            mi->setDataValue( dataValue );
            mi->setUnavailable( unavailable );
            mi->setInvalid( invalid );
            return;
        }
    }
    MaintainItem* item = new MaintainItem( uriCode, dataValue, cycleOrder, unavailable, invalid );
    maintainArray_.push( item );
    ComponentRegistry::Insert( item );
}

// removes the uriCode from being maintained
void Maintainer::removeMaintain( unsigned short int uriCode )
{
    MutexLocker mutexLocker( mutex_ );
    MaintainItem *mi;
    unsigned int i;

    // go through our list of maintain items and
    // find the item to remove and then pop it
    // off our list
    if( maintainArray_.size() > 0 )
    {
        for( i = maintainArray_.size() - 1; ; --i )
        {
            mi = maintainArray_.get( i );
            if( uriCode == ElementURI::NO_CODE || uriCode == mi->getUriCode() )
            {
                maintainArray_.pop( i );
                printf( "Removing uriCode = %d\n", mi->getUriCode() );
                ComponentRegistry::Remove( mi );
                //delete mi;
                if( uriCode != ElementURI::NO_CODE )
                {
                    return;
                }
            }
            if( i == 0 )
            {
                break;
            }
        }
    }
}

// removes all the maintain items so nothing will be maintained
void Maintainer::removeAllMaintains()
{
    removeMaintain( ElementURI::NO_CODE );
}

// lists the uriCodes being maintained
void Maintainer::listMaintains()
{
    MutexLocker mutexLocker( mutex_ );
    MaintainItem *mi;
    unsigned int i;

    // go through our list of maintain items and
    // tell each item to maintain its status
    for( i = 0; i < maintainArray_.size(); ++i )
    {
        mi = maintainArray_.get( i );
        mi->maintainStatus();
    }
}

// Constructor
Maintainer::MaintainItem::MaintainItem( unsigned short int uriCode, DataValue* dataValue,
                                        SyncComponent::CycleOrder cycleOrder, bool unavailable, bool invalid )
    : SyncMaintainerComponent( makeName( uriCode ), NULL, cycleOrder ),
      uriCode_( uriCode ),
      dataValue_( dataValue ),
      unavailable_( unavailable ),
      invalid_( invalid )
{
    dataWriter_ = NULL;
    initDataWriter();	// try to init the dataReader
}

Str Maintainer::MaintainItem::makeName( unsigned short int uriCode )
{
    Str* uri = Slate::GetElementURI( uriCode );
    if( NULL == uri )
    {
        return "Maintain";
    }
    return "Maintain_" + *uri;
}

// returns the uriCode of a maintain item
unsigned short int Maintainer::MaintainItem::getUriCode()
{
    return uriCode_;
}

// returns the data value to be maintained
DataValue* Maintainer::MaintainItem::getDataValue()
{
    return dataValue_;
}

// sets the data value to be maintained
void Maintainer::MaintainItem::setDataValue( DataValue* dataValue )
{
    if( NULL != dataValue_ )
    {
        delete dataValue_;
    }
    dataValue_ = dataValue;
}

// maintains the items string value as a string
bool Maintainer::MaintainItem::maintain()
{
    if( NULL != dataValue_ )
    {
        return dataWriter_->write( *dataValue_ );
    }
    if( unavailable_ && NULL != dataElement_  && NULL != dataWriter_ )
    {
        return dataElement_->setUnavailable( true, dataWriter_ );
    }
    if( invalid_ && NULL != dataElement_  && NULL != dataWriter_ )
    {
        return dataElement_->setInvalid( true, dataWriter_ );
    }
    return false;
}

void Maintainer::MaintainItem::maintainStatus( )
{
    ElementURI *elementUri;
    Str output;

    // get the name
    elementUri = Slate::GetElementURI( uriCode_ );
    if( NULL == elementUri )
    {
        return;
    }
    output = *elementUri + " maintaining ";
    if( NULL != dataValue_ )
    {
        output += dataValue_->toString();
    }
    if( unavailable_ )
    {
        output += " unavailable";
    }
    if( invalid_ )
    {
        output += " invalid";
    }
    logger_.syslog( output, Syslog::INFO );
}

// initializes the data reader for the maintain item.
// if the item has never been logged in the slate then
// its data element does not exist.  So, we will
// keep trying to get a data reader until we get it.
bool Maintainer::MaintainItem::initDataWriter()
{

    // is the dataReader already initialized?
    if( NULL != dataWriter_ )
    {
        return true;
    }

    // does there exist a dataElement for this uricode?
    dataElement_ = Slate::GetElement( uriCode_ );
    if( NULL == dataElement_ )
    {
        return false;
    }
    // get the data reader if there is a dataElement
    dataWriter_ = new DataWriter( this, *dataElement_, NULL, Logger::TIME_PRECISION_MILLIS );
    return true;
}

// Destructor
Maintainer::MaintainItem::~MaintainItem()
{
    // cleanup
    if( NULL != dataWriter_ )
    {
        delete dataWriter_;
    }
    if( NULL != dataValue_ )
    {
        delete dataValue_;
    }
}


