#include <boost/bind.hpp>
#include <boost/array.hpp>
#include <boost/asio/error.hpp>
#include <boost/asio/deadline_timer.hpp>

#include "Dim.h"
#include "Event.h"
#include "Shell.h"
#include "StateMachineDim.h"
#include "Connection.h"
#include "Configuration.h"
#include "Console.h"
#include "Converter.h"

#include "tools.h"

#include "HeadersFTM.h"


namespace ba = boost::asio;
namespace bs = boost::system;

using namespace std;

// ------------------------------------------------------------------------

class ConnectionFTM : public Connection
{
public:
    enum States
    {
        // State Machine states
        kDisconnected = 1,
        kConnected,
        kIdle,
        kConfigured,  // Returned if idle and fBufStaticData==fStaticData
        kTakingData,
    };

private:
    vector<uint16_t> fBuffer;

    bool fHasHeader;
    FTM::States fState;

    bool fIsVerbose;
    bool fIsDynamicOut;
    bool fIsHexOutput;

//    string fDefaultSetup;

    // --verbose
    // --hex-out
    // --dynamic-out
    // --load-file
    // --leds
    // --trigger-interval
    // --physcis-coincidence
    // --calib-coincidence
    // --physcis-window
    // --physcis-window
    // --trigger-delay
    // --time-marker-delay
    // --dead-time
    // --clock-conditioner-r0
    // --clock-conditioner-r1
    // --clock-conditioner-r8
    // --clock-conditioner-r9
    // --clock-conditioner-r11
    // --clock-conditioner-r13
    // --clock-conditioner-r14
    // --clock-conditioner-r15
    // ...

protected:
    map<uint16_t, uint32_t> fCounter;

    FTM::Header      fHeader;
    FTM::FtuList     fFtuList;
    FTM::StaticData  fStaticData;
    FTM::DynamicData fDynamicData;
    FTM::Error       fError;

    FTM::StaticData  fBufStaticData;

    virtual void UpdateFirstHeader()
    {
        // FIXME: Message() ?
        Out() << endl << kBold << "First header received:" << endl;
        Out() << fHeader;
        if (fIsHexOutput)
            Out() << Converter::GetHex<uint16_t>(fHeader, 16) << endl;
    }

    virtual void UpdateHeader()
    {
        // emit service with trigger counter from header
        if (!fIsVerbose)
            return;

        if (fHeader.fType==FTM::kDynamicData && !fIsDynamicOut)
            return;

        Out() << endl << kBold << "Header received:" << endl;
        Out() << fHeader;
        if (fIsHexOutput)
            Out() << Converter::GetHex<uint16_t>(fHeader, 16) << endl;
    }

    virtual void UpdateFtuList()
    {
        if (!fIsVerbose)
            return;

        Out() << endl << kBold << "FtuList received:" << endl;
        Out() << fFtuList;
        if (fIsHexOutput)
            Out() << Converter::GetHex<uint16_t>(fFtuList, 16) << endl;
    }

    virtual void UpdateStaticData()
    {
        if (!fIsVerbose)
            return;

        Out() << endl << kBold << "Static data received:" << endl;
        Out() << fStaticData;
        if (fIsHexOutput)
            Out() << Converter::GetHex<uint16_t>(fStaticData, 16) << endl;
    }

    virtual void UpdateDynamicData()
    {
        if (!fIsDynamicOut)
            return;

        Out() << endl << kBold << "Dynamic data received:" << endl;
        Out() << fDynamicData;
        if (fIsHexOutput)
            Out() << Converter::GetHex<uint16_t>(fDynamicData, 16) << endl;
    }

    virtual void UpdateError()
    {
        if (!fIsVerbose)
            return;

        Out() << endl << kRed << "Error received:" << endl;
        Out() << fError;
        if (fIsHexOutput)
            Out() << Converter::GetHex<uint16_t>(fError, 16) << endl;
    }

    virtual void UpdateCounter()
    {
        if (!fIsVerbose)
            return;

        if (!fIsDynamicOut)
            return;

        Out() << "Received: ";
        Out() << "H=" << fCounter[FTM::kHeader] << "  ";
        Out() << "S=" << fCounter[FTM::kStaticData] << "  ";
        Out() << "D=" << fCounter[FTM::kDynamicData] << "  ";
        Out() << "F=" << fCounter[FTM::kFtuList] << "  ";
        Out() << "E=" << fCounter[FTM::kErrorList] << "  ";
        Out() << "R=" << fCounter[FTM::kRegister] << endl;
    }

    bool CheckConsistency(FTM::StaticData &data)
    {
        bool warn1 = false;
        if (data.IsEnabled(FTM::StaticData::kPedestal) != (data.GetSequencePed()  >0) ||
            data.IsEnabled(FTM::StaticData::kLPint)    != (data.GetSequenceLPint()>0) ||
            data.IsEnabled(FTM::StaticData::kLPext)    != (data.GetSequenceLPext()>0))
        {
            warn1 = true;
            data.Enable(FTM::StaticData::kPedestal, data.GetSequencePed()>0);
            data.Enable(FTM::StaticData::kLPint,    data.GetSequenceLPint()>0);
            data.Enable(FTM::StaticData::kLPext,    data.GetSequenceLPext()>0);
        }

        bool warn2 = false;
        const uint16_t ref = data[0].fPrescaling;
        for (int i=1; i<40; i++)
        {
            if (data[i].fPrescaling != ref)
            {
                warn2 = true;
                data[i].fPrescaling = ref;
            }
        }

        if (warn1)
            Warn("GeneralSettings not consistent with trigger sequence.");
        if (warn2)
            Warn("Prescaling not consistent for all boards.");

        return !warn1 && !warn2;
    }

private:
    void HandleReceivedData(const bs::error_code& err, size_t bytes_received, int /*type*/)
    {
        // Do not schedule a new read if the connection failed.
        if (bytes_received==0 || err)
        {
            if (err==ba::error::eof)
                Warn("Connection closed by remote host (FTM).");

            // 107: Transport endpoint is not connected (bs::error_code(107, bs::system_category))
            // 125: Operation canceled
            if (err && err!=ba::error::eof &&                     // Connection closed by remote host
                err!=ba::error::basic_errors::not_connected &&    // Connection closed by remote host
                err!=ba::error::basic_errors::operation_aborted)  // Connection closed by us
            {
                ostringstream str;
                str << "Reading from " << URL() << ": " << err.message() << " (" << err << ")";// << endl;
                Error(str);
            }
            PostClose(err!=ba::error::basic_errors::operation_aborted);
            return;
        }

        // If we have not yet received a header we expect one now
        // This could be moved to a HandleReceivedHeader function
        if (!fHasHeader)
        {
            if (bytes_received!=sizeof(FTM::Header))
            {
                ostringstream str;
                str << "Excepted " << sizeof(FTM::Header) << " bytes (FTM::Header) but received " << bytes_received << ".";
                Error(str);
                PostClose(false);
                return;
            }

            fHeader = fBuffer;

            // Check the data integrity
            if (fHeader.fDelimiter!=FTM::kDelimiterStart)
            {
                ostringstream str;
                str << "Invalid header received: start delimiter wrong, received ";
                str << hex << fHeader.fDelimiter << ", expected " << FTM::kDelimiterStart << ".";
                Error(str);
                PostClose(false);
                return;
            }

            fHasHeader = true;

            // Convert FTM state into FtmCtrl state
            if (++fCounter[FTM::kHeader]==1)
                UpdateFirstHeader();

            UpdateCounter();
            UpdateHeader();

            // Start reading of data
            switch (fHeader.fType)
            {
            case FTM::kStaticData:
            case FTM::kDynamicData:
            case FTM::kFtuList:
            case FTM::kRegister:
            case FTM::kErrorList:
                // This is not very efficient because the space is reallocated
                // maybe we can check if the capacity of the std::vector
                // is ever decreased. If not, everythign is fine.
                fBuffer.resize(fHeader.fDataSize);
                AsyncRead(ba::buffer(fBuffer));
                AsyncWait(fInTimeout, 50, &Connection::HandleReadTimeout);
                return;

            default:
                ostringstream str;
                str << "Unknonw type " << fHeader.fType << " in received header." << endl;
                Error(str);
                PostClose(false);
                return;
            }

            return;
        }

        // Check the data integrity (check end delimiter)
        if (ntohs(fBuffer.back())!=FTM::kDelimiterEnd)
        {
            ostringstream str;
            str << "Invalid data received: end delimiter wrong, received ";
            str << hex << ntohs(fBuffer.back()) << ", expected " << FTM::kDelimiterEnd << ".";
            Error(str);
            PostClose(false);
            return;
        }

        // Remove end delimiter
        fBuffer.pop_back();

        try
        {
            // If we have already received a header this is the data now
            // This could be moved to a HandleReceivedData function

            fCounter[fHeader.fType]++;
            UpdateCounter();

            switch (fHeader.fType)
            {
            case FTM::kFtuList:
                fFtuList = fBuffer;
                UpdateFtuList();
                break;

            case FTM::kStaticData:
                if (fCounter[FTM::kStaticData]==1)
                {
                    // This check is only done at startup
                    FTM::StaticData data(fBuffer);
                    if (!CheckConsistency(data))
                    {
                        CmdSendStatDat(data);
                        break;
                    }
                }

                fStaticData = fBuffer;
                UpdateStaticData();
                break;

            case FTM::kDynamicData:
                fDynamicData = fBuffer;
                UpdateDynamicData();
                break;

            case FTM::kRegister:
                if (fIsVerbose)
                {
                    Out() << endl << kBold << "Register received: " << endl;
                    Out() << "Addr:  " << ntohs(fBuffer[0]) << endl;
                    Out() << "Value: " << ntohs(fBuffer[1]) << endl;
                }
                break;

            case FTM::kErrorList:
                fError = fBuffer;
                UpdateError();
                break;

            default:
                ostringstream str;
                str << "Unknonw type " << fHeader.fType << " in header." << endl;
                Error(str);
                PostClose(false);
                return;
            }
        }
        catch (const logic_error &e)
        {
            ostringstream str;
            str << "Exception converting buffer into data structure: " << e.what();
            Error(str);
            PostClose(false);
            return;
        }

        fInTimeout.cancel();

        //fHeader.clear();
        fHasHeader = false;
        fBuffer.resize(sizeof(FTM::Header)/2);
        AsyncRead(ba::buffer(fBuffer));
    }

