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

/** \defgroup supervisor Supervisor
 *
 *  This module includes the Supervisor ("main" component in the vehicle software),
 *  the ControlThread, which runs periodically to execute the computation
 *  cycle, the CommandLine for dealing with human input (from the keyboard and
 *  via satellite), as well as registries of running components and mission
 *  components currently loaded.
 */

#ifndef SUPERVISOR_H_
#define SUPERVISOR_H_

#include "component/SyncComponent.h"
#include "logger/DecimationLogWriter.h"
#include "logger/Logger.h"
#include "logger/Reporter.h"
#include "module/ModuleLoader.h"
#include "supervisor/CommandExec.h"
#include "supervisor/CommandLine.h"
#include "supervisor/Maintainer.h"

#include <dirent.h>

class BinaryLogWriter;
class CycleStarter;
class DataEntry;
class DecimationLogManager;
class FileLogWriter;
class Handler;
class LinearApproximationLogWriter;
class LogEngine;
class MissionItem;
class MissionManager;
class SendDestination;
class SplitFileLogWriter;

#ifndef __APPLE_CC__
#define DIRENT_CONST const
#else
#define DIRENT_CONST
#endif

/// Helper typedef for GetToShoreFilename() and GetLatestLogFilename()
typedef int ( *FileFilterType )( DIRENT_CONST struct dirent * );

/**
 *  The supervisor is the "main" component in the vehicle software.  It is the
 *  first to start (from the real "main()") function, and handles the loading
 *  of other modules, the instantiation of components and the spinning off of
 *  threads.  Once the vehicle is up and running it can host other
 *  infrequently-running (lower priority) components, as well as its own overhead
 *  processes.
 *
 *  The supervisor is the "owner" of all other system Components and is responsible
 *  for instantiation and uninstantiation.
 *
 *  \ingroup supervisor
 */
class Supervisor
{
public:

    friend class CommandExec;
    friend class CommandLine;
    friend class CBIT;

    /** Constructor:  To meet requirement \ref req_timing_accurateStart, this constructor
     *  sets the process priority to -20.
     */
    Supervisor( int argc, char **argv, Str& dataDir );

    /// Destructor
    virtual ~Supervisor();

    /// Initialization.  Creates the logger and loads any modules.
    /// This fills the ComponentRegistry, but components aren't initialized.
    virtual void initialize();

    /// The run() function.  Best to think of this as main() for
    /// the vehicle software.  Kicks off all the child threads
    /// (through the ComponentRegistry), then sits back and watches
    /// the software run.
    virtual void run();

    /// Uninitialization.  Spins down the threads, unloads the modules
    /// and deletes any of its own components.
    virtual void uninitialize();

    /// Generates a new data directory and returns its name
    static Str GenerateDataDir( const Str& logSymlinkSubdir = "" );

    /// Create a new directory in Logs and begin writing to it.
    static Str RestartLogs();

    /// Closes the current "toShore" file and begins a new one.
    static void IncrementShoreFile();

    /// Gets the name of the latest "toShore" file
    /// May be from a previous run of the software.
    static Str GetToShoreFilename( Service::ServiceType serviceType );

    /// Gets the name of the latest Log file that matches the filter
    /// May be from a previous run of the software.
    static Str GetLatestLogFilename( FileFilterType fileFilter );

    static bool IsRestart()
    {
        return Restart_;
    }

    static int GetArgc()
    {
        return Argc_;
    }

    static char** GetArgv()
    {
        return Argv_;
    }

    static void ClearArgs();

    /// Forces an entry into the shore log.
    static void SendData( DataEntry* dataEntry, Service::ServiceType serviceType, SendDestination* destination = NULL );

    /// Indicates if mission manager is failed
    static bool IsMissionManagerFailed();

    /// Indicates if a loaded mission is running
    static bool IsMissionRunning();

    /// If there is a file at Data/running, set WasRunning_ to true;
    /// Otherwise, just create file.
    static void TouchRunningFile();

    /// As part of orderly shutdown, delete the file at Data/running
    static void ClearRunningFile();

    static bool WasRunning()
    {
        return WasRunning_;
    }

    static bool IsRunningSimsInRealTime()
    {
        return RunSimsInRealTime_;
    }

    /// Queue up the sbd at the indicated address to be resent
    static void QueueSBD( const char* path );

    /// Get the path to next SBD to be resent, if any
    /// Delete the pointer after use!
    static char* GetQueuedSBD();

    /// Call upon DecimationLogManager to configure automatic
    /// Logging of data.
    static void ConfigureDecimation( Service::ServiceType serviceType,
                                     DecimationLogWriter::DecimationType decimationType,
                                     const Str& name, DataValue* parameter, Logger& logger );

private:

    static void SetArgs( const char* args );

    static void SetRestart( bool restart )
    {
        Restart_ = restart;
    }

    /// Returns singleton instance.
    static Supervisor* GetInstance()
    {
        return Instance_;
    }

    // Note that the copy constructor below is private and not given a body.
    // Any attempt to call it will return a compiler error.
    Supervisor( const Supervisor& old ); // disallow copy constructor

    /// Load up configuration settings
    void configure();

    /// Loads the specified mission file
    bool loadMission( const char* missionName );

