#ifndef FACT_MAIN
#define FACT_MAIN

#include <map>
#include <thread>
#include <functional>

#include "LocalControl.h"

void MainThread(StateMachineImp *io_service, bool dummy)
{
    // This is necessary so that the StateMachien Thread can signal the
    // Readline to exit
    io_service->Run(dummy);
    Readline::Stop();
}

/*
template<class S>
int RunDim(Configuration &conf)
{
    WindowLog wout;

    ReadlineColor::PrintBootMsg(wout, conf.GetName(), false);

    //log.SetWindow(stdscr);
    if (conf.Has("log"))
        if (!wout.OpenLogFile(conf.Get<string>("log")))
            wout << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;

    // Start io_service.Run to use the StateMachineImp::Run() loop
    // Start io_service.run to only use the commandHandler command detaching
    AutoScheduler<S> io_service(wout);
    if (!io_service.EvalConfiguration(conf))
        return -1;

    io_service.Run();

    return 0;
}
*/

template<class T, class S>
int Main(Configuration &conf, bool dummy=false)
{
    static T shell(conf.GetName().c_str(),
                   conf.Has("console") ? conf.Get<int>("console")!=1 : 0);

    WindowLog &win  = shell.GetStreamIn();
    WindowLog &wout = shell.GetStreamOut();

    if (conf.Has("log"))
        if (!wout.OpenLogFile(conf.Get<string>("log")))
            win << kRed << "ERROR - Couldn't open log-file " << conf.Get<string>("log") << ": " << strerror(errno) << endl;

    S io_service(wout);

    const boost::filesystem::path path(conf.GetName());

    const string pname = path.parent_path().string();
    const string fname = path.filename();

    const Time now;
    io_service.Write(now, "/----------------------- Program ------------------------");
    io_service.Write(now, "| Program:  "PACKAGE_STRING" ("+fname+")");
    io_service.Write(now, "| CallPath: "+pname);
    io_service.Write(now, "| Compiled: "__DATE__" "__TIME__);
    io_service.Write(now, "| Revision: "REVISION);
    io_service.Write(now, "| Contact:  "PACKAGE_BUGREPORT);
    io_service.Write(now, "| URL:      "PACKAGE_URL);
    io_service.Write(now, "| Start:    "+now.GetAsStr("%c"));
    io_service.Write(now, "\\----------------------- Options ------------------------");
    const multimap<string,string> mmap = conf.GetOptions();
    for (multimap<string,string>::const_iterator it=mmap.begin(); it!=mmap.end(); it++)
        io_service.Write(now, ": "+it->first+(it->second.empty()?"":" = ")+it->second);
    io_service.Write(now, "\\------------------- Evaluating options -----------------");
    const int rc = io_service.EvalOptions(conf);

    const map<string,string> &wco = conf.GetWildcardOptions();
    if (wco.size()>0)
    {
        io_service.Write(now, "------------- Unrecognized wildcard options -------------", MessageImp::kWarn);

        size_t max = 0;
        for (map<string,string>::const_iterator it=wco.begin(); it!=wco.end(); it++)
            if (it->second.length()>max)
                max = it->second.length();

        for (map<string,string>::const_iterator it=wco.begin(); it!=wco.end(); it++)
        {
            ostringstream str;
            str.setf(ios_base::left);
            str << setw(max+1) << it->second << " : " << it->first;
            io_service.Write(now, str.str(), MessageImp::kWarn);
        }
        return 127;
    }

    io_service.Message("==================== Starting main loop =================");
    if (rc>=0)
        return rc;

    shell.SetReceiver(io_service);

//    boost::thread t(boost::bind(&AutoScheduler<S>::Run, &io_service));
    thread t(bind(MainThread, &io_service, dummy));

    const vector<string> v1 = conf.Vec<string>("cmd");
    for (vector<string>::const_iterator it=v1.begin(); it!=v1.end(); it++)
        shell.ProcessLine(*it);

    const vector<string> v2 = conf.Vec<string>("exec");
    for (vector<string>::const_iterator it=v2.begin(); it!=v2.end(); it++)
        shell.Execute(*it);

    if (conf.Get<bool>("quit"))
        shell.Stop();

    shell.Run();                 // Run the shell
    io_service.Stop();           // Signal Loop-thread to stop
    // io_service.Close();       // Obsolete, done by the destructor
    // wout << "join: " << t.timed_join(boost::posix_time::milliseconds(0)) << endl;

    // Wait until the StateMachine has finished its thread
    // before returning and destroying the dim objects which might
    // still be in use.
    t.join();

    return 0;
}

#endif