    // This is called when a connection was established
    void ConnectionEstablished()
    {
        fCounter.clear();
        fBufStaticData.clear();

        fHeader.clear();
        fHasHeader = false;
        fBuffer.resize(sizeof(FTM::Header)/2);
        AsyncRead(ba::buffer(fBuffer));

//        if (!fDefaultSetup.empty())
//            LoadStaticData(fDefaultSetup);

        // Get a header and configdata!
        CmdReqStatDat();

        // get the DNA of the FTUs
        CmdPing();
    }

    void HandleReadTimeout(const bs::error_code &error)
    {
        if (error==ba::error::basic_errors::operation_aborted)
            return;

        if (error)
        {
            ostringstream str;
            str << "Read timeout of " << URL() << ": " << error.message() << " (" << error << ")";// << endl;
            Error(str);

            PostClose();
            return;

        }

        if (!is_open())
        {
            // For example: Here we could schedule a new accept if we
            // would not want to allow two connections at the same time.
            return;
        }

        // Check whether the deadline has passed. We compare the deadline
        // against the current time since a new asynchronous operation
        // may have moved the deadline before this actor had a chance
        // to run.
        if (fInTimeout.expires_at() > ba::deadline_timer::traits_type::now())
            return;

        Error("Timeout reading data from "+URL());

        PostClose();
    }


    template<size_t N>
    void PostCmd(boost::array<uint16_t, N> dat, uint16_t u1=0, uint16_t u2=0, uint16_t u3=0, uint16_t u4=0)
    {
        boost::array<uint16_t, 5> cmd = {{ '@', u1, u2, u3, u4 }};

        ostringstream msg;
        msg << "Sending command:" << hex;
        msg << " 0x" << setw(4) << setfill('0') << cmd[0];
        msg << " 0x" << setw(4) << setfill('0') << u1;
        msg << " 0x" << setw(4) << setfill('0') << u2;
        msg << " 0x" << setw(4) << setfill('0') << u3;
        msg << " 0x" << setw(4) << setfill('0') << u4;
        msg << " (+" << dec << dat.size() << " words)";
        Message(msg);

        vector<uint16_t> out(cmd.size()+dat.size());

        transform(cmd.begin(), cmd.end(), out.begin(), htons);
        transform(dat.begin(), dat.end(), out.begin()+cmd.size(), htons);

        PostMessage(out);
    }

    void PostCmd(vector<uint16_t> dat, uint16_t u1=0, uint16_t u2=0, uint16_t u3=0, uint16_t u4=0)
    {
        boost::array<uint16_t, 5> cmd = {{ '@', u1, u2, u3, u4 }};

        ostringstream msg;
        msg << "Sending command:" << hex;
        msg << " 0x" << setw(4) << setfill('0') << cmd[0];
        msg << " 0x" << setw(4) << setfill('0') << u1;
        msg << " 0x" << setw(4) << setfill('0') << u2;
        msg << " 0x" << setw(4) << setfill('0') << u3;
        msg << " 0x" << setw(4) << setfill('0') << u4;
        msg << " (+" << dec << dat.size() << " words)";
        Message(msg);

        vector<uint16_t> out(cmd.size()+dat.size());

        transform(cmd.begin(), cmd.end(), out.begin(), htons);
        copy(dat.begin(), dat.end(), out.begin()+cmd.size());

        PostMessage(out);
    }

    void PostCmd(uint16_t u1=0, uint16_t u2=0, uint16_t u3=0, uint16_t u4=0)
    {
        PostCmd(boost::array<uint16_t, 0>(), u1, u2, u3, u4);
    }
public:

//    static const uint16_t kMaxAddr;

public:
    ConnectionFTM(ba::io_service& ioservice, MessageImp &imp) : Connection(ioservice, imp()),
        fIsVerbose(true), fIsDynamicOut(true), fIsHexOutput(true)
    {
        SetLogStream(&imp);
    }

    void CmdToggleLed()
    {
        PostCmd(FTM::kCmdToggleLed);
    }

    void CmdPing()
    {
        PostCmd(FTM::kCmdPing);
    }

    void CmdReqDynDat()
    {
        PostCmd(FTM::kCmdRead, FTM::kCmdDynamicData);
    }

    void CmdReqStatDat()
    {
        PostCmd(FTM::kCmdRead, FTM::kCmdStaticData);
    }

    void CmdSendStatDat(const FTM::StaticData &data)
    {
        fBufStaticData = data;

        PostCmd(data.HtoN(), FTM::kCmdWrite, FTM::kCmdStaticData);

        // Request the changed configuration to ensure the
        // change is distributed in the network
        CmdReqStatDat();
    }

    void CmdStartRun()
    {
        PostCmd(FTM::kCmdStartRun, FTM::kStartRun);

        // Update state information by requesting a new header
        CmdGetRegister(0);
    }

    void CmdStopRun()
    {
        PostCmd(FTM::kCmdStopRun);

        // Update state information by requesting a new header
        CmdGetRegister(0);
    }

    void CmdTakeNevents(uint32_t n)
    {
        const boost::array<uint16_t, 2> data = {{ uint16_t(n>>16), uint16_t(n&0xffff) }};
        PostCmd(data, FTM::kCmdStartRun, FTM::kTakeNevents);

        // Update state information by requesting a new header
        CmdGetRegister(0);
    }

    bool CmdSetRegister(uint16_t addr, uint16_t val)
    {
        if (addr>FTM::StaticData::kMaxAddr)
            return false;

        const boost::array<uint16_t, 2> data = {{ addr, val }};
        PostCmd(data, FTM::kCmdWrite, FTM::kCmdRegister);

        fBufStaticData.clear();

        // Request the changed configuration to ensure the
        // change is distributed in the network
        CmdReqStatDat();

        return true;
    }

    bool CmdGetRegister(uint16_t addr)
    {
        if (addr>FTM::StaticData::kMaxAddr)
            return false;

        const boost::array<uint16_t, 1> data = {{ addr }};
        PostCmd(data, FTM::kCmdRead, FTM::kCmdRegister);

        return true;
    }

    bool CmdResetCrate(uint16_t addr)
    {
        if (addr>3)
            return false;

        PostCmd(FTM::kCmdCrateReset, 1<<addr);

        return true;
    }

    bool CmdResetCamera()
    {
        PostCmd(FTM::kCmdCrateReset, FTM::kResetCrate0);
        PostCmd(FTM::kCmdCrateReset, FTM::kResetCrate1);
        PostCmd(FTM::kCmdCrateReset, FTM::kResetCrate2);
        PostCmd(FTM::kCmdCrateReset, FTM::kResetCrate3);

        return true;
    }

    bool CmdDisableReports(bool b)
    {
        PostCmd(FTM::kCmdDisableReports, b ? uint16_t(0) : uint16_t(1));
        return true;
    }

    void SetVerbose(bool b)
    {
        fIsVerbose = b;
    }

    void SetHexOutput(bool b)
    {
        fIsHexOutput = b;
    }

    void SetDynamicOut(bool b)
    {
        fIsDynamicOut = b;
    }
/*
    void SetDefaultSetup(const string &file)
    {
        fDefaultSetup = file;
    }
*/

    bool LoadStaticData(string name)
    {
        if (name.rfind(".bin")!=name.length()-4)
            name += ".bin";

        ifstream fin(name);
        if (!fin)
            return false;

        FTM::StaticData data;

        fin.read(reinterpret_cast<char*>(&data), sizeof(FTM::StaticData));

        if (fin.gcount()<streamsize(sizeof(FTM::StaticData)))
            return false;

        if (fin.fail() || fin.eof())
            return false;

        if (fin.peek()!=-1)
            return false;

        CmdSendStatDat(data);

        return true;
    }

    bool SaveStaticData(string name) const
    {
        if (name.rfind(".bin")!=name.length()-4)
            name += ".bin";

        ofstream fout(name);
        if (!fout)
            return false;

        fout.write(reinterpret_cast<const char*>(&fStaticData), sizeof(FTM::StaticData));

        return !fout.bad();
    }

    bool SetThreshold(int32_t patch, int32_t value)
    {
        if (patch>159)
            return false;

        if (value<0 || value>0xffff)
            return false;

        FTM::StaticData data(fStaticData);

        if (patch<0)
        {
            bool ident = true;
            for (int i=0; i<160; i++)
                if (data[i/4].fDAC[patch%4] != value)
                {
                    ident = false;
                    break;
                }

            if (ident)
                return true;

            for (int i=0; i<160; i++)
                data[i/4].fDAC[i%4] = value;
        }
        else
        {
            if (data[patch/4].fDAC[patch%4] == value)
                return true;

            data[patch/4].fDAC[patch%4] = value;
        }

        // Maybe move to a "COMMIT" command?
        CmdSendStatDat(data);

        return true;
    }

