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

#include <cassert>

#include "ComponentRegistry.h"

#include "logger/LogEngineComponent.h"
#include "process/ThreadHandler.h"
#include "supervisor/Supervisor.h"

//===================================================================

/// The list of ComponentRegistryEntries
ComponentRegistry::ListOfComponentRegistryEntries* ComponentRegistry::List_;

/// Pointer to a Logger
Logger* ComponentRegistry::Logger_;

/// Pointer to the ControlThread
ControlThread* ComponentRegistry::ControlThread_;

/// Initializes the list and logger.
void ComponentRegistry::Initialize( )
{
    List_ = new ListOfComponentRegistryEntries( false );
    Logger_ = new Logger( "ComponentRegistry" );
    ControlThread_ = new ControlThread();
    Insert( ControlThread_ );
}

/// Uninitializes the list.
void ComponentRegistry::Uninitialize()
{
    // called by supervisor uninitialize -- shutdownThreads();
    DeleteAll();
    delete List_;
    delete Logger_;
    delete ControlThread_;
    ControlThread_ = NULL;
}

Handler* ComponentRegistry::Insert( Component* component, bool insertFront )
{
    ComponentRegistryEntry * newEntry = NULL;

    // Check for duplicates (based on pointer .. could have several
    // instances of a component loaded?)
    for( int i = List_->getMinIndex(); i <= List_->getMaxIndex(); ++i )
    {
        if( ( List_->get( i ) )->component_ == component )
        {
            Logger_->syslog( Str( "Attempting to register duplicate component named \"" ) + component->getName() + "\"" );
            return NULL;
        }
    }

    // use the component's preferences to assign it to a handler
    // this can be changed later.
    Handler *handler( NULL );
    SyncComponent* syncComponent( NULL );
    AsyncComponent* asyncComponent( NULL );
    switch( component->getComponentType() )
    {
    case Component::COMPONENT_CONFIG:
        Logger_->syslog( Str( "Loaded Config Component \"" ) + component->getName() );
        newEntry = new ComponentRegistryEntry( component, Component::COMPONENT_CONFIG, NULL );
        break;

    case Component::COMPONENT_BEHAVIOR:
        Logger_->syslog( Str( "Component \"" ) + component->getName() + "\" is being handled by the mission." );
        newEntry = new ComponentRegistryEntry( component, Component::COMPONENT_BEHAVIOR, NULL );
        break;

    case Component::COMPONENT_SYNC:
        syncComponent = dynamic_cast<SyncComponent*>( component );
        if( NULL != syncComponent )
        {
            Logger_->syslog( Str( "SyncComponent \"" ) + syncComponent->getName() + "\" handled in the control thread." );
            newEntry = new ComponentRegistryEntry( component, Component::COMPONENT_SYNC,
                                                   ControlThread_->getSyncHandler() );
            ControlThread_->addComponent( syncComponent );
        }
        else
        {
            Logger_->syslog( Str( "Attempt to add non-component component \"" ) + component->getName() + "\" to the control thread.", Syslog::ERROR );
        }
        break;

    case Component::COMPONENT_ASYNC:
        asyncComponent = dynamic_cast<AsyncComponent*>( component );
        if( NULL != asyncComponent )
        {
            bool isIdle = asyncComponent->isIdlePriority();
            bool isControlThread = ( asyncComponent->getName() == "controlThread" );
            Logger_->syslog( Str( "Component \"" ) + asyncComponent->getName() + "\" handled in its own thread." );
            handler = new ThreadHandler( *asyncComponent, asyncComponent->getName() + " ThreadHandler", isIdle, isControlThread );
            if( NULL != dynamic_cast<LogEngineComponent*>( component ) )
            {
                handler->setAsLogHandler( true );
            }
            newEntry = new ComponentRegistryEntry( component, Component::COMPONENT_ASYNC, handler );
            break;
        }
        else
        {
            Logger_->syslog( Str( "Attempt to add non-component component \"" ) + component->getName() + "\" as async thread.", Syslog::ERROR );
        }

    default:
        Logger_->syslog( Str( "Component \"" ) + component->getName() + "\" doesn't have a component handler preference.", Syslog::ERROR );
        break;
    }

    // And finally, add the new ComponentRegistryEntry to the table(s)
    //insert( make_pair( component->getName(), newEntry ) );
    if( NULL != newEntry )
    {
        if( insertFront == true )
        {
            List_->push( newEntry );
        }
        else
        {
            // The default
            List_->push( newEntry );
        }
    }

    return NULL == newEntry ? NULL : newEntry->handler_;
};

void ComponentRegistry::Remove( Component* component )
{
    // Remove the entry from the list.
    for( int i = List_->getMinIndex(); i <= List_->getMaxIndex(); ++i )
    {
        ComponentRegistryEntry* entry = List_->get( i );
        if( entry->component_ == component )
        {
            List_->pop( i );
            PurgeEntry( entry );
            // Make the assumption it's only in the list once.
            return ;
        }
    }
}

