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

#include "Supervisor.h"

#include <cerrno>
#include <cstring>
#include <dirent.h>
#include <limits.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>

#include "component/ComponentRegistry.h"
#include "component/BehaviorRegistry.h"
#include "data/Slate.h"
#include "data/SimSlate.h"
#include "data/LcmInstance.h"
#include "io/LzmaEncoder.h"
#include "io/NavChartDb.h"
#include "io/ZipEncoder.h"
#include "logger/Reporter.h"
#include "logger/BinaryLogWriter.h"
#include "logger/DecimationLogManager.h"
#include "logger/DirectoryLogWriter.h"
#include "logger/FileLogWriter.h"
#include "logger/SplitFileLogWriter.h"
#include "logger/KmlLogWriter.h"
#include "logger/LinearApproximationLogWriter.h"
#include "logger/LogEngine.h"
#include "logger/LogEngineComponent.h"
#include "logger/LogSplitterComponent.h"
#include "logger/TextLogWriter.h"
#include "missionScript/MissionItem.h"
#include "missionScript/MissionManager.h"
#include "module/Config.h"
#include "module/ModuleLoader.h"
#include "process/Handler.h"
#include "supervisor/CycleStarter.h"
#include "supervisor/SendData.h"
#include "utils/AuvMath.h"
#include "VehicleIF.h"
#include <sys/syscall.h>

/// Flag set when it's time to restart
bool Supervisor::Restart_( false );

/// Arguments passed to main when restarting
int Supervisor::Argc_( 0 );
char** Supervisor::Argv_( NULL );
char* Supervisor::Args_( NULL );
bool Supervisor::WasRunning_( false );
bool Supervisor::RunSimsInRealTime_( false );

Supervisor* Supervisor::Instance_( NULL );

void Supervisor::SetArgs( const char* args )
{
    ClearArgs();
    unsigned int len = strlen( args );
    Args_ = new char[len + 1];
    Args_[len] = 0;
    bool quoteMode = false;
    Argc_ = 1;
    for( unsigned int i = 0; i < len; ++i )
    {
        if( ( i == 0 || ( args[i - 1] == ' ' && !quoteMode ) ) && args[i] != ' ' )
        {
            ++Argc_;
        }
        Args_[i] = args[i] != '\"' && ( quoteMode || args[i] != ' ' ) ? args[i] : 0;
        quoteMode ^= args[i] == '\"';
    }
    Argv_ = new char*[Argc_];
    unsigned int argi = 0;
    Argv_[argi++] = NULL;
    for( unsigned int i = 0; i < len; ++i )
    {
        if( ( i == 0 || Args_[i - 1] == 0 ) && Args_[i] != 0 )
        {
            Argv_[argi++] = Args_ + i;
        }
    }
}

void Supervisor::ClearArgs()
{
    if( NULL != Argv_ )
    {
        delete[] Argv_;
        Argv_ = NULL;
    }
    if( NULL != Args_ )
    {
        delete[] Args_;
        Args_ = NULL;
    }
    Argc_ = 0;
}

/// Forces an entry into the shore log.
void Supervisor::SendData( DataEntry* dataEntry, Service::ServiceType serviceType, SendDestination* destination )
{
    if( NULL != destination )
    {
        SendData::Push( destination, dataEntry->getDataValue()->copy(), &dataEntry->getDataValue()->getUnit(), true );
    }
    else
    {
        Instance_->decimationLogManager_->forceWrite( dataEntry, serviceType );
    }
}

/// Indicates if mission manager is failed
bool Supervisor::IsMissionManagerFailed()
{
    return Instance_ == NULL || Instance_->missionManager_ == NULL
           || Instance_->missionManager_->isFailed();
}

/// Indicates if a loaded mission is running
bool Supervisor::IsMissionRunning()
{
    return Instance_ != NULL && Instance_->missionManager_ != NULL
           && Instance_->missionManager_->isMissionRunning();
}