    bool SetPrescaling(uint32_t value)
    {
        if (value>0xffff)
            return false;

        FTM::StaticData data(fStaticData);

        bool ident = true;
        for (int i=0; i<40; i++)
            if (data[i].fPrescaling != value)
            {
                ident = false;
                break;
            }

        if (ident)
            return true;

        data.SetPrescaling(value);

        // Maybe move to a "COMMIT" command?
        CmdSendStatDat(data);

        return true;
    }

    bool EnableFTU(int32_t board, bool enable)
    {
        if (board>39)
            return false;

        FTM::StaticData data(fStaticData);

        if (board<0)
        {
            if (enable)
                data.EnableAllFTU();
            else
                data.DisableAllFTU();
        }
        else
        {
            if (enable)
                data.EnableFTU(board);
            else
                data.DisableFTU(board);

        }

        // Maybe move to a "COMMIT" command?
        CmdSendStatDat(data);

        return true;
    }

    bool ToggleFTU(uint32_t board)
    {
        if (board>39)
            return false;

        FTM::StaticData data(fStaticData);

        data.ToggleFTU(board);

        // Maybe move to a "COMMIT" command?
        CmdSendStatDat(data);

        return true;
    }

    bool SetVal(uint16_t *dest, uint32_t val, uint32_t max)
    {
        if (val>max)
            return false;

        if (*dest==val)
            return true;

        FTM::StaticData data(fStaticData);

        dest = reinterpret_cast<uint16_t*>(&data) + (dest - reinterpret_cast<uint16_t*>(&fStaticData));

        *dest = val;

        CmdSendStatDat(data);

        return true;
    }

    bool SetTriggerInterval(uint32_t val)
    {
        return SetVal(&fStaticData.fTriggerInterval, val,
                      FTM::StaticData::kMaxTriggerInterval);
    }

    bool SetTriggerDelay(uint32_t val)
    {
        return SetVal(&fStaticData.fDelayTrigger, val,
                      FTM::StaticData::kMaxDelayTrigger);
    }

    bool SetTimeMarkerDelay(uint32_t val)
    {
        return SetVal(&fStaticData.fDelayTimeMarker, val,
                      FTM::StaticData::kMaxDelayTimeMarker);
    }

    bool SetDeadTime(uint32_t val)
    {
        return SetVal(&fStaticData.fDeadTime, val,
                      FTM::StaticData::kMaxDeadTime);
    }

    void Enable(FTM::StaticData::GeneralSettings type, bool enable)
    {
        //if (fStaticData.IsEnabled(type)==enable)
        //    return;

        FTM::StaticData data(fStaticData);
        data.Enable(type, enable);
        CmdSendStatDat(data);
    }

    bool SetTriggerSeq(const uint16_t d[3])
    {
	if (d[0]>FTM::StaticData::kMaxSequence ||
            d[1]>FTM::StaticData::kMaxSequence ||
            d[2]>FTM::StaticData::kMaxSequence)
            return false;

        FTM::StaticData data(fStaticData);

        /*
         data.Enable(FTM::StaticData::kPedestal, d[0]>0);
         data.Enable(FTM::StaticData::kLPext,    d[1]>0);
         data.Enable(FTM::StaticData::kLPint,    d[2]>0);
         */

        data.SetSequence(d[0], d[2], d[1]);

        //if (fStaticData.fTriggerSeq     !=data.fTriggerSequence ||
        //    fStaticData.fGeneralSettings!=data.fGeneralSettings)
        //    CmdSendStatDat(data);

        CmdSendStatDat(data);

        return true;
    }

    bool SetTriggerMultiplicity(uint16_t n)
    {
        if (n==0 || n>FTM::StaticData::kMaxMultiplicity)
            return false;

        if (n==fStaticData.fMultiplicityPhysics)
            return true;

        FTM::StaticData data(fStaticData);

        data.fMultiplicityPhysics = n;

        CmdSendStatDat(data);

        return true;
    }

    bool SetTriggerWindow(uint16_t win)
    {
        if (win>FTM::StaticData::kMaxWindow)
            return false;

        if (win==fStaticData.fWindowPhysics)
            return true;

        FTM::StaticData data(fStaticData);

        data.fWindowPhysics = win;

        CmdSendStatDat(data);

        return true;
    }

    bool SetCalibMultiplicity(uint16_t n)
    {
        if (n==0 || n>FTM::StaticData::kMaxMultiplicity)
            return false;

        if (n==fStaticData.fMultiplicityCalib)
            return true;

        FTM::StaticData data(fStaticData);

        data.fMultiplicityCalib = n;

        CmdSendStatDat(data);

        return true;
    }

    bool SetCalibWindow(uint16_t win)
    {
        if (win>FTM::StaticData::kMaxWindow)
            return false;

        if (win==fStaticData.fWindowCalib)
            return true;

        FTM::StaticData data(fStaticData);

        data.fWindowCalib = win;

        CmdSendStatDat(data);

        return true;
    }

    bool SetClockRegister(const uint64_t reg[])
    {
        FTM::StaticData data(fStaticData);

        for (int i=0; i<8; i++)
            if (reg[i]>0xffffffff)
                return false;

        data.SetClockRegister(reg);

        CmdSendStatDat(data);

        return true;
    }

    bool EnablePixel(int16_t idx, bool enable)
    {
        if (idx<-1 || idx>FTM::StaticData::kMaxPixelIdx)
            return false;

        FTM::StaticData data(fStaticData);

        if (idx==-1)
            for (int i=0; i<=FTM::StaticData::kMaxPixelIdx; i++)
                data.EnablePixel(i, enable);
        else
            data.EnablePixel(idx, enable);

        CmdSendStatDat(data);

        return true;
    }

    bool DisableAllPixelsExcept(uint16_t idx)
    {
        if (idx>FTM::StaticData::kMaxPixelIdx)
            return false;

        FTM::StaticData data(fStaticData);

        for (int i=0; i<=FTM::StaticData::kMaxPixelIdx; i++)
            data.EnablePixel(i, i==idx);

        CmdSendStatDat(data);

        return true;
    }

    bool DisableAllPatchesExcept(uint16_t idx)
    {
        if (idx>FTM::StaticData::kMaxPatchIdx)
            return false;

        FTM::StaticData data(fStaticData);

        for (int i=0; i<=FTM::StaticData::kMaxPixelIdx; i++)
            data.EnablePixel(i, i/9==idx);

        CmdSendStatDat(data);

        return true;
    }

    bool TogglePixel(uint16_t idx)
    {
        if (idx>FTM::StaticData::kMaxPixelIdx)
            return false;

        FTM::StaticData data(fStaticData);

        data.EnablePixel(idx, !fStaticData.Enabled(idx));

        CmdSendStatDat(data);

        return true;
    }

    States GetState() const
    {
        if (!IsConnected())
            return kDisconnected;

        switch (fHeader.fState)
        {
        case FTM::kFtmUndefined:
            return kConnected;

        case FTM::kFtmRunning:
        case FTM::kFtmCalib:
            return kTakingData;

        case FTM::kFtmIdle:
        case FTM::kFtmConfig:
            return fStaticData == fBufStaticData ? kConfigured : kIdle;
        }

        throw runtime_error("ConnectionFTM::GetState - Impossible code reached.");
    }

    int GetCounter(FTM::Types type) { return fCounter[type]; }

    const FTM::StaticData &GetStaticData() const { return fStaticData; }
};

//const uint16_t ConnectionFTM::kMaxAddr = 0xfff;

// ------------------------------------------------------------------------

#include "DimDescriptionService.h"

class ConnectionDimFTM : public ConnectionFTM
{
private:

    DimDescribedService fDimPassport;
    DimDescribedService fDimTriggerCounter;
    DimDescribedService fDimError;
    DimDescribedService fDimFtuList;
    DimDescribedService fDimStaticData;
    DimDescribedService fDimDynamicData;
    DimDescribedService fDimCounter;

    void UpdateFirstHeader()
    {
        ConnectionFTM::UpdateFirstHeader();

        const FTM::DimPassport data(fHeader);
        fDimPassport.Update(data);
    }

    void UpdateHeader()
    {
        ConnectionFTM::UpdateHeader();

        if (fHeader.fType!=FTM::kDynamicData)
            return;

        const FTM::DimTriggerCounter data(fHeader);
        fDimTriggerCounter.Update(data);
    }

    void UpdateFtuList()
    {
        ConnectionFTM::UpdateFtuList();

        const FTM::DimFtuList data(fHeader, fFtuList);
        fDimFtuList.Update(data);
    }

    void UpdateStaticData()
    {
        ConnectionFTM::UpdateStaticData();

        const FTM::DimStaticData data(fHeader, fStaticData);
        fDimStaticData.Update(data);
    }

    void UpdateDynamicData()
    {
        ConnectionFTM::UpdateDynamicData();

        const FTM::DimDynamicData data(fHeader, fDynamicData);
        fDimDynamicData.Update(data);
    }

    void UpdateError()
    {
        ConnectionFTM::UpdateError();

        const FTM::DimError data(fHeader, fError);
        fDimError.Update(data);
    }