Component* ComponentRegistry::Find( const Str& name )
{
    // Since this isn't a map anymore, this is a more manual process
    for( int i = List_->getMinIndex(); i <= List_->getMaxIndex(); ++i )
    {
        Component* thisComponent = List_->get( i )->component_;
        if( thisComponent->getName() == name )
        {
            return thisComponent;
        }
        else
        {
            //printf( "%s != %s\n", thisComponent->getName().c_str(), name.c_str() );
        }
    }
    return NULL;
}

Handler* ComponentRegistry::FindHandler( const Str& name )
{
    // Since this isn't a map anymore, this is a more manual process
    for( int i = List_->getMinIndex(); i <= List_->getMaxIndex(); ++i )
    {
        ComponentRegistryEntry* entry = List_->get( i );
        Component* thisComponent = entry->component_;
        if( thisComponent->getName() == name )
        {
            return entry->handler_;
        }
        else
        {
            //printf( "%s != %s\n", thisComponent->getName().c_str(), name.c_str() );
        }
    }
    return NULL;
}

Component *ComponentRegistry::PurgeEntry( ComponentRegistryEntry *entry )
{
    // Check for validity of iterator
    Component * component = entry->component_;

    // I guess this means goodbye
    switch( entry->handlerType_ )
    {
    case Component::COMPONENT_CONFIG:
    case Component::COMPONENT_BEHAVIOR:
        // Nothing
        break;
    case Component::COMPONENT_SYNC:
        if( NULL != ControlThread_ )
        {
            SyncComponent* syncComponent = dynamic_cast<SyncComponent*>( component );
            if( NULL != syncComponent )
            {
                ControlThread_->removeComponent( syncComponent );
            }
        }
        break;
    case Component::COMPONENT_ASYNC:
        if( NULL != entry->handler_ )
        {
            // reap the thread
            entry->handler_->shutdown();
        }
        delete entry->handler_;
        break;
    default:
        break; // Nothing
    }

    delete entry;
    return component;
}

size_t ComponentRegistry::Length()
{
    return List_->size();
}

void ComponentRegistry::StartThreads( void )
{
    for( int i = List_->getMinIndex(); i <= List_->getMaxIndex(); ++i )
    {
        ComponentRegistryEntry* entry = List_->get( i );
        if( entry->handler_ != NULL
                && entry->handlerType_ == Component::COMPONENT_ASYNC
                && !entry->handler_->isRunning() )
        {
            //printf( "Running thread %s\n" , entry->component_->getName().cStr());
            entry->handler_->run();
        }
    }
}


void ComponentRegistry::ShutdownThreads( void )
{
    // Go throuh the table and reap any threads (in last-to-first order)
    for( int rev = List_->getMaxIndex(); rev >= List_->getMinIndex(); --rev )
    {
        ComponentRegistryEntry* entry = List_->get( rev );

        if( ( entry->handler_ != NULL )
                && ( entry->handlerType_ == Component::COMPONENT_ASYNC ) )
        {
            Logger_->syslog( "Shutting down " + entry->handler_->getName(), Syslog::INFO );
            entry->handler_->shutdown();
        }
    }
}

void ComponentRegistry::DeleteAll( void )
{
    // Go in reverse order
    while( !List_->isEmpty() )
    {
        Component *component = PurgeEntry( List_->pop() );

        // Catch this special case
        if( component != ControlThread_ )
        {
            delete component;
        }
    }
}

/**
 *  Gets the period of the ControlThread
 *  \return period of the ControlThread, in seconds
 */
const Timespan& ComponentRegistry::GetControlThreadPeriod()
{
    return ControlThread_->getPeriod();
}

/**
 *  Sets the period of the ControlThread
 *  \param period Period of the ControlThread, in seconds
 */
void ComponentRegistry::SetControlThreadPeriod( const Timespan& period )
{
    ControlThread_->setPeriod( period );
}

/**
 *  Returns the number of Entries
 */
unsigned int ComponentRegistry::GetEntryCount( void )
{
    return List_->size();
}

/**
 *  Returns the component at the indexed position
 *  Returns NULL if there is no component at the indicated index
 */
const Component* ComponentRegistry::GetIndexed( const unsigned int index )
{
    ComponentRegistryEntry* registryEntry = List_->get( index );
    return registryEntry == NULL ? NULL : registryEntry->component_;
}


//===================================================================
ComponentRegistry::ComponentRegistryEntry::ComponentRegistryEntry( Component *component,
        Component::ComponentType handlerType,
        Handler *handler )
    : component_( component ),
      handlerType_( handlerType ),
      handler_( handler )
{}