/// If there is a file at Data/running, set WasRunning_ to true;
/// Otherwise, just create file.
void Supervisor::TouchRunningFile()
{
    int error = mkdir( "Data", ACCESSPERMS );
    if( error && EEXIST != errno )
    {
        printf( "Error opening Data directory due to error %d: %s\n", errno, strerror( errno ) );
    }
    FILE* f = fopen( "Data/running", "r" );

    if( NULL == f )
    {
        f = fopen( "Data/running", "w" );
        WasRunning_ = false;
    }
    else
    {
        WasRunning_ = true;
    }
    if( NULL != f )
    {
        fclose( f );
    }
}

/// As part of orderly shutdown, delete the file at Data/running
void Supervisor::ClearRunningFile()
{
    remove( "Data/running" );
    Timespan( 1.0 ).sleepFor();
}

/// Queue up the sbd at the indicated address to be resent
void Supervisor::QueueSBD( const char* path )
{
    size_t len = strlen( path );
    char* newPath = new char[len + 1];
    strncpy( newPath, path, len );
    newPath[len] = 0;
    Instance_->sbdQueue_.setDoArrayDelete( true );
    Instance_->sbdQueue_.push( newPath );
}

/// Get the path to next SBD to be resent, if any
/// Delete the pointer after use!
char* Supervisor::GetQueuedSBD()
{
    return Instance_->sbdQueue_.pop( 0 );
}

/// Call upon DecimationLogManager to configure automatic
/// Logging of data.
void Supervisor::ConfigureDecimation( Service::ServiceType serviceType,
                                      DecimationLogWriter::DecimationType decimationType,
                                      const Str& name, DataValue* parameter, Logger& logger )
{
    Instance_->decimationLogManager_->configLog(
        serviceType, decimationType, name, parameter, logger );
}


/// Constructor
Supervisor::Supervisor( int argc, char **argv, Str& dataDir )
    : logEngine_( NULL ),
      logger_( "Supervisor" ),
      argc_( argc ),
      argv_( argv ),
      loadedModules_( false ),
      moduleLoader_( loadedModules_ ),
      commandExecHandler_( NULL ),
      commandLineHandler_( NULL ),
      missionManager_( NULL ),
      maintainer_( NULL ),
      reporter_( NULL ),
      running_( false ),
      dataDir_( dataDir ),
      sbdQueue_( true ),
      decimationLogManager_( NULL ),
      syslogFileWriter_( NULL ),
      slateWriter_( NULL ),
      slateFileWriter_( NULL ),
      slateDirFileWriter_( NULL ),
      kmlFileWriter_( NULL ),
      kmlApproximationWriter_( NULL )

{
    Instance_ = this;
}

/// Destructor
Supervisor::~Supervisor()
{
    Instance_ = NULL;
}