    /// Resumes a previously stopped mission
    bool resumeMission( const char* missionName );

    /// Loads the specified mission file if non-null
    /// Runs that mission or previously loaded one
    bool runMission( const char* missionName, bool quitAtEnd );

    /// Causes the currently running mission stack to be displayed
    void showStack( );

    /// Kills the currently running mission file
    void stopMission( const char* calledBy );

    /// Terminates Supervisor run -- available only to CommandLine
    void terminate();

    /// Add a uriCode and value to be maintained -- available only to CommandLine
    /// function prototype mirrored from Maintainer.h
    void addMaintain( unsigned short int uriCode, DataValue* dataValue,
                      SyncComponent::CycleOrder cycleOrder, bool unavailable = false, bool invalid = false );

    /// Remove the maintaining of an uriCode from the Maintainer -- available only to CommandLine
    /// function prototype mirrored from Maintainer.h
    void removeMaintain( unsigned short int uriCode );

    /// Remove the maintaining of all uriCodes from the Maintainer -- available only to CommandLine
    /// function prototype mirrored from Maintainer.h
    void removeAllMaintains();

    /// List the uriCodes being maintained by the Maintainer -- available only to CommandLine
    /// function prototype mirrored from Maintainer.h
    void listMaintains();

    /// Add the reporting of an uriCode to the Reporter -- available only to CommandLine
    /// function prototype mirrored from Reporter.h
    void addReport( unsigned short int uriCode,  Reporter::ReportType type = Reporter::REPORT_CHANGE, const Timespan *timespan = NULL );

    /// Remove the reporting of an uriCode from the Reporter -- available only to CommandLine
    /// function prototype mirrored from Reporter.h
    void removeReport( unsigned short int uriCode );

    /// Remove the reporting of all uriCodes from the Reporter -- available only to CommandLine
    /// function prototype mirrored from Reporter.h
    void removeAllReports();

    /// List the uriCodes being reported by the Reporter -- available only to CommandLine
    /// function prototype mirrored from Reporter.h
    void listReports();

    // Kick off the command line
    void startCommandLine();

    /// Flag set when it's time to restart
    static bool Restart_;

    /// Arguments passed to main when restarting
    static int Argc_;
    static char **Argv_;
    static char *Args_;
    static bool WasRunning_;

    /// To make simulated missions run in real-time
    static bool RunSimsInRealTime_;

    /// Main LogEngine for the system
    LogEngine* logEngine_;

    /// Logger for Supervisor
    Logger logger_;

    /// Arguments passed from main
    int argc_;
    char **argv_;

    // alternate configuration location
    Str altConfig_;

    /// Module handling
    ListOfLoadedModulePtrs loadedModules_;
    ModuleLoader moduleLoader_;

    /// Command execution handler
    Handler* commandExecHandler_;

    /// Keyboard interface handler
    Handler* commandLineHandler_;

    /// Mission handling
    MissionManager* missionManager_;

    /// Maintains values specified at console
    Maintainer* maintainer_;

    /// Reporting to console
    Reporter* reporter_;

    /// For shutdown purposes
    bool running_;

    /// Where to write data!
    Str& dataDir_;

    /// Queue of SBDs to re-send to shore
    FlexArray<char*> sbdQueue_;

    /// Manages the "toShore" files
    DecimationLogManager* decimationLogManager_;

    /// Write logs
    FileLogWriter* syslogFileWriter_;
    BinaryLogWriter* slateWriter_;
    SplitFileLogWriter* slateFileWriter_;
    FileLogWriter* slateDirFileWriter_;
    FileLogWriter* kmlFileWriter_;
    LinearApproximationLogWriter* kmlApproximationWriter_;

    /// Helper function for GetToShoreFilename()
    static int FilterLogDir( DIRENT_CONST struct dirent* dirEntry );

    static Supervisor* Instance_;

    /// Helper function for GetToShoreFilename()
    static FileFilterType GetServiceFilter( Service::ServiceType serviceType, bool lzma );

    /// Helper function for GetToShoreFilename()
    static Str GetActiveShoreFile( Service::ServiceType serviceType );

    /// Helper function for GetToShoreFilename()
    static int FilterCourier( DIRENT_CONST struct dirent* dirEntry );

    /// Helper function for GetToShoreFilename()
    static int FilterCourierLzma( DIRENT_CONST struct dirent* dirEntry );

    /// Helper function for GetToShoreFilename()
    static int FilterExpress( DIRENT_CONST struct dirent* dirEntry );

    /// Helper function for GetToShoreFilename()
    static int FilterExpressLzma( DIRENT_CONST struct dirent* dirEntry );

    /// Helper function for GetToShoreFilename()
    static int FilterPriority( DIRENT_CONST struct dirent* dirEntry );

    /// Helper function for GetToShoreFilename()
    static int FilterPriorityLzma( DIRENT_CONST struct dirent* dirEntry );

    /// Helper function for GetToShoreFilename()
    static int FilterNormal( DIRENT_CONST struct dirent* dirEntry );

    /// Helper function for GetToShoreFilename()
    static int FilterNormalLzma( DIRENT_CONST struct dirent* dirEntry );
};

#endif /*Supervisor_H_*/