    void UpdateCounter()
    {
        ConnectionFTM::UpdateCounter();

        const uint32_t counter[6] =
        {
            fCounter[FTM::kHeader],
            fCounter[FTM::kStaticData],
            fCounter[FTM::kDynamicData],
            fCounter[FTM::kFtuList],
            fCounter[FTM::kErrorList],
            fCounter[FTM::kRegister],
        };

        fDimCounter.Update(counter);
    }

public:
    ConnectionDimFTM(ba::io_service& ioservice, MessageImp &imp) :
        ConnectionFTM(ioservice, imp),
        fDimPassport      ("FTM_CONTROL/PASSPORT",        "X:1;S:1", ""),
        fDimTriggerCounter("FTM_CONTROL/TRIGGER_COUNTER", "X:1;I:1", ""),
        fDimError         ("FTM_CONTROL/ERROR",           "X:1;S:1;S:28", ""),
        fDimFtuList       ("FTM_CONTROL/FTU_LIST",        "X:1;X:1;S:1;C:4;X:40;C:40;C:40",  ""),
        fDimStaticData    ("FTM_CONTROL/STATIC_DATA",     "X:1;S:1;S:1;X:1;S:1;S:3;S:1;S:1;S:1;S:1;S:1;S:1;I:1;I:8;S:90;S:160;S:40;S:40", ""),
        fDimDynamicData   ("FTM_CONTROL/DYNAMIC_DATA",    "X:1;X:1;F:4;I:160;I:40;S:40;S:40", ""),
        fDimCounter       ("FTM_CONTROL/COUNTER",         "I:6", "")
    {
    }

    // A B [C] [D] E [F] G H [I] J K [L] M N O P Q R [S] T U V W [X] Y Z
};

// ------------------------------------------------------------------------

template <class T, class S>
class StateMachineFTM : public T, public ba::io_service, public ba::io_service::work
{
    int Wrap(boost::function<void()> f)
    {
        f();
        return T::GetCurrentState();
    }

    boost::function<int(const EventImp &)> Wrapper(boost::function<void()> func)
    {
        return boost::bind(&StateMachineFTM::Wrap, this, func);
    }

private:
    S fFTM;

    bool CheckEventSize(size_t has, const char *name, size_t size)
    {
        if (has==size)
            return true;

        ostringstream msg;
        msg << name << " - Received event has " << has << " bytes, but expected " << size << ".";
        T::Fatal(msg);
        return false;
    }

    int SetRegister(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetRegister", 8))
            return T::kSM_FatalError;

        const uint32_t *dat = evt.Ptr<uint32_t>();

        if (dat[1]>uint16_t(-1))
        {
            ostringstream msg;
            msg << hex << "Value " << dat[1] << " out of range.";
            T::Error(msg);
            return T::GetCurrentState();
        }


        if (dat[0]>uint16_t(-1) || !fFTM.CmdSetRegister(dat[0], dat[1]))
        {
            ostringstream msg;
            msg << hex << "Address " << dat[0] << " out of range.";
            T::Error(msg);
        }

        return T::GetCurrentState();
    }