/// Fill a little time
void Supervisor::initialize( void )
{
    SetRestart( false );

    // Create the logger component and push it onto the list of components later.
    logEngine_ = new LogEngine( );

    slateWriter_ = new DirectoryLogWriter();
    slateFileWriter_ = new SplitFileLogWriter( dataDir_ + "/slate", 10 * 1000 * 1000, slateWriter_ );
    syslogFileWriter_ = new FileLogWriter( dataDir_ + "/syslog", new TextLogWriter(), true );
    slateDirFileWriter_ = new FileLogWriter( dataDir_ + "/slate.dir", new DirectoryLogWriter(), true );
    decimationLogManager_ = new DecimationLogManager( *logEngine_, dataDir_ );

    logEngine_->addAssociation(
        syslogFileWriter_,
        new OrRule(
            new EventTypeRule( EventEntry::COMPONENT_ACTION ),
            new SyslogSeverityRule( Syslog::DEBUG ) ) );

    // Creates a combined text+binary log for debugging binary log issues.
    /*
    FileLogWriter* debugWriter = new FileLogWriter( dataDir_ + "/debug", new BinaryLogWriter( true, true ) );
    logEngine_->addAssociation(
        debugWriter,
        new OrRule(
            new TypeRule( LogEntry::EVENT_LOG_ENTRY ),
            new TypeRule( LogEntry::DIRECTORY_LOG_ENTRY ),
            new SyslogSeverityRule( Syslog::DEBUG ),
            new DataWriteRule( true ) ) );
    */

    logEngine_->addAssociation(
        slateFileWriter_,
        new OrRule(
            new TypeRule( LogEntry::EVENT_LOG_ENTRY ),
            new TypeRule( LogEntry::DIRECTORY_LOG_ENTRY ),
            new SyslogSeverityRule( Syslog::DEBUG ),
            new DataWriteRule( true ) ) );

    logEngine_->addAssociation(
        slateDirFileWriter_,
        new TypeRule( LogEntry::DIRECTORY_LOG_ENTRY ) );

    logger_.syslog( "Initializing supervisor." );

    // Start up the missionComponent creator registry
    BehaviorRegistry::Initialize();

    // Start up the static component registry
    ComponentRegistry::Initialize();

    CycleStarter* cycleStarter = new CycleStarter();
    ComponentRegistry::Insert( cycleStarter, true );

    CommandExec* commandExec = new CommandExec( *this );
    commandExecHandler_ = ComponentRegistry::Insert( commandExec );

    CommandLine* commandLine = new CommandLine( *this );
    commandLineHandler_ = ComponentRegistry::Insert( commandLine );

    logEngine_->addAssociation(
        new TextLogWriter( *commandLine ),
        new SyslogSeverityRule( Syslog::INFO ) );

    LogEngineComponent* logEngineComponent = new LogEngineComponent( logEngine_, "logger" );
    ComponentRegistry::Insert( logEngineComponent );

    LogSplitterComponent* logSplitterComponent = new LogSplitterComponent();
    ComponentRegistry::Insert( logSplitterComponent );

    // ControlThread is now created by ComponentRegistry (who needs to know about
    // it in any case to pass other components to it)

    // Deal with command line inputs
    for( int i = 1; i < argc_; ++i )
    {
        if( i < argc_ - 1 && strcasecmp( argv_[ i ], "-c" ) == 0 )
        {
            altConfig_ = Str( argv_[ i + 1 ] ) + "/";
        }
        else if( strcasecmp( argv_[ i ], "-r" ) == 0 )
        {
            RunSimsInRealTime_ = true;
        }
    }

    // Load up config files, configure loggers
    configure();

    // Load up modules
    moduleLoader_.loadModules( "Modules/" );

    // Get ready to run a mission
    missionManager_ = new MissionManager();
    ComponentRegistry::Insert( missionManager_ );

    // Create Maintainer
    maintainer_ = new Maintainer();
    //ComponentRegistry::Insert( maintainer_ );

    // Create Reporter
    reporter_ = new Reporter();
    ComponentRegistry::Insert( reporter_ );

    // Start up NavChart Database
    NavChartDb* navChartDb = new NavChartDb();
    ComponentRegistry::Insert( navChartDb );

    // Start KML logging
    StrValue vehicleName;
    if( ! Slate::ReadOnce( VehicleIF::NAME_CFG, vehicleName, logger_, Syslog::FAULT ) )
    {
        vehicleName.setString( "Unknown" );
    }
    kmlFileWriter_ = new FileLogWriter( dataDir_ + "/slate.kml", new KmlLogWriter( vehicleName.toString( Units::NONE ) ), true, new ZipEncoder( ".kmz" ) );

    kmlApproximationWriter_ = new LinearApproximationLogWriter(
        kmlFileWriter_,
        3,
        KmlLogWriter::GetKmlArgs(),
        KmlLogWriter::GetKmlUnits(),
        KmlLogWriter::GetKmlErrors(),
        true );

    logEngine_->addAssociation(
        kmlApproximationWriter_,
        new OrRule(
            new EventTypeRule( EventEntry::START_CYCLE ),
            new DataWriteRule( true ),
            new SyslogSeverityRule( Syslog::ERROR ) ) );

    logger_.syslog( "Main Thread ID is ", ( int )syscall( SYS_gettid ), Syslog::INFO );

}


// Kick off the command line
void Supervisor::startCommandLine()
{
    commandExecHandler_->run();
    commandLineHandler_->run();
}

