// $Header: https://oberon.oapd.inaf.it/shins/trunk/shins/src/obs_ctrl/obsControlServer.cpp 3896 2022-04-13 13:24:17Z depa $

#include <cstdlib>

// ICE
#include <Ice/Ice.h>
#include <Ice/Logger.h>
#include <IceUtil/Mutex.h>

//NICE
#include <Nice/Application.h>
//#include <Nice/Log.h>
#include <Basda/Basdard.h>

// BOOST
#include <boost/signals2/signal.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/asio.hpp>

// LBTO IIF
#include <LbtoIIFInterface2.h>
#include <Ltcs/Iif/ServiceWorkerInterface.h>
#include <Ltcs/Iif/PropertyName.h>

// SHINS
#include "obsControl.h"
#include "os_sm.h"
#include "sequencerI.hpp"
//#include "sensorsI.h"
#include "systemI.hpp"
// #include "OsApp.h"
#include "tcs_iif/iifApp.h"
#include "util/shins_util.h"
#include "util/logger.h"
#include "util/shins_util_f.h"

using boost::asio::ip::tcp;

namespace su = shins::util;
namespace suc = shins::util::constants;


class OsApp : public Nice::Application
{
public:
    OsApp(int argc, char * argv[])
    {
        Nice::Application::init ( argc, argv );
        // U1_LOG("OSAPP CONFIG : " << Nice::Application::config());
        // Nice::SeqString app_children;
        // Nice::Application::config()->children(app_children);
        // U2_LOG(app_children.size());
        // Nice::SeqString::iterator it;
        // for(it = app_children.begin(); it < app_children.end(); it++)
        // {
        //     U2_LOG(*it);
        // }
        try
        {
        //    Nice::Endpoint ep = Nice::Endpoint::fromString("[ALIAS=ltcs.iif-svr.IIF, TIMEOUT=180]");
        //    Nice::AppConfigClientPtr con =  Nice::AppConfigClient::connectWithEndpointAtNode ( ep, "ltcs.iif-svr.IIF" );
        //    m_iif = Ltcs::IifServiceWorkerInterface::create("ltcs.iif-svr.IIF");
            // Me.m_motion->m_app_ptr = Nice::AppConfigClient::create(nodemotion->getAtNode<Nice::Endpoint>("ALIAS"), nodemotion);
        //    iif = tcsApp.connect<Ltcs::IifServiceWorkerInterface>("ltcs.iif-svr.IIF");
            // U9_LLOG("m_iif : " << iif << "; m_iif ptr : 0x" << std::hex << std::setfill('0') << std::setw(16) << ((long long)iif._ptr));
        }
        catch(const IceUtil::NullHandleException & e)
        {
            A_LLOG("Unable to reach IIF service");
            E_LLOG(e.what());
        }
        catch(const Nice::Exception & e)
        {
            A_LLOG("Unable to reach IIF service");
        }
    }
    ~OsApp()
    {
       U8_LOG("OsApp::" << __FUNCTION__ << " ~ Goodbye");
       int status = EXIT_SUCCESS;
       status = Application::end(status);
    }
private:
    // Ltcs::IifServiceWorkerInterfacePtr m_iif;
protected:
    virtual int run ( int argc, char * argv[] )
    {
        W_LOG(" run #" << argc);
        if(argc > 1)
        {
            W_LOG("arg1 #" << * argv[1]);
        }
        if(argc > 2)
        {
            W_LOG("arg1 #" << * argv[2]);
        }
        return EXIT_SUCCESS;
    }
public:
    // Ltcs::IifServiceWorkerInterfacePtr getIifInterface(){ return m_iif;};
    virtual void interruptCallback ( int _sig )
    {
        W_LOG("Interrupt #" << _sig);
    }
};

    // ++++ TYPEDEFS ++++
typedef IceUtil::Handle<SequencerI> SequencerIPtr;
//    typedef IceUtil::Handle<SensorsI> SensorsIPtr;
// typedef IceUtil::Handle<SystemI> SystemIPtr;
    // typedef IceUtil::Handle<IceUtil::Thread> ThreadPtr;