    int GetRegister(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "GetRegister", 4))
            return T::kSM_FatalError;

        const unsigned int addr = evt.GetInt();
        if (addr>uint16_t(-1) || !fFTM.CmdGetRegister(addr))
        {
            ostringstream msg;
            msg << hex << "Address " << addr << " out of range.";
            T::Error(msg);
        }

        return T::GetCurrentState();
    }

    int TakeNevents(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "TakeNevents", 4))
            return T::kSM_FatalError;

        const unsigned int dat = evt.GetUInt();

        /*
        if (dat[1]>uint32_t(-1))
        {
            ostringstream msg;
            msg << hex << "Value " << dat[1] << " out of range.";
            T::Error(msg);
            return T::GetCurrentState();
        }*/

        fFTM.CmdTakeNevents(dat);

        return T::GetCurrentState();
    }

    int DisableReports(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "DisableReports", 1))
            return T::kSM_FatalError;

        fFTM.CmdDisableReports(evt.GetBool());

        return T::GetCurrentState();
    }

    int SetVerbosity(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetVerbosity", 1))
            return T::kSM_FatalError;

        fFTM.SetVerbose(evt.GetBool());

        return T::GetCurrentState();
    }

    int SetHexOutput(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetHexOutput", 1))
            return T::kSM_FatalError;

        fFTM.SetHexOutput(evt.GetBool());

        return T::GetCurrentState();
    }

    int SetDynamicOut(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetDynamicOut", 1))
            return T::kSM_FatalError;

        fFTM.SetDynamicOut(evt.GetBool());

        return T::GetCurrentState();
    }

    int LoadStaticData(const EventImp &evt)
    {
        if (fFTM.LoadStaticData(evt.GetString()))
            return T::GetCurrentState();

        ostringstream msg;
        msg << "Loading static data from file '" << evt.GetString() << "' failed ";

        if (errno)
            msg << "(" << strerror(errno) << ")";
        else
            msg << "(wrong size, expected " << sizeof(FTM::StaticData) << " bytes)";

        T::Warn(msg);

        return T::GetCurrentState();
    }

    int SaveStaticData(const EventImp &evt)
    {
        if (fFTM.SaveStaticData(evt.GetString()))
            return T::GetCurrentState();

        ostringstream msg;
        msg << "Writing static data to file '" << evt.GetString() << "' failed ";
        msg << "(" << strerror(errno) << ")";

        T::Warn(msg);

        return T::GetCurrentState();
    }

    int SetThreshold(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetThreshold", 8))
            return T::kSM_FatalError;

        const int32_t *data = evt.Ptr<int32_t>();

        if (!fFTM.SetThreshold(data[0], data[1]))
            T::Warn("SetThreshold - Maximum allowed patch number 159, valid value range 0-0xffff");

        return T::GetCurrentState();
    }

    int EnableFTU(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "EnableFTU", 5))
            return T::kSM_FatalError;

        const int32_t &board  = evt.Get<int32_t>();
        const int8_t  &enable = evt.Get<int8_t>(4);

        if (!fFTM.EnableFTU(board, enable))
            T::Warn("EnableFTU - Board number must be <40.");

        return T::GetCurrentState();
    }

    int ToggleFTU(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "ToggleFTU", 4))
            return T::kSM_FatalError;

        if (!fFTM.ToggleFTU(evt.GetInt()))
            T::Warn("ToggleFTU - Allowed range of boards 0-39.");

        return T::GetCurrentState();
    }

    int SetTriggerInterval(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetTriggerInterval", 4))
            return T::kSM_FatalError;

        if (!fFTM.SetTriggerInterval(evt.GetInt()))
            T::Warn("SetTriggerInterval - Value out of range.");

        return T::GetCurrentState();
    }

    int SetTriggerDelay(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetTriggerDelay", 4))
            return T::kSM_FatalError;

        if (!fFTM.SetTriggerDelay(evt.GetInt()))
            T::Warn("SetTriggerDealy - Value out of range.");

        return T::GetCurrentState();
    }

    int SetTimeMarkerDelay(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetTimeMarkerDelay", 4))
            return T::kSM_FatalError;

        if (!fFTM.SetTimeMarkerDelay(evt.GetInt()))
            T::Warn("SetTimeMarkerDelay - Value out of range.");

        return T::GetCurrentState();
    }

    int SetPrescaling(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetPrescaling", 4))
            return T::kSM_FatalError;

        if (!fFTM.SetPrescaling(evt.GetInt()-1))
            T::Warn("SetPrescaling - Value out of range.");

        return T::GetCurrentState();
    }

    int SetTriggerSeq(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetTriggerSeq", 6))
            return T::kSM_FatalError;

        const uint16_t *data = evt.Ptr<uint16_t>();

        if (!fFTM.SetTriggerSeq(data))
            T::Warn("SetTriggerSeq - Value out of range.");

        return T::GetCurrentState();
    }

    int SetDeadTime(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetDeadTime", 4))
            return T::kSM_FatalError;

        if (!fFTM.SetDeadTime(evt.GetInt()))
            T::Warn("SetDeadTime - Value out of range.");

        return T::GetCurrentState();
    }

    int SetTriggerMultiplicity(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetTriggerMultiplicity", 2))
            return T::kSM_FatalError;

        if (!fFTM.SetTriggerMultiplicity(evt.GetUShort()))
            T::Warn("SetTriggerMultiplicity -  Value out of range.");

        return T::GetCurrentState();
    }

    int SetCalibMultiplicity(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetCalibMultiplicity", 2))
            return T::kSM_FatalError;

        if (!fFTM.SetCalibMultiplicity(evt.GetUShort()))
            T::Warn("SetCalibMultiplicity -  Value out of range.");

        return T::GetCurrentState();
    }

    int SetTriggerWindow(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetTriggerWindow", 2))
            return T::kSM_FatalError;

        if (!fFTM.SetTriggerWindow(evt.GetUShort()))
            T::Warn("SetTriggerWindow -  Value out of range.");

        return T::GetCurrentState();
    }

    int SetCalibWindow(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetCalibWindow", 2))
            return T::kSM_FatalError;

        if (!fFTM.SetCalibWindow(evt.GetUShort()))
            T::Warn("SetCalibWindow -  Value out of range.");

        return T::GetCurrentState();
    }

    int SetClockRegister(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetClockRegister", 8*8))
            return T::kSM_FatalError;

        const uint64_t *reg = evt.Ptr<uint64_t>();

        if (!fFTM.SetClockRegister(reg))
            T::Warn("SetClockRegister - Value out of range.");

        return T::GetCurrentState();
    }

    int SetClockFrequency(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "SetClockFrequency", 4))
            return T::kSM_FatalError;

        const map<uint16_t,boost::array<uint64_t, 8>>::const_iterator it =
            fClockCondSetup.find(evt.GetUInt());

        if (it==fClockCondSetup.end())
        {
            T::Warn("SetClockFrequency - Frequency not supported.");
            return T::GetCurrentState();
        }

        if (!fFTM.SetClockRegister(it->second.data()))
            T::Warn("SetClockFrequency - Register values out of range.");

        return T::GetCurrentState();
    }

    int Enable(const EventImp &evt, FTM::StaticData::GeneralSettings type)
    {
        if (!CheckEventSize(evt.GetSize(), "Enable", 1))
            return T::kSM_FatalError;

        fFTM.Enable(type, evt.GetBool());

        return T::GetCurrentState();
    }

    int EnablePixel(const EventImp &evt, bool b)
    {
        if (!CheckEventSize(evt.GetSize(), "EnablePixel", 2))
            return T::kSM_FatalError;

        if (!fFTM.EnablePixel(evt.GetUShort(), b))
            T::Warn("EnablePixel -  Value out of range.");

        return T::GetCurrentState();
    }

    int DisableAllPixelsExcept(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "DisableAllPixelsExcept", 2))
            return T::kSM_FatalError;

        if (!fFTM.DisableAllPixelsExcept(evt.GetUShort()))
            T::Warn("DisableAllPixelsExcept -  Value out of range.");

        return T::GetCurrentState();
    }

    int DisableAllPatchesExcept(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "DisableAllPatchesExcept", 2))
            return T::kSM_FatalError;

        if (!fFTM.DisableAllPatchesExcept(evt.GetUShort()))
            T::Warn("DisableAllPatchesExcept -  Value out of range.");

        return T::GetCurrentState();
    }

    int TogglePixel(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "TogglePixel", 2))
            return T::kSM_FatalError;

        if (!fFTM.TogglePixel(evt.GetUShort()))
            T::Warn("TogglePixel -  Value out of range.");

        return T::GetCurrentState();
    }

    int ResetCrate(const EventImp &evt)
    {
        if (!CheckEventSize(evt.GetSize(), "ResetCrate", 2))
            return T::kSM_FatalError;

        fFTM.CmdResetCrate(evt.GetUShort());

        return T::GetCurrentState();
    }

    int Disconnect()
    {
        // Close all connections
        fFTM.PostClose(false);

        /*
         // Now wait until all connection have been closed and
         // all pending handlers have been processed
         poll();
         */

        return T::GetCurrentState();
    }

    int Reconnect(const EventImp &evt)
    {
        // Close all connections to supress the warning in SetEndpoint
        fFTM.PostClose(false);

        // Now wait until all connection have been closed and
        // all pending handlers have been processed
        poll();

        if (evt.GetBool())
            fFTM.SetEndpoint(evt.GetString());

        // Now we can reopen the connection
        fFTM.PostClose(true);

        return T::GetCurrentState();
    }

    /*
    int Transition(const Event &evt)
    {
        switch (evt.GetTargetState())
        {
        case kDisconnected:
        case kConnected:
        }

        return T::kSM_FatalError;
    }*/

    int64_t fCounterReg;
    int64_t fCounterStat;

    typedef map<string, FTM::StaticData> Configs;
    Configs fConfigs;
    Configs::const_iterator fTargetConfig;

    int ConfigureFTM(const EventImp &evt)
    {
        const string name = evt.GetText();

        fTargetConfig = fConfigs.find(name);
        if (fTargetConfig==fConfigs.end())
        {
            T::Error("ConfigureFTM - Run-type '"+name+"' not found.");
            return T::GetCurrentState();
        }

        T::Message("Starting configuration for '"+name+"'");

        fCounterReg = fFTM.GetCounter(FTM::kRegister);
        fFTM.CmdStopRun();

        return FTM::kConfiguring1;
    }

    int ResetConfig()
    {
        return fFTM.GetState();
    }

    int Execute()
    {
        // Dispatch (execute) at most one handler from the queue. In contrary
        // to run_one(), it doesn't wait until a handler is available
        // which can be dispatched, so poll_one() might return with 0
        // handlers dispatched. The handlers are always dispatched/executed
        // synchronously, i.e. within the call to poll_one()
        poll_one();

        // If FTM is neither in data taking nor idle,
        // leave configuration state
        switch (fFTM.GetState())
        {
        case ConnectionFTM::kDisconnected: return FTM::kDisconnected;
        case ConnectionFTM::kConnected:    return FTM::kConnected;
        default:
            break;
        }

        switch (T::GetCurrentState())
        {
        case FTM::kConfiguring1:
            // If FTM has received an anwer to the stop_run command
            // the counter for the registers has been increased
            if (fFTM.GetCounter(FTM::kRegister)<=fCounterReg)
                break;

            // If now the state is not idle as expected this means we had
            // an error (maybe old events waiting in the queue)
            if (fFTM.GetState()!=ConnectionFTM::kIdle &&
                fFTM.GetState()!=ConnectionFTM::kConfigured)
                return FTM::kConfigError1;

            fCounterStat = fFTM.GetCounter(FTM::kStaticData);

            T::Message("Trigger successfully disabled... sending new configuration.");

            fFTM.CmdSendStatDat(fTargetConfig->second);

            // Next state is: wait for the answer to our configuration
            return FTM::kConfiguring2;

        case FTM::kConfiguring2:
        case FTM::kConfigured:
            // If FTM has received an anwer to the stop_run command
            // the counter for the registers has been increased
            if (fFTM.GetCounter(FTM::kStaticData)<=fCounterStat)
                break;

            // If now the configuration is not what we expected
            // we had an error (maybe old events waiting in the queue?)
            // ======================
            if (fFTM.GetState()!=ConnectionFTM::kConfigured)
                return FTM::kConfigError2;
            // ======================

            // Check configuration again when a new static data block
            // will be received
            fCounterStat = fFTM.GetCounter(FTM::kStaticData);

            T::Message("Sending new configuration was successfull.");

            // Next state is: wait for the answer to our configuration
            return FTM::kConfigured;

        default:
            switch (fFTM.GetState())
            {
            case ConnectionFTM::kIdle:         return FTM::kIdle;
            case ConnectionFTM::kConfigured:   return FTM::kIdle;
            case ConnectionFTM::kTakingData:   return FTM::kTakingData;
            default:
                throw runtime_error("StateMachienFTM - Execute() - Inavlid state.");
            }
        }

        if (T::GetCurrentState()==FTM::kConfigured &&
            fFTM.GetState()==ConnectionFTM::kTakingData)
            return FTM::kTakingData;

        return T::GetCurrentState();
    }