/// This is the guts of the supervisor.
/// We run this only once.
void Supervisor::run()
{
    logger_.syslog( "Running supervisor." );
    running_ = true;

    startCommandLine();

    // Deal with "set" command line "-x" inputs
    for( int i = 1; i < argc_ - 1; ++i )
    {
        if( strcasecmp( argv_[ i ], "-x" ) == 0 && strncasecmp( argv_[ i + 1 ], "set ", 4 ) == 0 )
        {
            CommandExec::DoCommand( argv_[ i + 1 ], true );
        }
    }

    // Kick off any independent threads (including control thread)
    ComponentRegistry::StartThreads();

    // Deal with command line "-x" inputs
    for( int i = 1; i < argc_ - 1; ++i )
    {
        if( strcasecmp( argv_[ i ], "-x" ) == 0 )
        {
            CommandExec::DoCommand( argv_[ i + 1 ], false );
        }
    }

    while( running_ )
    {
        // Probably should use a mutex or something instead of this sleepFor
        Timespan::Seconds( 0.2 ).sleepFor();
    }
}

// And on the way out
void Supervisor::uninitialize( void )
{
    logger_.syslog( "Uninitializing supervisor and starting cleanup.  Bye!" );

    // Give all components a chance to uninitialize
    ComponentRegistry::ShutdownThreads();

    // Clean up LCM -- for some reason standard static cleanup isn't occurring
    // when LcmInstance_ is on the stack. So we put it on the heap and cleanup here.
    LcmInstance::Uninitialize();

    // Clear the list of loadedModules in reverse order of creation
    // with the expectaion that modules will unregister
    // themselves from the component register and delete
    // any components...
    while( !loadedModules_.isEmpty() )
    {
        LoadedModule * thisLoadedModule = loadedModules_.pop( 0 );
        delete thisLoadedModule;
    }

    /// Delete any components which are left
    /// (most modules should have destroyed their own components...)
    ComponentRegistry::Uninitialize();

    // Maintainer is no longer a component
    delete maintainer_;

    // Wind down the missionComponent creator registry
    BehaviorRegistry::Uninitialize();

    /// Wrap up the logs
    delete decimationLogManager_;
    DirectoryLogWriter::Uninitialize();

    /// Wrap up the persisted config settings
    Config::Uninitialize();

    /// Wrap up the entries
    Slate::Uninitialize();
    SimSlate::Uninitialize();


}


/// Closes the current "toShore" file and begins a new one.
void Supervisor::IncrementShoreFile()
{
    if( NULL != Instance_ )
    {
        if( NULL != Instance_->decimationLogManager_ )
        {
            Instance_->decimationLogManager_->writeFooters();
            Instance_->decimationLogManager_->splits();
            Instance_->decimationLogManager_->writeHeaders();
        }
    }
}

/// Create a new directory in Logs and begin writing to it.
Str Supervisor::RestartLogs()
{
    Str dataDir = GenerateDataDir();
    Instance_->dataDir_ = dataDir;

    // This call may make the other locks unnecessary.
    Instance_->logEngine_->lockProcessing();

    //Instance_->decimationLogManager_->lockWrites();
    Instance_->decimationLogManager_->writeFooters();
    Instance_->decimationLogManager_->resetFileBases();
    Instance_->decimationLogManager_->resetDirectoryEntries();
    Instance_->decimationLogManager_->writeHeaders();
    //Instance_->decimationLogManager_->flush();
    //Instance_->decimationLogManager_->unlockWrites();

    Instance_->slateFileWriter_->resetFileBase( dataDir + "/slate" );
    Instance_->slateWriter_->resetTimeBase();
    Instance_->syslogFileWriter_->resetFilename( dataDir + "/syslog" );
    Instance_->slateDirFileWriter_->resetFilename( dataDir + "/slate.dir" );

    Instance_->kmlApproximationWriter_->lockWrites();
    Instance_->kmlApproximationWriter_->writeFooter();
    Instance_->kmlFileWriter_->resetFilename( dataDir + "/slate.kml" );
    Instance_->kmlApproximationWriter_->writeHeader();
    Instance_->kmlApproximationWriter_->forceWriteRow();
    Instance_->kmlApproximationWriter_->unlockWrites();

    Instance_->logEngine_->unlockProcessing();

    return dataDir;
}