int main(int argc, char * argv[])
{
    LOG_SYSLOG;

    F_LLOG ("____________+++++ OS START +++++____________"); // Fatal Error
    A_LLOG ("____________+++++ OS START +++++____________"); // Alert Error
    C_LLOG ("____________+++++ OS START +++++____________"); // Critical Error
    E_LLOG ("____________+++++ OS START +++++____________"); // Error
    W_LLOG ("____________+++++ OS START +++++____________"); // Warning
    N_LLOG ("____________+++++ OS START +++++____________"); // Notice
    I_LLOG ("____________+++++ OS START +++++____________"); // Info
    U1_LLOG("____________+++++ OS START +++++____________"); // Detailed Info
    U2_LLOG("____________+++++ OS START +++++____________"); // Very Detailed Info
    U3_LLOG("____________+++++ OS START +++++____________"); // Other Details 3
    U4_LLOG("____________+++++ OS START +++++____________"); // Other Details 4
    U5_LLOG("____________+++++ OS START +++++____________"); // Other Details 5
    U6_LLOG("____________+++++ OS START +++++____________"); // Function
    U7_LLOG("____________+++++ OS START +++++____________"); // Function Parameters
    U8_LLOG("____________+++++ OS START +++++____________"); // Program Details 8 || Function Exit
    U9_LLOG("____________+++++ OS START +++++____________"); // Program Details 9
    D_LLOG ("____________+++++ OS START +++++____________"); // Debug Info
    T_LLOG ("____________+++++ OS START +++++____________"); // Trace Info

    I_LLOG ("********************************************");
    I_LLOG ("*************** SHINS START ****************");
    I_LLOG ("********************************************");


    I_LLOG (shins::util::banner_string("OS Arguments:", '_', 80))
    for(int i = 0; i < argc; i++)
    {
        I_LLOG(shins::util::banner_string(argv[i], '.', 80));
    }
    I_LLOG (shins::util::banner_string("End OS Arguments", '_', 80));

    // ++++ VARIABLES ++++
    // ++ asio variables ++
    boost::shared_ptr<boost::asio::io_service> ptrIoService = boost::make_shared<boost::asio::io_service>();
    boost::shared_ptr<boost::asio::io_service::work> work(new boost::asio::io_service::work(*ptrIoService));
    boost::thread_group ioServiceThreads;

    // ++ shins variables
    osfsm::OSFsm m_stateMachine;
    boost::shared_ptr<osfsm::OSFsm> ptrStateMachine;
    IceUtil::Mutex m_mtxStateMachine;

    std::string endPointName = "CONNECT";
    std::string serviceName = "IIF.SVC";
    std::string LogLevel = "--LOGGER.LEVEL:LogLevel=INFO";

    char log_level[64];
    {
        for(int i = 0; i < argc; i++)
        {
            std::string argument = argv[i];
            if(argument.find("--LOGGER.LEVEL:LogLevel=", 0) == 0)
            {
                LogLevel = LogLevel.replace(24, 28, argument.substr(24));
                U9_LLOG("Log Level changed to " << argument.substr(24) << " :" << LogLevel);
                break;
            }
            else
            {
                U9_LLOG(argument << " " << argument.find("--LOGGER.LEVEL:LogLevel=", 0) << " --LOGGER.LEVEL:LogLevel=");
            }
        }
        size_t level_size = LogLevel.size();
        if(level_size < 64)
        {
            memcpy(log_level, LogLevel.c_str(), level_size);
            *(log_level + level_size) = 0;
        }
        U9_LLOG("log_level = " << log_level);
    }

    int status = 0;

    std::string envShinsCfg;
    std::string strShinsCfg;
    std::string strAliasFileName;
    std::string strLoggerPrefix = "";
    std::map<std::string, bool> mapSubSysInitFlag;

    std::map<std::string, std::string> mapCfgFiles = su::build_cfg_files_map();

    ////////////////////////////////
    // create telemetry directory //
    ////////////////////////////////
    if(su::create_telemetry_dirs() == EXIT_FAILURE)
    {
        throw(std::runtime_error("unable to create telemetry directories"));
    }

    Ltcs::IifServiceWorkerInterfacePtr iif = NULL;
    // U9_LLOG("m_iif : " << iif << "; m_iif ptr : 0x" << std::hex << std::setfill('0') << std::setw(16) << ((long long)iif._ptr));

    // ++ Ice variables
    Ice::CommunicatorPtr ic = NULL;

    try
    {
        OsApp osapp(argc, argv);
        ic = osapp.communicator();
        strLoggerPrefix = ic->getLogger()->getPrefix();

        // getting ICE properties configuration file provided at command line
        Ice::PropertiesPtr props = ic->getProperties();
//        props->setProperty ( "Ice.ThreadPool.Client.Size", ( config()->exist( "Ice.ThreadPool.Client.Size" ) ? config()->getAtNode<std::string>( "Ice.ThreadPool.Client.Size" ) : "3") );
        std::string sizeMax = props->getProperty("Ice.ThreadPool.Server.SizeMax");
        std::string sizeWarn = props->getProperty("Ice.ThreadPool.Server.SizeWarn");
        std::string threadPool = props->getProperty("Ice.Trace.ThreadPool");
    //    props->setProperty("Ice.ThreadPool.Server.SizeMax", "6");
    //    props->setProperty("Ice.ThreadPool.Server.SizeWarn", "5");

    //    props->setProperty("Ice.Trace.ThreadPool", "1");
        {
        //    Ice::Error err(ic->getLogger());
        //    err << "ThisIsTheLogger";
        //    U9_LLOG("ThisIsTheLogger  " << err.str())
        }
        props->setProperty("Ice.Override.Timeout", "16");

        envShinsCfg = props->getPropertyWithDefault("Tan.Insroot", "INSROOT");
        strShinsCfg = su::get_env(envShinsCfg);
//        strAliasFileName = strShinsCfg + props->getPropertyWithDefault("Tan.AliasPath", "/etc/alias.cfg");
//        D_LLOG("strAliasFileName = " << strAliasFileName);
        mapSubSysInitFlag[suc::calunitCtrl] = props->getPropertyAsIntWithDefault("Shins.Calunit.On", 1);
        mapSubSysInitFlag[suc::motionCtrl] = props->getPropertyAsIntWithDefault("Shins.Motion.On", 1);
        mapSubSysInitFlag[suc::trackingCtrl] = props->getPropertyAsIntWithDefault("Shins.Tracking.On", 1);
        mapSubSysInitFlag[suc::tiptiltCtrl] = props->getPropertyAsIntWithDefault("Shins.Tiptilt.On", 1);

        I_LLOG(suc::tiptiltCtrl << " = " << mapSubSysInitFlag[suc::tiptiltCtrl]);

        ptrStateMachine = boost::make_shared<osfsm::OSFsm>(m_stateMachine);

        // Ice::ObjectAdapterPtr adapter =
        //   ic->createObjectAdapterWithEndpoints("ObsControlAdapter", "default -p 10500");
        Ice::ObjectAdapterPtr adapter = ic->createObjectAdapter("ObsControlAdapter");
        I_LLOG("Created Ice object adapter");

        U1_LLOG("Creating Ice servants");
        SequencerIPtr servantSeq = new SequencerI(mapSubSysInitFlag, ptrStateMachine, ptrIoService, props);

//        SystemIPtr servantSystem = new SystemI();
//        SensorsIPtr servantSensors = new SensorsI(argc, argv, ptrIoService);

        adapter->add(servantSeq, ic->stringToIdentity("ObsControlAdapterSeq"));
//        adapter->add(servantSensors, ic->stringToIdentity("ObsControlAdapterSen"));
//        adapter->add(servantSystem, ic->stringToIdentity("ObsControlAdapterSys"));
        // adapter->add(servantEng, ic->stringToIdentity("ObsControlAdapterEng"));
        U9_LLOG("Adapter Pointer is: " << adapter.get());

        adapter->activate();
        U9_LLOG("Creating io_service_thread");
        ioServiceThreads.create_thread(boost::bind(&su::io_service_thread, ptrIoService));
        U9_LLOG("io_service_thread is running")

        // Set OS Online here
        // ptrStateMachine->setNow(Basda::ONLINE);

        ic->waitForShutdown();
    }
    catch(const Nice::Exception & e)
    {
        A_LLOG(e.what());
        status = EXIT_FAILURE;
    }
    catch(const Ice::SocketException & e)
    {
        A_LLOG("Caught exception " << e.ice_name());
        status = 1;
    }
    catch(const IceUtil::NullHandleException & e)
    {
        A_LLOG(e.what());
        status = EXIT_FAILURE;
    }
    catch(const Ice::Exception & e)
    {
        A_LLOG("Caught exception " << e.ice_name());
        status = 1;
    }
    catch(const char * msg)
    {
        A_LLOG(msg);
        status = 1;
    }
    catch(...)
    {
        E_LLOG(__FUNCTION__);
        status = 1;
    }

    if(ic)
    {
        try
        {
            ic->destroy();
        }
        catch(const Ice::Exception & e)
        {
            A_LLOG("Caught exception " << e.ice_name() << " when destroying Ice communicator");
            status = 1;
        }
    }
    I_LLOG("Stopping and joining socket I/O thread...")
    ptrIoService->stop();
    ioServiceThreads.join_all();
    F_LLOG ("____________++++++ OS END ++++++____________");
    return status;
}