public:
    StateMachineFTM(ostream &out=cout) :
        T(out, "FTM_CONTROL"), ba::io_service::work(static_cast<ba::io_service&>(*this)),
        fFTM(*this, *this)
    {
        // ba::io_service::work is a kind of keep_alive for the loop.
        // It prevents the io_service to go to stopped state, which
        // would prevent any consecutive calls to run()
        // or poll() to do nothing. reset() could also revoke to the
        // previous state but this might introduce some overhead of
        // deletion and creation of threads and more.

        // State names
        T::AddStateName(FTM::kDisconnected, "Disconnected",
                        "FTM board not connected via ethernet.");

        T::AddStateName(FTM::kConnected, "Connected",
                        "Ethernet connection to FTM established (no state received yet).");

        T::AddStateName(FTM::kIdle, "Idle",
                        "Ethernet connection to FTM established, FTM in idle state.");

        T::AddStateName(FTM::kConfiguring1, "Configuring1",
                        "Command to diable run sent... waiting for response.");
        T::AddStateName(FTM::kConfiguring2, "Configuring2",
                        "New configuration sent... waiting for response.");
        T::AddStateName(FTM::kConfigured,   "Configured",
                        "Received answer identical with target configuration.");

        T::AddStateName(FTM::kTakingData, "TakingData",
                        "Ethernet connection to FTM established, FTM is in taking data state.");

        T::AddStateName(FTM::kConfigError1, "ErrorInConfig1", "");
        T::AddStateName(FTM::kConfigError2, "ErrorInConfig2", "");

        // FTM Commands
        T::AddEvent("TOGGLE_LED", FTM::kIdle)
            (Wrapper(boost::bind(&ConnectionFTM::CmdToggleLed, &fFTM)))
            ("toggle led");

        T::AddEvent("PING", FTM::kIdle)
            (Wrapper(boost::bind(&ConnectionFTM::CmdPing, &fFTM)))
            ("send ping");

        T::AddEvent("REQUEST_DYNAMIC_DATA", FTM::kIdle)
            (Wrapper(boost::bind(&ConnectionFTM::CmdReqDynDat, &fFTM)))
            ("request transmission of dynamic data block");

        T::AddEvent("REQUEST_STATIC_DATA", FTM::kIdle)
            (Wrapper(boost::bind(&ConnectionFTM::CmdReqStatDat, &fFTM)))
            ("request transmission of static data from FTM to memory");

        T::AddEvent("GET_REGISTER", "I", FTM::kIdle)
            (boost::bind(&StateMachineFTM::GetRegister, this, _1))
            ("read register from address addr"
            "|addr[short]:Address of register");

        T::AddEvent("SET_REGISTER", "I:2", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetRegister, this, _1))
            ("set register to value"
            "|addr[short]:Address of register"
            "|val[short]:Value to be set");

        T::AddEvent("START_RUN", FTM::kIdle, FTM::kConfigured)
            (Wrapper(boost::bind(&ConnectionFTM::CmdStartRun, &fFTM)))
            ("start a run (start distributing triggers)");

        T::AddEvent("STOP_RUN", FTM::kTakingData)
            (Wrapper(boost::bind(&ConnectionFTM::CmdStopRun, &fFTM)))
            ("stop a run (stop distributing triggers)");

        T::AddEvent("TAKE_N_EVENTS", "I", FTM::kIdle)
            (boost::bind(&StateMachineFTM::TakeNevents, this, _1))
            ("take n events (distribute n triggers)|number[int]:Number of events to be taken");

        T::AddEvent("DISABLE_REPORTS", "B", FTM::kIdle)
            (boost::bind(&StateMachineFTM::DisableReports, this, _1))
            ("disable sending rate reports"
             "|status[bool]:disable or enable that the FTM sends rate reports (yes/no)");

        T::AddEvent("SET_THRESHOLD", "I:2", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetThreshold, this, _1))
            ("Set the comparator threshold"
             "|Patch[idx]:Index of the patch (0-159), -1 for all"
             "|Threshold[counts]:Threshold to be set in binary counts");

        T::AddEvent("SET_PRESCALING", "I:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetPrescaling, this, _1))
            (""
             "|[]:");

        T::AddEvent("ENABLE_FTU", "I:1;B:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::EnableFTU, this, _1))
            ("Enable or disable FTU"
             "|Board[idx]:Index of the board (0-39), -1 for all"
             "|Enable[bool]:Whether FTU should be enabled or disabled (yes/no)");

        T::AddEvent("DISABLE_PIXEL", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::EnablePixel, this, _1, false))
            ("(-1 or all)");

        T::AddEvent("ENABLE_PIXEL", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::EnablePixel, this, _1, true))
            ("(-1 or all)");

        T::AddEvent("DISABLE_ALL_PIXELS_EXCEPT", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::DisableAllPixelsExcept, this, _1))
            ("");

        T::AddEvent("DISABLE_ALL_PATCHES_EXCEPT", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::DisableAllPatchesExcept, this, _1))
            ("");

        T::AddEvent("TOGGLE_PIXEL", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::TogglePixel, this, _1))
            ("");

        T::AddEvent("TOGGLE_FTU", "I:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::ToggleFTU, this, _1))
            ("Toggle status of FTU (this is mainly meant to be used in the GUI)"
             "|Board[idx]:Index of the board (0-39)");

        T::AddEvent("SET_TRIGGER_INTERVAL", "I:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetTriggerInterval, this, _1))
            ("Sets the trigger interval which is the distance between two consecutive artificial triggers."
             "|interval[int]:The applied trigger interval is: interval*4ns+8ns");

        T::AddEvent("SET_TRIGGER_DELAY", "I:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetTriggerDelay, this, _1))
            (""
             "|delay[int]:The applied trigger delay is: delay*4ns+8ns");

        T::AddEvent("SET_TIME_MARKER_DELAY", "I:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetTimeMarkerDelay, this, _1))
            (""
            "|delay[int]:The applied time marker delay is: delay*4ns+8ns");

        T::AddEvent("SET_DEAD_TIME", "I:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetDeadTime, this, _1))
            (""
            "|dead_time[int]:The applied dead time is: dead_time*4ns+8ns");

        T::AddEvent("ENABLE_TRIGGER", "B:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::Enable, this, _1, FTM::StaticData::kTrigger))
            ("Switch on the physics trigger"
             "|Enable[bool]:Enable physics trigger (yes/no)");

        // FIXME: Switch on/off depending on sequence
        T::AddEvent("ENABLE_EXT1", "B:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::Enable, this, _1, FTM::StaticData::kExt1))
            ("Switch on the triggers through the first external line"
             "|Enable[bool]:Enable ext1 trigger (yes/no)");

        // FIXME: Switch on/off depending on sequence
        T::AddEvent("ENABLE_EXT2", "B:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::Enable, this, _1, FTM::StaticData::kExt2))
            ("Switch on the triggers through the second external line"
             "|Enable[bool]:Enable ext2 trigger (yes/no)");

        T::AddEvent("ENABLE_VETO", "B:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::Enable, this, _1, FTM::StaticData::kVeto))
            ("Enable veto line"
             "|Enable[bool]:Enable veto (yes/no)");

        T::AddEvent("ENABLE_CLOCK_CONDITIONER", "B:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::Enable, this, _1, FTM::StaticData::kClockConditioner))
            ("Enable clock conidtioner output in favor of time marker output"
             "|Enable[bool]:Enable clock conditioner (yes/no)");

        T::AddEvent("SET_TRIGGER_SEQUENCE", "S:3", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetTriggerSeq, this, _1))
            ("Setup the sequence of artificial triggers produced by the FTM"
             "|Ped[short]:number of pedestal triggers in a row"
             "|LPext[short]:number of triggers of the external light pulser"
             "|LPint[short]:number of triggers of the internal light pulser");

        T::AddEvent("SET_TRIGGER_MULTIPLICITY", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetTriggerMultiplicity, this, _1))
            ("Setup the Multiplicity condition for physcis triggers"
             "|N[int]:Number of requirered coincident triggers from sum-patches (1-40)");

        T::AddEvent("SET_TRIGGER_WINDOW", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetTriggerWindow, this, _1))
            ("");

        T::AddEvent("SET_CALIBRATION_MULTIPLICITY", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetCalibMultiplicity, this, _1))
            ("Setup the Multiplicity condition for artificial (calibration) triggers"
             "|N[int]:Number of requirered coincident triggers from sum-patches (1-40)");

        T::AddEvent("SET_CALIBRATION_WINDOW", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetCalibWindow, this, _1))
            ("");

        T::AddEvent("SET_CLOCK_FREQUENCY", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetClockFrequency, this, _1))
            ("");

        T::AddEvent("SET_CLOCK_REGISTER", "X:8", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SetClockRegister, this, _1))
            ("");

        // A new configure will first stop the FTM this means
        // we can allow it in idle _and_ taking data
        T::AddEvent("CONFIGURE", "C", FTM::kIdle, FTM::kTakingData)
            (boost::bind(&StateMachineFTM::ConfigureFTM, this, _1))
            ("");

        T::AddEvent("RESET_CONFIGURE", FTM::kConfiguring1, FTM::kConfiguring2, FTM::kConfigured, FTM::kConfigError1, FTM::kConfigError2)
            (boost::bind(&StateMachineFTM::ResetConfig, this))
            ("");



        T::AddEvent("RESET_CRATE", "S:1", FTM::kIdle)
            (boost::bind(&StateMachineFTM::ResetCrate, this, _1))
            ("Reset one of the crates 0-3"
             "|crate[short]:Crate number to be reseted (0-3)");

        T::AddEvent("RESET_CAMERA", FTM::kIdle)
            (Wrapper(boost::bind(&ConnectionFTM::CmdResetCamera, &fFTM)))
            ("Reset all crates. The commands are sent in the order 0,1,2,3");


        // Load/save static data block
        T::AddEvent("SAVE", "C", FTM::kIdle)
            (boost::bind(&StateMachineFTM::SaveStaticData, this, _1))
            ("Saves the static data (FTM configuration) from memory to a file"
             "|filename[string]:Filename (can include a path), .bin is automatically added");

        T::AddEvent("LOAD", "C", FTM::kIdle)
            (boost::bind(&StateMachineFTM::LoadStaticData, this, _1))
            ("Loads the static data (FTM configuration) from a file into memory and sends it to the FTM"
             "|filename[string]:Filename (can include a path), .bin is automatically added");



        // Verbosity commands
        T::AddEvent("SET_VERBOSE", "B")
            (boost::bind(&StateMachineFTM::SetVerbosity, this, _1))
            ("set verbosity state"
             "|verbosity[bool]:disable or enable verbosity for received data (yes/no), except dynamic data");

        T::AddEvent("SET_HEX_OUTPUT", "B")
            (boost::bind(&StateMachineFTM::SetHexOutput, this, _1))
            ("enable or disable hex output for received data"
             "|hexout[bool]:disable or enable hex output for received data (yes/no)");

        T::AddEvent("SET_DYNAMIC_OUTPUT", "B")
            (boost::bind(&StateMachineFTM::SetDynamicOut, this, _1))
            ("enable or disable output for received dynamic data (data is still broadcasted via Dim)"
             "|dynout[bool]:disable or enable output for dynamic data (yes/no)");


        // Conenction commands
        T::AddEvent("DISCONNECT", FTM::kConnected, FTM::kIdle)
            (boost::bind(&StateMachineFTM::Disconnect, this))
            ("disconnect from ethernet");

        T::AddEvent("RECONNECT", "O", FTM::kDisconnected, FTM::kConnected, FTM::kIdle, FTM::kConfigured)
            (boost::bind(&StateMachineFTM::Reconnect, this, _1))
            ("(Re)connect ethernet connection to FTM, a new address can be given"
             "|[host][string]:new ethernet address in the form <host:port>");

        fFTM.StartConnect();
    }

    void SetEndpoint(const string &url)
    {
        fFTM.SetEndpoint(url);
    }

    map<uint16_t, boost::array<uint64_t, 8>> fClockCondSetup;

    template<class V>
    bool CheckConfigVal(const Configuration &conf, V max, const string &name, const string &sub)
    {
        if (!conf.HasDef(name, sub))
        {
            T::Error("Neither "+name+"default nor "+name+sub+" found.");
            return false;
        }

        const V val = conf.GetDef<V>(name, sub);

        if (val<=max)
            return true;

        ostringstream str;
        str << name << sub << "=" << val << " exceeds allowed maximum of " << max << "!";
        T::Error(str);

        return false;
    }

    int EvalConfiguration(const Configuration &conf)
    {
        // ---------- General setup ----------
        fFTM.SetVerbose(!conf.Get<bool>("quiet"));
        fFTM.SetHexOutput(conf.Get<bool>("hex-out"));
        fFTM.SetDynamicOut(conf.Get<bool>("dynamic-out"));

        // ---------- Setup clock conditioner frequencies ----------
        const vector<uint16_t> freq = conf.Vec<uint16_t>("clock-conditioner.frequency");
        if (freq.size()==0)
            T::Warn("No frequencies for the clock-conditioner defined.");
        else
            T::Message("Defining clock conditioner frequencies");
        for (vector<uint16_t>::const_iterator it=freq.begin();
             it!=freq.end(); it++)
        {
            if (fClockCondSetup.count(*it)>0)
            {
                T::Error("clock-conditioner frequency defined twice.");
                return 1;
            }

            if (!conf.HasDef("clock-conditioner.R0.",  *it) ||
                !conf.HasDef("clock-conditioner.R1.",  *it) ||
                !conf.HasDef("clock-conditioner.R8.",  *it) ||
                !conf.HasDef("clock-conditioner.R9.",  *it) ||
                !conf.HasDef("clock-conditioner.R11.", *it) ||
                !conf.HasDef("clock-conditioner.R13.", *it) ||
                !conf.HasDef("clock-conditioner.R14.", *it) ||
                !conf.HasDef("clock-conditioner.R15.", *it))
            {
                T::Error("clock-conditioner values incomplete.");
                return 1;
            }

            boost::array<uint64_t, 8> &arr = fClockCondSetup[*it];

            arr[0] = conf.GetDef<Hex<uint32_t>>("clock-conditioner.R0.",  *it);
            arr[1] = conf.GetDef<Hex<uint32_t>>("clock-conditioner.R1.",  *it);
            arr[2] = conf.GetDef<Hex<uint32_t>>("clock-conditioner.R8.",  *it);
            arr[3] = conf.GetDef<Hex<uint32_t>>("clock-conditioner.R9.",  *it);
            arr[4] = conf.GetDef<Hex<uint32_t>>("clock-conditioner.R11.", *it);
            arr[5] = conf.GetDef<Hex<uint32_t>>("clock-conditioner.R13.", *it);
            arr[6] = conf.GetDef<Hex<uint32_t>>("clock-conditioner.R14.", *it);
            arr[7] = conf.GetDef<Hex<uint32_t>>("clock-conditioner.R15.", *it);

            ostringstream out;
            out << " -> " << setw(4) << *it << "MHz:" << hex << setfill('0');
            for (int i=0; i<8; i++)
                out << " " << setw(8) << arr[i];
            T::Message(out.str());
        }

        // ---------- Setup run types ---------
        const vector<string> types = conf.Vec<string>("run-type");
        if (types.size()==0)
            T::Warn("No run-types defined.");
        else
            T::Message("Defining run-types");
        for (vector<string>::const_iterator it=types.begin();
             it!=types.end(); it++)
        {
            T::Message(" -> "+ *it);

            if (fConfigs.count(*it)>0)
            {
                T::Error("Run-type "+*it+" defined twice.");
                return 2;
            }

            FTM::StaticData data;

            const uint16_t frq = conf.GetDef<uint16_t>("sampling-frequency.", *it);
            if (fClockCondSetup.count(frq)==0)
            {
                T::Error("sampling-frequency."+*it+" - frequency not available.");
                return 2;
            }

            data.SetClockRegister(fClockCondSetup[frq].data());

            // Trigger sequence ped:lp1:lp2
            // (data. is used here as an abbreviation for FTM::StaticData::
            if (!CheckConfigVal<bool>    (conf, true,                     "enable-trigger.",             *it) ||
                !CheckConfigVal<bool>    (conf, true,                     "enable-external-1.",          *it) ||
                !CheckConfigVal<bool>    (conf, true,                     "enable-external-2.",          *it) ||
                !CheckConfigVal<bool>    (conf, true,                     "enable-veto.",                *it) ||
                !CheckConfigVal<bool>    (conf, true,                     "enable-clock-conditioner.",   *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxSequence,        "trigger-sequence-ped.",       *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxSequence,        "trigger-sequence-lp-ext.",    *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxSequence,        "trigger-sequence-lp-int.",    *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxTriggerInterval, "trigger-interval.",           *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxMultiplicity,    "multiplicity-physics.",       *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxMultiplicity,    "multiplicity-calib.",         *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxWindow,          "coincidence-window-physics.", *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxWindow,          "coincidence-window-calib.",   *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxDeadTime,        "dead-time.",                  *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxDelayTrigger,    "trigger-delay.",              *it) ||
                !CheckConfigVal<uint16_t>(conf, data.kMaxDelayTimeMarker, "time-marker-delay.",          *it) ||
                !CheckConfigVal<uint16_t>(conf, 0xffff,                   "ftu-report-interval.",        *it) ||
                0)
                return 2;

            data.Enable(data.kTrigger,          conf.GetDef<bool>("enable-trigger.",           *it));
            data.Enable(data.kExt1,             conf.GetDef<bool>("enable-external-1.",        *it));
            data.Enable(data.kExt2,             conf.GetDef<bool>("enable-external-2.",        *it));
            data.Enable(data.kVeto,             conf.GetDef<bool>("enable-veto.",              *it));
            data.Enable(data.kClockConditioner, conf.GetDef<bool>("enable-clock-conditioner.", *it));

            // [ms] Interval between two artificial triggers (no matter which type) minimum 1ms, 10 bit
            data.fTriggerInterval     = conf.GetDef<uint16_t>("trigger-interval.",           *it);
            data.fMultiplicityPhysics = conf.GetDef<uint16_t>("multiplicity-physics.",       *it);
            data.fMultiplicityCalib   = conf.GetDef<uint16_t>("multiplicity-calib.",         *it);
            data.fWindowPhysics       = conf.GetDef<uint16_t>("coincidence-window-physics.", *it); /// (4ns * x + 8ns)
            data.fWindowCalib         = conf.GetDef<uint16_t>("coincidence-window-calib.",   *it); /// (4ns * x + 8ns)
            data.fDelayTrigger        = conf.GetDef<uint16_t>("trigger-delay.",              *it); /// (4ns * x + 8ns)
            data.fDelayTimeMarker     = conf.GetDef<uint16_t>("time-marker-delay.",          *it); /// (4ns * x + 8ns)
            data.fDeadTime            = conf.GetDef<uint16_t>("dead-time.",                  *it); /// (4ns * x + 8ns)

            data.SetPrescaling(conf.GetDef<uint16_t>("ftu-report-interval.", *it));

            const uint16_t seqped = conf.GetDef<uint16_t>("trigger-sequence-ped.",       *it);
            const uint16_t seqint = conf.GetDef<uint16_t>("trigger-sequence-lp-int.",    *it);
            const uint16_t seqext = conf.GetDef<uint16_t>("trigger-sequence-lp-ext.",    *it);

            data.SetSequence(seqped, seqint, seqext);

            data.EnableAllFTU();
            data.EnableAllPixel();

            const vector<uint16_t> pat1 = conf.Vec<uint16_t>("disable-patch.default");
            const vector<uint16_t> pat2 = conf.Vec<uint16_t>("disable-patch."+*it);

            const vector<uint16_t> pix1 = conf.Vec<uint16_t>("disable-pixel.default");
            const vector<uint16_t> pix2 = conf.Vec<uint16_t>("disable-pixel."+*it);

            vector<uint16_t> pat, pix;
            pat.insert(pat.end(), pat1.begin(), pat1.end());
            pat.insert(pat.end(), pat2.begin(), pat2.end());
            pix.insert(pix.end(), pix1.begin(), pix1.end());
            pix.insert(pix.end(), pix2.begin(), pix2.end());

            for (vector<uint16_t>::const_iterator ip=pat.begin(); ip!=pat.end(); ip++)
            {
                if (*ip>FTM::StaticData::kMaxPatchIdx)
                {
                    ostringstream str;
                    str << "disable-patch.*=" << *ip << " exceeds allowed maximum of " << FTM::StaticData::kMaxPatchIdx << "!";
                    T::Error(str);
                    return 2;
                }
                data.DisableFTU(*ip);
            }
            for (vector<uint16_t>::const_iterator ip=pix.begin(); ip!=pix.end(); ip++)
            {
                if (*ip>FTM::StaticData::kMaxPixelIdx)
                {
                    ostringstream str;
                    str << "disable-pixel.*=" << *ip << " exceeds allowed maximum of " << FTM::StaticData::kMaxPixelIdx << "!";
                    T::Error(str);
                    return 2;
                }
                data.EnablePixel(*ip, false);
            }

            fConfigs[*it] = data;

            /*
             threshold-A  data[n].fDAC[0] = val
             threshold-B  data[n].fDAC[1] = val
             threshold-C  data[n].fDAC[2] = val
             threshold-D  data[n].fDAC[3] = val
             threshold-H  data[n].fDAC[4] = val
             */

            // kMaxDAC = 0xfff,
        }

        // FIXME: Add a check about unsused configurations

        // ---------- FOR TESTING PURPOSE ---------

        //        fFTM.SetDefaultSetup(conf.Get<string>("default-setup"));
        fConfigs["test"] = FTM::StaticData();

        // ---------- Setup connection endpoint ---------
        SetEndpoint(conf.Get<string>("addr"));

        return -1;
    }
};

// ------------------------------------------------------------------------

#include "Main.h"

/*
void RunThread(StateMachineImp *io_service)
{
    // This is necessary so that the StateMachien Thread can signal the
    // Readline to exit
    io_service->Run();
    Readline::Stop();
}
*/
/*
template<class S, class T>
int RunDim(Configuration &conf)
{
    WindowLog wout;

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

    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
    StateMachineFTM<S, T> io_service(wout);
    if (!io_service.EvalConfiguration(conf))
        return -1;

    io_service.Run();

    return 0;
}
*/

template<class T, class S, class R>
int RunShell(Configuration &conf)
{
    return Main<T, StateMachineFTM<S, R>>(conf);
/*
    static T shell(conf.GetName().c_str(), conf.Get<int>("console")!=1);

    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;

    StateMachineFTM<S, R> io_service(wout);
    if (!io_service.EvalConfiguration(conf))
        return -1;

    shell.SetReceiver(io_service);

    boost::thread t(boost::bind(RunThread, &io_service));
    // boost::thread t(boost::bind(&StateMachineFTM<S>::Run, &io_service));

    if (conf.Has("cmd"))
    {
        const vector<string> v = conf.Get<vector<string>>("cmd");
        for (vector<string>::const_iterator it=v.begin(); it!=v.end(); it++)
            shell.ProcessLine(*it);
    }

    if (conf.Has("exec"))
    {
        const vector<string> v = conf.Get<vector<string>>("exec");
        for (vector<string>::const_iterator it=v.begin(); it!=v.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

    // 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;
    */
}

void SetupConfiguration(Configuration &conf)
{
    const string n = conf.GetName()+".log";

    po::options_description config("Program options");
    config.add_options()
        ("dns",       var<string>("localhost"), "Dim nameserver host name (Overwites DIM_DNS_NODE environment variable)")
        ("log,l",     var<string>(n), "Write log-file")
        ("no-dim,d",  po_bool(),      "Disable dim services")
        ("console,c", var<int>(),     "Use console (0=shell, 1=simple buffered, X=simple unbuffered)")
        ("cmd",       vars<string>(), "Execute one or more commands at startup")
        ("exec,e",    vars<string>(), "Execute one or more scrips at startup")
        ("quit",      po_switch(),    "Quit after startup");
        ;

    po::options_description control("Control options");
    control.add_options()
        ("addr,a",        var<string>("localhost:5000"),  "Network address of FTM")
        ("quiet,q",       po_bool(),  "Disable printing contents of all received messages (except dynamic data) in clear text.")
        ("hex-out",       po_bool(),  "Enable printing contents of all printed messages also as hex data.")
        ("dynamic-out",   po_bool(),  "Enable printing received dynamic data.")
//        ("default-setup", var<string>(), "Binary file with static data loaded whenever a connection to the FTM was established.")
        ;

    po::options_description runtype("Run type configuration");
    runtype.add_options()
        ("clock-conditioner.frequency",  vars<uint16_t>(),      "Frequencies for which to setup the clock-conditioner")
        ("clock-conditioner.R0.*",       var<Hex<uint32_t>>(),  "Clock-conditioner R0 (replace * by the defined frequency)")
        ("clock-conditioner.R1.*",       var<Hex<uint32_t>>(),  "Clock-conditioner R1 (replace * by the defined frequency)")
        ("clock-conditioner.R8.*",       var<Hex<uint32_t>>(),  "Clock-conditioner R8 (replace * by the defined frequency)")
        ("clock-conditioner.R9.*",       var<Hex<uint32_t>>(),  "Clock-conditioner R9 (replace * by the defined frequency)")
        ("clock-conditioner.R11.*",      var<Hex<uint32_t>>(),  "Clock-conditioner R11 (replace * by the defined frequency)")
        ("clock-conditioner.R13.*",      var<Hex<uint32_t>>(),  "Clock-conditioner R13 (replace * by the defined frequency)")
        ("clock-conditioner.R14.*",      var<Hex<uint32_t>>(),  "Clock-conditioner R14 (replace * by the defined frequency)")
        ("clock-conditioner.R15.*",      var<Hex<uint32_t>>(),  "Clock-conditioner R15 (replace * by the defined frequency)")
        ("run-type",                     vars<string>(),        "")
        ("sampling-frequency.*",         var<uint16_t>(),       "")
        ("enable-trigger.*",             var<bool>(),           "")
        ("enable-external-1.*",          var<bool>(),           "")
        ("enable-external-2.*",          var<bool>(),           "")
        ("enable-veto.*",                var<bool>(),           "")
        ("enable-clock-conditioner.*",   var<bool>(),           "")
        ("trigger-interval.*",           var<uint16_t>(),       "")
        ("trigger-sequence-ped.*",       var<uint16_t>(),       "")
        ("trigger-sequence-lp-int.*",    var<uint16_t>(),       "")
        ("trigger-sequence-lp-ext.*",    var<uint16_t>(),       "")
        ("multiplicity-physics.*",       var<uint16_t>(),       "")
        ("multiplicity-calib.*",         var<uint16_t>(),       "")
        ("coincidence-window-physics.*", var<uint16_t>(),       "")
        ("coincidence-window-calib.*",   var<uint16_t>(),       "")
        ("dead-time.*",                  var<uint16_t>(),       "")
        ("trigger-delay.*",              var<uint16_t>(),       "")
        ("time-marker-delay.*",          var<uint16_t>(),       "")
        ("diable-pixel.*",               vars<uint16_t>(),      "")
        ("diable-patch.*",               vars<uint16_t>(),      "")
        ("ftu-report-interval.*",        var<uint16_t>(),       "")
        ;

    conf.AddEnv("dns", "DIM_DNS_NODE");

    conf.AddOptions(config);
    conf.AddOptions(control);
    conf.AddOptions(runtype);
}

/*
 Extract usage clause(s) [if any] for SYNOPSIS.
 Translators: "Usage" and "or" here are patterns (regular expressions) which
 are used to match the usage synopsis in program output.  An example from cp
 (GNU coreutils) which contains both strings:
  Usage: cp [OPTION]... [-T] SOURCE DEST
    or:  cp [OPTION]... SOURCE... DIRECTORY
    or:  cp [OPTION]... -t DIRECTORY SOURCE...
 */
void PrintUsage()
{
    cout <<
        "The ftmctrl controls the FTM (FACT Trigger Master) board.\n"
        "\n"
        "The default is that the program is started without user intercation. "
        "All actions are supposed to arrive as DimCommands. Using the -c "
        "option, a local shell can be initialized. With h or help a short "
        "help message about the usuage can be brought to the screen.\n"
        "\n"
        "Usage: ftmctrl [-c type] [OPTIONS]\n"
        "  or:  ftmctrl [OPTIONS]\n";
    cout << endl;
}

void PrintHelp()
{
    /* Additional help text which is printed after the configuration
     options goes here */

    /*
     cout << "bla bla bla" << endl << endl;
     cout << endl;
     cout << "Environment:" << endl;
     cout << "environment" << endl;
     cout << endl;
     cout << "Examples:" << endl;
     cout << "test exam" << endl;
     cout << endl;
     cout << "Files:" << endl;
     cout << "files" << endl;
     cout << endl;
     */
}

int main(int argc, const char* argv[])
{
    Configuration conf(argv[0]);
    conf.SetPrintUsage(PrintUsage);
    SetupConfiguration(conf);

    po::variables_map vm;
    try
    {
        vm = conf.Parse(argc, argv);
    }
#if BOOST_VERSION > 104000
    catch (po::multiple_occurrences &e)
    {
        cerr << "Program options invalid due to: " << e.what() << " of '" << e.get_option_name() << "'." << endl;
        return -1;
    }
#endif
    catch (exception& e)
    {
        cerr << "Program options invalid due to: " << e.what() << endl;
        return -1;
    }

    if (conf.HasVersion() || conf.HasPrint())
        return -1;

    if (conf.HasHelp())
    {
        PrintHelp();
        return -1;
    }

    Dim::Setup(conf.Get<string>("dns"));

    //try
    {
        // No console access at all
        if (!conf.Has("console"))
        {
            if (conf.Get<bool>("no-dim"))
                return RunShell<LocalStream, StateMachine, ConnectionFTM>(conf);
            else
                return RunShell<LocalStream, StateMachineDim, ConnectionDimFTM>(conf);
        }
        // Cosole access w/ and w/o Dim
        if (conf.Get<bool>("no-dim"))
        {
            if (conf.Get<int>("console")==0)
                return RunShell<LocalShell, StateMachine, ConnectionFTM>(conf);
            else
                return RunShell<LocalConsole, StateMachine, ConnectionFTM>(conf);
        }
        else
        {
            if (conf.Get<int>("console")==0)
                return RunShell<LocalShell, StateMachineDim, ConnectionDimFTM>(conf);
            else
                return RunShell<LocalConsole, StateMachineDim, ConnectionDimFTM>(conf);
        }
    }
    /*catch (std::exception& e)
    {
        cerr << "Exception: " << e.what() << endl;
        return -1;
    }*/

    return 0;
}