/// Generates a new data directory and returns its name
Str Supervisor::GenerateDataDir( const Str& logSymlinkSubdir )
{
    Str dataSubdir = Timestamp::Now().toSmallString();

    int error = mkdir( "Logs", ACCESSPERMS );
    if( error && EEXIST != errno )
    {
        printf( "Error opening Log directory due to error %d: %s\n", errno, strerror( errno ) );
        return "";
    }

    Str dataDir = "Logs/" + dataSubdir;

    error = mkdir( dataDir.cStr(), ACCESSPERMS );
    if( error && EEXIST != errno )
    {
        printf( "Error opening Log directory %s due to error %d: %s\n", dataDir.cStr(), errno, strerror( errno ) );
        return "";
    }
    error = unlink( "Logs/previous" );
    if( 0 != error && ENOENT != errno )
    {
        printf( "Error removing Log directory symlink due to error %d: %s.\n", errno, strerror( errno ) );
    }
    else
    {
        error = rename( "Logs/latest", "Logs/previous" );
        if( 0 != error && ENOENT != errno )
        {
            printf( "Error moving 'latest' Log directory symlink due to error %d: %s.\n", errno, strerror( errno ) );
        }
        else
        {
            error = symlink( dataSubdir.cStr(), "Logs/latest" );
            if( error && EEXIST != errno )
            {
                printf( "Error creating Log directory symlink due to error %d: %s\n", errno, strerror( errno ) );
            }
        }
    }

    Str logSymlink = logSymlinkSubdir.length() == 0 ? "" : "Logs/" + logSymlinkSubdir;

    if( logSymlink.length() > 0 )
    {
        error = unlink( logSymlink.cStr() );
        if( 0 != error && ENOENT != errno )
        {
            printf( "Error removing Log directory symlink due to error %d: %s.\n", errno, strerror( errno ) );
        }
        else
        {
            error = symlink( dataSubdir.cStr(), logSymlink.cStr() );
            if( error )
            {
                printf( "Error creating Log directory symlink due to error %d: %s\n", errno, strerror( errno ) );
            }
        }
    }
    return dataDir;
}

/// Gets the name of the latest "toShore" file
Str Supervisor::GetToShoreFilename( Service::ServiceType serviceType )
{
    Str shoreFileName( Str::EMPTY_STR );

    struct dirent **logsDirList;
    int nLogDirs = scandir( "Logs", &logsDirList, FilterLogDir, alphasort );
    if( nLogDirs < 0 )
    {
        return shoreFileName;
    }
    for( int iLogDir = 0; iLogDir < nLogDirs; ++iLogDir )
    {
        if( shoreFileName == Str::EMPTY_STR )
        {
            Str logPath = Str( "Logs/" ) + logsDirList[iLogDir]->d_name;
            struct dirent **dirList;
            int nShore = scandir( logPath.cStr(), &dirList, GetServiceFilter( serviceType, false ), alphasort );
            if( nShore > 0 )
            {
                LzmaEncoder encoder;
                for( int iShore = 0; iShore < nShore; ++iShore )
                {
                    Str shoreName( logPath + "/" + dirList[iShore]->d_name );
                    if( shoreName != GetActiveShoreFile( serviceType ) )
                    {
                        encoder.encode( shoreName.cStr() );
                    }
                    free( dirList[iShore] );
                }
                free( dirList );
            }
            int nLzma = scandir( logPath.cStr(), &dirList, GetServiceFilter( serviceType, true ), alphasort );
            if( nLzma > 0 )
            {
                for( int iLzma = 0; iLzma < nLzma; ++iLzma )
                {
                    if( shoreFileName == Str::EMPTY_STR )
                    {
                        shoreFileName = logPath + "/" + dirList[iLzma]->d_name;

                        // But skip if the source (.bak) file is only 8 bytes.
                        // Such files contain only a header and do not need to be sent to shore.
                        Str bakFileName = shoreFileName;
                        bakFileName.setInside( ".bak\x00", shoreFileName.length() - 5, 5 );
                        struct stat st;
                        stat( bakFileName.cStr(), &st );
                        if( st.st_size == 8 )
                        {
                            Str renameFilenameStr  =  shoreFileName + ".bak";
                            rename( shoreFileName.cStr(), renameFilenameStr.cStr() );
                            shoreFileName = Str::EMPTY_STR;
                        }
                    }
                    free( dirList[iLzma] );
                }
                free( dirList );
            }
        }
        free( logsDirList[iLogDir] );
    }
    free( logsDirList );

    return shoreFileName;
}

/// Gets the name of the latest Log file that matches the filter
Str Supervisor::GetLatestLogFilename( FileFilterType fileFilter )
{
    Str filename( Str::EMPTY_STR );

    struct dirent **logsDirList;
    int nLogDirs = scandir( "Logs", &logsDirList, FilterLogDir, alphasort );
    if( nLogDirs < 0 )
    {
        return filename;
    }
    for( int iLogDir = nLogDirs - 1; iLogDir >= 0; --iLogDir )
    {
        if( filename == Str::EMPTY_STR )
        {
            Str logPath = Str( "Logs/" ) + logsDirList[iLogDir]->d_name;
            struct dirent **dirList;
            int nFiles = scandir( logPath.cStr(), &dirList, fileFilter, alphasort );
            if( nFiles > 0 )
            {
                for( int iFile = 0; iFile < nFiles; ++iFile )
                {
                    if( filename == Str::EMPTY_STR )
                    {
                        filename = logPath + "/" + dirList[iFile]->d_name;
                    }
                    free( dirList[iFile] );
                }
                free( dirList );
            }
        }
        free( logsDirList[iLogDir] );
    }
    free( logsDirList );

    return filename;
}

// Load up config files
void Supervisor::configure()
{
    Config::LoadConfigFiles( "Config/", altConfig_, dataDir_, logger_ );
    for( unsigned int i = 0; i < ComponentRegistry::GetEntryCount(); ++i )
    {
        Component* component = ( Component* )ComponentRegistry::GetIndexed( i );
        component->configure();
    }

    // Configure loggers (using just-loaded config information)
    logEngine_->configAssociatedLogWriters( logger_ );

}

bool Supervisor::loadMission( const char* missionName )
{
    MissionItem* missionItem = missionManager_->loadFromFile( missionName );
    return NULL != missionItem;
}

bool Supervisor::resumeMission( const char* missionName )
{
    if( NULL != missionName && 0 != *missionName )
    {
        missionManager_->loadFromFile( missionName );
    }
    bool running = missionManager_->resumeLoadedMission();

#ifdef __FASTSIM
    if( running )
    {
        Handler::SetAllowFastSim( true );
    }
#endif
    return running;
}

bool Supervisor::runMission( const char* missionName, bool quitAtEnd )
{
    if( NULL != missionName && 0 != *missionName )
    {
        missionManager_->loadFromFile( missionName );
    }
    bool running = missionManager_->runLoadedMission( quitAtEnd );

#ifdef __FASTSIM
    if( running )
    {
        Handler::SetAllowFastSim( true );
    }
#endif
    return running;
}

void Supervisor::showStack()
{
    missionManager_->logStack();
}

/// Kills the currently running mission file
void Supervisor::stopMission( const char* calledBy )
{
    if( NULL != missionManager_ && !missionManager_->isCompleted() )
    {
        logger_.syslog( "Stop Mission called by ", calledBy, Syslog::INFO );
        missionManager_->setState( Component::BLOCK_COMPLETED );
    }

}

/// Terminates Supervisor run -- available only to CommandLine
void Supervisor::terminate()
{
    ClearRunningFile();
    running_ = false;
    stopMission( "Supervisor::terminate" );
}

/// Add the maintaining of an uriCode to the Maintainer -- available only to CommandLine
/// function prototype mirrored from Maintainer.h
void Supervisor::addMaintain( unsigned short int uriCode, DataValue* dataValue,
                              SyncComponent::CycleOrder cycleOrder, bool unavailable, bool invalid )
{
    if( NULL == maintainer_ )
    {
        logger_.syslog( "Maintainer has not been initialized", Syslog::ERROR );
        return;
    }
    maintainer_->addMaintain( uriCode, dataValue, cycleOrder, unavailable, invalid );
}

/// Remove the maintaining of an uriCode from the Maintainer -- available only to CommandLine
/// function prototype mirrored from Maintainer.h
void Supervisor::removeMaintain( unsigned short int uriCode )
{
    if( NULL == maintainer_ )
    {
        logger_.syslog( "Maintainer has not been initialized", Syslog::ERROR );
        return;
    }
    maintainer_->removeMaintain( uriCode );
}

/// Remove the maintaining of all uriCodes from the Maintainer -- available only to CommandLine
/// function prototype mirrored from Maintainer.h
void Supervisor::removeAllMaintains()
{
    if( NULL == maintainer_ )
    {
        logger_.syslog( "Maintainer has not been initialized", Syslog::ERROR );
        return;
    }
    maintainer_->removeAllMaintains();
}

/// List the uriCodes being maintained by the Maintainer -- available only to CommandLine
/// function prototype mirrored from Maintainer.h
void Supervisor::listMaintains()
{
    if( NULL == maintainer_ )
    {
        logger_.syslog( "Maintainer has not been initialized", Syslog::ERROR );
        return;
    }
    maintainer_->listMaintains();
}

/// Add the reporting of an uriCode to the Reporter -- available only to CommandLine
/// function prototype mirrored from Reporter.h
void Supervisor::addReport( unsigned short int uriCode,  Reporter::ReportType type, const Timespan *timespan )
{
    if( NULL == reporter_ || ElementURI::NO_CODE == uriCode )
    {
        logger_.syslog( "Reporter has not been initialized", Syslog::ERROR );
        return;
    }
    reporter_->addReport( uriCode, type, timespan );
}

/// Remove the reporting of an uriCode from the Reporter -- available only to CommandLine
/// function prototype mirrored from Reporter.h
void Supervisor::removeReport( unsigned short int uriCode )
{
    if( NULL == reporter_ || ElementURI::NO_CODE == uriCode )
    {
        logger_.syslog( "Reporter has not been initialized", Syslog::ERROR );
        return;
    }
    reporter_->removeReport( uriCode );
}

/// Remove the reporting of all uriCodes from the Reporter -- available only to CommandLine
/// function prototype mirrored from Reporter.h
void Supervisor::removeAllReports()
{
    if( NULL == reporter_ )
    {
        logger_.syslog( "Reporter has not been initialized", Syslog::ERROR );
        return;
    }
    reporter_->removeAllReports();
}

/// List the uriCodes being reported by the Reporter -- available only to CommandLine
/// function prototype mirrored from Reporter.h
void Supervisor::listReports()
{
    if( NULL == reporter_ )
    {
        logger_.syslog( "Reporter has not been initialized", Syslog::ERROR );
        return;
    }
    reporter_->listReports();
}

/// Helper function for GetToShoreFilename()
int Supervisor::FilterLogDir( DIRENT_CONST struct dirent* dirEntry )
{

    int retVal = dirEntry != NULL
                 && dirEntry->d_type == DT_DIR
                 && strlen( dirEntry->d_name ) == 15
                 && dirEntry->d_name[8] == 'T';
    return retVal;
}


/// Helper function for GetToShoreFilename()
FileFilterType Supervisor::GetServiceFilter( Service::ServiceType serviceType, bool lzma )
{
    switch( serviceType )
    {
    case Service::SERVICE_COURIER:
        return lzma ? Supervisor::FilterCourierLzma : Supervisor::FilterCourier;
    case Service::SERVICE_EXPRESS:
        return lzma ? Supervisor::FilterExpressLzma : Supervisor::FilterExpress;
    case Service::SERVICE_PRIORITY:
        return lzma ? Supervisor::FilterPriorityLzma : Supervisor::FilterPriority;
    case Service::SERVICE_NORMAL:
        return lzma ? Supervisor::FilterNormalLzma : Supervisor::FilterNormal;
    default:
        return NULL;
    }
}

/// Helper function for GetToShoreFilename()
Str Supervisor::GetActiveShoreFile( Service::ServiceType serviceType )
{
    Str retVal( Str::EMPTY_STR );
    if( ( NULL != Instance_ ) && ( NULL != Instance_->decimationLogManager_ ) )
    {
        retVal = Instance_->decimationLogManager_->getFileWriter( serviceType )->getFilename();
    }
    return retVal;
}

/// Helper function for GetToShoreFilename()
int Supervisor::FilterCourier( DIRENT_CONST struct dirent* dirEntry )
{
    int retVal = dirEntry != NULL
                 && dirEntry->d_type == DT_REG
                 && strstr( dirEntry->d_name, Service::ServiceType2Str( Service::SERVICE_COURIER ) ) != NULL
                 && strlen( dirEntry->d_name ) == Service::ServiceType2Strlen( Service::SERVICE_COURIER ) + 4;
    return retVal;
}

/// Helper function for GetToShoreFilename()
int Supervisor::FilterCourierLzma( DIRENT_CONST struct dirent* dirEntry )
{
    int retVal = dirEntry != NULL
                 && dirEntry->d_type == DT_REG
                 && strstr( dirEntry->d_name, Service::ServiceType2Str( Service::SERVICE_COURIER ) ) != NULL
                 && strstr( dirEntry->d_name, ".lzma" ) != NULL
                 && strlen( dirEntry->d_name ) == Service::ServiceType2Strlen( Service::SERVICE_COURIER ) + 9;
    return retVal;
}

/// Helper function for GetToShoreFilename()
int Supervisor::FilterExpress( DIRENT_CONST struct dirent* dirEntry )
{
    int retVal = dirEntry != NULL
                 && dirEntry->d_type == DT_REG
                 && strstr( dirEntry->d_name, Service::ServiceType2Str( Service::SERVICE_EXPRESS ) ) != NULL
                 && strlen( dirEntry->d_name ) == Service::ServiceType2Strlen( Service::SERVICE_EXPRESS ) + 4;
    return retVal;
}

/// Helper function for GetToShoreFilename()
int Supervisor::FilterExpressLzma( DIRENT_CONST struct dirent* dirEntry )
{
    int retVal = dirEntry != NULL
                 && dirEntry->d_type == DT_REG
                 && strstr( dirEntry->d_name, Service::ServiceType2Str( Service::SERVICE_EXPRESS ) ) != NULL
                 && strstr( dirEntry->d_name, ".lzma" ) != NULL
                 && strlen( dirEntry->d_name ) == Service::ServiceType2Strlen( Service::SERVICE_EXPRESS ) + 9;
    return retVal;
}

/// Helper function for GetToShoreFilename()
int Supervisor::FilterPriority( DIRENT_CONST struct dirent* dirEntry )
{
    int retVal = dirEntry != NULL
                 && dirEntry->d_type == DT_REG
                 && strstr( dirEntry->d_name, Service::ServiceType2Str( Service::SERVICE_PRIORITY ) ) != NULL
                 && strlen( dirEntry->d_name ) == Service::ServiceType2Strlen( Service::SERVICE_PRIORITY ) + 4;
    return retVal;
}

/// Helper function for GetToShoreFilename()
int Supervisor::FilterPriorityLzma( DIRENT_CONST struct dirent* dirEntry )
{
    int retVal = dirEntry != NULL
                 && dirEntry->d_type == DT_REG
                 && strstr( dirEntry->d_name, Service::ServiceType2Str( Service::SERVICE_PRIORITY ) ) != NULL
                 && strstr( dirEntry->d_name, ".lzma" ) != NULL
                 && strlen( dirEntry->d_name ) == Service::ServiceType2Strlen( Service::SERVICE_PRIORITY ) + 9;
    return retVal;
}


/// Helper function for GetToShoreFilename()
int Supervisor::FilterNormal( DIRENT_CONST struct dirent* dirEntry )
{
    int retVal = dirEntry != NULL
                 && dirEntry->d_type == DT_REG
                 && strstr( dirEntry->d_name, Service::ServiceType2Str( Service::SERVICE_NORMAL ) ) != NULL
                 && strlen( dirEntry->d_name ) == Service::ServiceType2Strlen( Service::SERVICE_NORMAL ) + 4;
    return retVal;
}

/// Helper function for GetToShoreFilename()
int Supervisor::FilterNormalLzma( DIRENT_CONST struct dirent* dirEntry )
{
    int retVal = dirEntry != NULL
                 && dirEntry->d_type == DT_REG
                 && strstr( dirEntry->d_name, Service::ServiceType2Str( Service::SERVICE_NORMAL ) ) != NULL
                 && strstr( dirEntry->d_name, ".lzma" ) != NULL
                 && strlen( dirEntry->d_name ) == Service::ServiceType2Strlen( Service::SERVICE_NORMAL ) + 9;
    return retVal;
}

