#include "MprControllerItemBase.h"
#include "MprProgramItemBase.h"
#include "MprBasicStatements.h"
#include "MprVariableList.h"
#include "MprMultiVariableListItem.h"
#include <cnoid/ItemManager>
#include <cnoid/KinematicBodyItemSet>
#include <cnoid/DigitalIoDevice>
#include <cnoid/ItemList>
#include <cnoid/BodyItem>
#include <cnoid/ControllerIO>
#include <cnoid/MessageOut>
#include <cnoid/CloneMap>
#include <cnoid/LazyCaller>
#include <cnoid/PutPropertyFunction>
#include <cnoid/Archive>
#include <cnoid/Format>
#include <unordered_map>
#include <regex>
#include <cctype>
#include <algorithm>
#include <limits>
#include "gettext.h"

using namespace std;
using namespace cnoid;

namespace {

class Processor : public Referenced
{
public:
    virtual void input() { }
    virtual bool control() = 0;
    virtual void output() { }
};

class ControlFunctionSetProcessor : public Processor
{
public:
    function<bool()> control_;
    function<void()> input_;
    function<void()> output_;

    ControlFunctionSetProcessor(
        function<bool()> control, function<void()> input, function<void()> output)
        : control_(control), input_(input), output_(output)
    { }

    virtual void input() override { if(input_) input_(); }
    virtual bool control() override { return control_(); }
    virtual void output() override { if(output_) output_(); }
};

class OutputOnceFunctionProcessor : public Processor
{
public:
    function<void()> output_;

    OutputOnceFunctionProcessor(function<void()> output): output_(output) { }

    virtual bool control() override {
        return output_ != nullptr;
    }
    virtual void output() override {
        if(output_){
            output_();
            output_ = nullptr;
        }
    }
};

}
    
    
namespace cnoid {

class MprControllerItemBase::Impl
{
public:
    MprControllerItemBase* self;
    ControllerIO* io;
    MprProgramItemBasePtr startupProgramItem;
    MprProgramPtr startupProgram;
    MprProgramPtr currentProgram;
    unordered_map<string, MprProgramPtr> otherProgramMap;
    CloneMap cloneMap;
    KinematicBodySetPtr kinematicBodySetForInternalUse;

    // Used for the default variable mappings
    vector<MprVariableListPtr> variableLists;

    bool isEnabled;
    bool isActiveControlState;
    typedef MprProgram::iterator Iterator;
    Iterator iterator;

    struct ProgramPosition {
        MprProgramPtr program;
        Iterator current;
        Iterator next;
        vector<int> hierachicalPosition;
    };
    vector<ProgramPosition> programStack;
    
    vector<ref_ptr<Processor>> processorStack;
    
    vector<function<void()>> residentInputFunctions;

    typedef function<bool(MprStatement* statement)> InterpretFunction;
    unordered_map<type_index, InterpretFunction> interpreterMap;
    DigitalIoDevicePtr ioDevice;
    double speedRatio;
    double floatingPointValueMin;
    double floatingPointValueMax;

    MprControllerLogPtr currentLog;
    unordered_map<MprProgramPtr, shared_ptr<string>> topLevelProgramToSharedNameMap;
    bool isLogEnabled;

    regex termPattern;
    regex operatorPattern;
    regex cmpOperatorPattern;
    regex intPattern;
    regex floatPattern;
    regex boolPattern;
    regex stringPattern;
    regex variablePattern; // for the default variable expression syntax

    Impl(MprControllerItemBase* self);
    bool initialize(ControllerIO* io);
    bool setupStartStep();
    bool createKinematicBodySetForInternalUse();

    // Default variable mappings
    bool initializeDefaultVariableMappings();
    void extractProgramLocalVariables(Item* item, vector<MprVariableListPtr>& variableLists);
    void extractControllerGlobalVariables(Item* item, vector<MprVariableListPtr>& variableLists);
    void addVariableList(vector<MprVariableListPtr>& variableLists, MprMultiVariableListItem* listItem);
    void notifyGuiOfVariableUpdate(
        MprVariableList* listInController, MprVariableList* listInGui, int variableIndex);
    void updateVariableListInGui(MprVariableList* listInGui, MprVariable* variableForNotification);
    MprVariable* findVariable(const GeneralId& id);

    void setCurrent(MprProgram* program, MprProgram::iterator iter, MprProgram::iterator upperNext);
    bool control();
    void setCurrentProgramPositionToLog(MprControllerLog* log);
    void clear();
    bool interpretCommentStatement(MprCommentStatement* statement);
    bool interpretGroupStatement(MprGroupStatement* statement);
    bool interpretIfStatement(MprIfStatement* statement);
    bool interpretWhileStatement(MprWhileStatement* statement);
    bool interpretCallStatement(MprCallStatement* statement);
    std::optional<bool> evalConditionalExpression(const string& expression);
    std::optional<MprVariable::Value> getTermValue(string::const_iterator& pos, string::const_iterator end);
    std::optional<string> getComparisonOperator(string::const_iterator& pos, string::const_iterator end);
    bool interpretAssignStatement(MprAssignStatement* statement);
    bool applyBinaryOperation(MprVariable::Value& lhsValue, char op, const MprVariable::Value& rhsValue);
    bool interpretSetSignalStatement(MprSignalStatement* statement);
    bool interpretWaitStatement(MprWaitStatement* statement);
    bool interpretDelayStatement(MprDelayStatement* statement);
};

}


void MprControllerItemBase::initializeClass(ExtensionManager* ext)
{
    ext->itemManager().registerAbstractClass<MprControllerItemBase, ControllerItem>();
}



MprControllerItemBase::MprControllerItemBase()
{
    impl = new Impl(this);
}


MprControllerItemBase::MprControllerItemBase(const MprControllerItemBase& org)
    : ControllerItem(org)
{
    impl = new Impl(this);

    impl->speedRatio = org.impl->speedRatio;
    impl->floatingPointValueMin = org.impl->floatingPointValueMin;
    impl->floatingPointValueMax = org.impl->floatingPointValueMax;
}


MprControllerItemBase::Impl::Impl(MprControllerItemBase* self)
    : self(self)
{
    isEnabled = true;
    isActiveControlState = false;
    speedRatio = 1.0;
    floatingPointValueMin = -std::numeric_limits<double>::max();
    floatingPointValueMax = std::numeric_limits<double>::max();
    currentLog = new MprControllerLog;
}


MprControllerItemBase::~MprControllerItemBase()
{
    delete impl;
}


void MprControllerItemBase::registerStatementInterpreter
(std::type_index statementType, const std::function<bool(MprStatement* statement)>& interpret)
{
    impl->interpreterMap[statementType] = interpret;
}


void MprControllerItemBase::registerBaseStatementInterpreters()
{
    MprControllerItemBase::Impl* impl_ = impl;

    registerStatementInterpreter<MprCommentStatement>(
        [impl_](MprCommentStatement* statement){
            return impl_->interpretCommentStatement(statement); });

    registerStatementInterpreter<MprGroupStatement>(
        [impl_](MprGroupStatement* statement){
            return impl_->interpretGroupStatement(statement); });

    registerStatementInterpreter<MprIfStatement>(
        [impl_](MprIfStatement* statement){
            return impl_->interpretIfStatement(statement); });

    registerStatementInterpreter<MprWhileStatement>(
        [impl_](MprWhileStatement* statement){
            return impl_->interpretWhileStatement(statement); });
 
    registerStatementInterpreter<MprCallStatement>(
        [impl_](MprCallStatement* statement){
            return impl_->interpretCallStatement(statement); });

    registerStatementInterpreter<MprAssignStatement>(
        [impl_](MprAssignStatement* statement){
            return impl_->interpretAssignStatement(statement); });

    registerStatementInterpreter<MprSignalStatement>(
        [impl_](MprSignalStatement* statement){
            return impl_->interpretSetSignalStatement(statement); });

    registerStatementInterpreter<MprWaitStatement>(
        [impl_](MprWaitStatement* statement){
            return impl_->interpretWaitStatement(statement); });
    
    registerStatementInterpreter<MprDelayStatement>(
        [impl_](MprDelayStatement* statement){
            return impl_->interpretDelayStatement(statement); });

    impl->termPattern.assign("^\\s*(.+)\\s*");
    impl->operatorPattern.assign("^\\s*([+-])\\s*");
    impl->cmpOperatorPattern.assign("^\\s*(=|!=|<=|>=|<|>)\\s*");
    impl->intPattern.assign("^[+-]?\\d+");
    impl->floatPattern.assign("^[+-]?(\\d+\\.\\d*|\\.\\d+)");
    impl->boolPattern.assign("^([Tt][Rr][Uu][Ee]|[Ff][Aa][Ll][Ss][Ee])");
    impl->stringPattern.assign("^\"(.*)\"");
    impl->variablePattern.assign("^var\\[(\\d+)\\]");
}


void MprControllerItemBase::setResidentInputFunction(std::function<void()> input)
{
    impl->residentInputFunctions.push_back(input);
}


bool MprControllerItemBase::isEnabled() const
{
    return impl->isEnabled;
}


void MprControllerItemBase::setEnabled(bool on)
{
    impl->isEnabled = on;
}


double MprControllerItemBase::timeStep() const
{
    return impl->io ? impl->io->timeStep() : 0.0;
}
        

bool MprControllerItemBase::initialize(ControllerIO* io)
{
    if(!isEnabled()){
        return false;
    }
    
    impl->isActiveControlState = false;
    if(impl->initialize(io)){
        if(onInitialize(io)){
            impl->isActiveControlState = true;
        }
    }
    return impl->isActiveControlState;
}    


bool MprControllerItemBase::Impl::initialize(ControllerIO* io)
{
    this->io = io;
    
    auto mout = MessageOut::master();

    clear();
    
    auto programItems = self->descendantItems<MprProgramItemBase>();
    if(programItems.empty()){
        mout->putErrorln(
            formatR(_("Any program item for {} is not found."), self->displayName()));
        return false;
    }
    
    startupProgramItem.reset();
    for(auto& programItem : programItems){
        if(!programItem->resolveAllReferences()){
            mout->putWarningln(
                formatR(_("Program \"{0}\" is incomplete due to unresolved references."),
                        programItem->displayName()));
        }
        if(programItem->isStartupProgram()){
            startupProgramItem = programItem;
        }
    }
    if(!startupProgramItem){
        mout->putErrorln(
            formatR(_("The startup program for {0} is not specified."), self->displayName()));
        return false;
    }
    startupProgram = cloneMap.getClone(startupProgramItem->program());
    currentProgram = startupProgram;

    if(!createKinematicBodySetForInternalUse()){
        return false;
    }

    for(auto& item : programItems){
        if(item != startupProgramItem){
            auto program = cloneMap.getClone(item->program());
            otherProgramMap.insert(make_pair(item->name(), program));
        }
    }
    
    if(!self->initializeVariables()){
        mout->putErrorln(
            formatR(_("Variables for {} cannot be initialized."), self->displayName()));
        return false;
    }

    iterator = currentProgram->begin();

    if(!setupStartStep()){
        return false;
    }

    auto body = io->body();

    ioDevice = body->findDevice<DigitalIoDevice>();

    isLogEnabled = io->enableLog();

    return true;
}


bool MprControllerItemBase::Impl::setupStartStep()
{
    auto startStep = startupProgramItem->startStep();
    if(!startStep){
        return true;
    }

    int numStatements = currentProgram->numAllStatements();
    if(*startStep >= numStatements){
        int displayStep = *startStep + startupProgramItem->displayStepIndexBase();
        MessageOut::master()->putErrorln(
            formatR(_("Start step {0} of {1} exceeds the program size ({2} steps)."),
                    displayStep, self->displayName(), numStatements));
        return false;
    }

    auto startStatement = currentProgram->statementAtStep(*startStep);
    if(!startStatement){
        return true;
    }

    // Build the program hierarchy from the start statement up to the top level
    MprStatement* stmt = startStatement;
    MprProgram* prog = stmt->holderProgram();

    vector<pair<MprProgram*, MprProgram::iterator>> hierarchy;
    while(prog){
        auto it = prog->find(stmt);
        if(it != prog->end()){
            hierarchy.push_back({prog, it});
        }
        if(auto holder = prog->holderStatement()){
            stmt = holder;
            prog = holder->holderProgram();
        } else {
            break;
        }
    }

    if(hierarchy.empty()){
        return true;
    }

    // Set up from top level down to the start statement
    // The last element in the hierarchy is the top level program
    currentProgram = hierarchy.back().first;
    iterator = hierarchy.back().second;

    // Push intermediate levels onto the program stack (from top to bottom, excluding the deepest)
    for(int i = static_cast<int>(hierarchy.size()) - 1; i > 0; --i){
        auto& [upperProg, upperIter] = hierarchy[i];
        auto& [lowerProg, lowerIter] = hierarchy[i - 1];

        ProgramPosition pos;
        pos.program = upperProg;
        pos.current = upperIter;
        pos.next = upperIter + 1;

        if(!programStack.empty()){
            pos.hierachicalPosition = programStack.back().hierachicalPosition;
        }
        pos.hierachicalPosition.push_back(static_cast<int>(upperIter - upperProg->begin()));

        programStack.push_back(pos);

        currentProgram = lowerProg;
        iterator = lowerIter;
    }

    return true;
}


bool MprControllerItemBase::Impl::createKinematicBodySetForInternalUse()
{
    kinematicBodySetForInternalUse = cloneMap.getClone(self->kinematicBodyItemSet());
    if(!kinematicBodySetForInternalUse || !kinematicBodySetForInternalUse->mainBodyPart()){
        return false;
    }
    return true;
}


bool MprControllerItemBase::initializeVariables()
{
    return impl->initializeDefaultVariableMappings();
}


bool MprControllerItemBase::Impl::initializeDefaultVariableMappings()
{
    variableLists.clear();
    
    extractProgramLocalVariables(startupProgramItem, variableLists);
    extractControllerGlobalVariables(self, variableLists);
    
    if(variableLists.empty()){
        variableLists.push_back(new MprVariableList);
    }

    return true;
}


void MprControllerItemBase::Impl::extractProgramLocalVariables
(Item* item, vector<MprVariableListPtr>& variableLists)
{
    for(Item* child = item->childItem(); child; child = child->nextItem()){
        if(auto listItem = dynamic_cast<MprMultiVariableListItem*>(child)){
            addVariableList(variableLists, listItem);
        }
        extractProgramLocalVariables(child, variableLists);
    }
}


void MprControllerItemBase::Impl::extractControllerGlobalVariables
(Item* item, vector<MprVariableListPtr>& variableLists)
{
    for(Item* child = item->childItem(); child; child = child->nextItem()){
        if(auto programItem = dynamic_cast<MprProgramItemBase*>(child)){
            continue;
        }
        if(auto listItem = dynamic_cast<MprMultiVariableListItem*>(child)){
            addVariableList(variableLists, listItem);
        }
        extractControllerGlobalVariables(child, variableLists);
    }
}


void MprControllerItemBase::Impl::addVariableList
(vector<MprVariableListPtr>& variableLists, MprMultiVariableListItem* listItem)
{
    MprVariableListPtr listInGui = listItem->findVariableList(MprVariableList::GeneralVariable);
    if(listInGui){
        auto listInController = listInGui->clone();
        self->setVariableListSync(listInGui, listInController);
        variableLists.push_back(listInController);
    }
}


void MprControllerItemBase::setVariableListSync
(MprVariableList* listInGui, MprVariableList* listInController)
{
    listInController->sigVariableAdded().connect(
        [this, listInController, listInGui](int index){
            impl->notifyGuiOfVariableUpdate(listInController, listInGui, index); });

    listInController->sigVariableUpdated().connect(
        [this, listInController, listInGui](int index, int flags){
            if(flags & MprVariable::ValueUpdate){
                impl->notifyGuiOfVariableUpdate(listInController, listInGui, index);
            }
        });
}


void MprControllerItemBase::Impl::notifyGuiOfVariableUpdate
(MprVariableList* listInController, MprVariableList* listInGui, int variableIndex)
{
    auto variableInController = listInController->variableAt(variableIndex);
    MprVariablePtr variableForNotification = new MprVariable(*variableInController);
    callLater(
        [this, listInGui, variableForNotification](){
            updateVariableListInGui(listInGui, variableForNotification); });
}
    

/**
   This is a temporary implementation to update the original variables managed in the main GUI thread.
   The variables should be updated with the simulation log data which records the variable states for
   each time frame.
*/
void MprControllerItemBase::Impl::updateVariableListInGui
(MprVariableList* listInGui, MprVariable* variableForNotification)
{
    auto variable = listInGui->findVariable(variableForNotification->id());
    if(!variable){
        listInGui->append(variableForNotification);
    } else {
        variable->setValue(variableForNotification->value());
        variable->notifyUpdate(MprVariable::ValueUpdate);
    }
}


MprVariable* MprControllerItemBase::Impl::findVariable(const GeneralId& id)
{
    for(auto& list : variableLists){
        if(auto variable = list->findVariable(id)){
            return variable;
        }
    }
    return nullptr;
}


std::optional<MprVariable::Value> MprControllerItemBase::evalExpressionAsVariableValue
(std::string::const_iterator& io_expressionBegin, std::string::const_iterator expressionEnd)
{
    std::smatch match;
    if(regex_search(io_expressionBegin, expressionEnd, match, impl->variablePattern)){
        io_expressionBegin = match[0].second;
        GeneralId id(std::stoi(match.str(1)));
        if(auto variable = impl->findVariable(id)){
            return variable->value();
        }
        impl->io->os() << formatR(_("Variable {0} is not defined."), id.label()) << endl;
    }
    return std::nullopt;
}


std::function<bool(MprVariable::Value value)> MprControllerItemBase::evalExpressionAsVariableToAssginValue
(const std::string& expression)
{
    std::smatch match;
    if(regex_search(expression, match, impl->variablePattern)){
        GeneralId id(std::stoi(match.str(1)));
        auto variable = impl->findVariable(id);
        if(!variable){
            auto list = impl->variableLists.front();
            MprVariablePtr newVariable = new MprVariable(id, list->defaultValue());
            if(list->append(newVariable)){
                variable = newVariable;
            }
        }
        if(variable){
            return
                [this, variable](MprVariable::Value value){
                    if(variable->setValue(value)){
                        pushOutputOnceFunction([variable](){ variable->notifyUpdate(); });
                        return true;
                    }
                    return false;
                };
        }
    }
    return nullptr;
}
    
    
bool MprControllerItemBase::onInitialize(ControllerIO* /* io */)
{
    return true;
}


MprProgramItemBase* MprControllerItemBase::getStartupProgramItem()
{
    return impl->startupProgramItem;
}


MprProgram* MprControllerItemBase::getStartupProgram()
{
    return impl->startupProgram;
}


MprProgram* MprControllerItemBase::getCurrentProgram()
{
    return impl->currentProgram;
}


MprProgram::iterator MprControllerItemBase::getCurrentIterator()
{
    return impl->iterator;
}


void MprControllerItemBase::setCurrent(MprProgram::iterator iter)
{
    impl->iterator = iter;
}


void MprControllerItemBase::setCurrent
(MprProgram* program, MprProgram::iterator iter, MprProgram::iterator upperNext)
{
    impl->setCurrent(program, iter, upperNext);
}


void MprControllerItemBase::Impl::setCurrent
(MprProgram* program, MprProgram::iterator iter, MprProgram::iterator upperNext)
{
    ProgramPosition upper;
    upper.program = currentProgram;
    upper.current = iterator;
    upper.next = upperNext;

    if(!programStack.empty()){
        upper.hierachicalPosition = programStack.back().hierachicalPosition;
    }
    int statementIndex = upper.current - currentProgram->begin();
    upper.hierachicalPosition.push_back(statementIndex);

    programStack.push_back(upper);
    
    currentProgram = program;
    iterator = iter;
}


MprProgram* MprControllerItemBase::findProgram(const std::string& name)
{
    auto iter = impl->otherProgramMap.find(name);
    if(iter != impl->otherProgramMap.end()){
        return iter->second;
    }
    return nullptr;
}


KinematicBodySet* MprControllerItemBase::kinematicBodySetForInternalUse()
{
    return impl->kinematicBodySetForInternalUse;
}


bool MprControllerItemBase::start()
{
    impl->currentLog->isErrorState_ = false;
    if(impl->ioDevice){
        impl->ioDevice->resetAllSignals(true);
    }
    return onStart();
}


bool MprControllerItemBase::onStart()
{
    return true;
}


void MprControllerItemBase::input()
{
    for(auto& input : impl->residentInputFunctions){
        input();
    }
    if(!impl->processorStack.empty()){
        impl->processorStack.back()->input();
    }
}


bool MprControllerItemBase::control()
{
    return impl->control();
}


bool MprControllerItemBase::Impl::control()
{
    if(!isActiveControlState){
        return false;
    }

    int loopCounter = 0;
    bool stateChanged = false;

    while(true){
        bool isProcessorActive = false;
        while(!processorStack.empty()){
            isProcessorActive = processorStack.back()->control();
            if(isProcessorActive){
                break;
            }
            processorStack.pop_back();
        }
        if(isProcessorActive){
            break;
        }

        if(loopCounter > 10){
            break;
        }

        bool hasNextStatement = (iterator != currentProgram->end());
        
        while(!hasNextStatement){
            if(programStack.empty()){
                break;
            }
            auto& upper = programStack.back();
            iterator = upper.next;
            currentProgram = upper.program;
            programStack.pop_back();
            hasNextStatement = (iterator != currentProgram->end());
        }
        if(!hasNextStatement){
            isActiveControlState = false;
            break;
        }

        if(isLogEnabled){
            setCurrentProgramPositionToLog(currentLog);
            stateChanged = true;
        }
        
        auto statement = iterator->get();

        if(!statement->isEnabled()){
            ++iterator;

        } else {
            auto p = interpreterMap.find(typeid(*statement));
            if(p == interpreterMap.end()){
                io->os() << formatR(_("{0} cannot be executed because the interpreter for it is not found."),
                                   statement->label(0)) << endl;
                ++iterator;
            } else {
                auto& interpret = p->second;
                if(!interpret(statement)){
                    isActiveControlState = false;
                    if(isLogEnabled){
                        currentLog->isErrorState_ = true;
                    }
                    io->os() << formatR(_("Failed to execute {0} statement. The control was terminated."),
                                        statement->label(0)) << endl;
                    break;
                }
                isActiveControlState = true;
            }
        }
        
        ++loopCounter;
    }

    if(isLogEnabled && stateChanged){
        io->outputLogFrame(new MprControllerLog(*currentLog));
    }
        
    return isActiveControlState;
}


void MprControllerItemBase::Impl::setCurrentProgramPositionToLog(MprControllerLog* log)
{
    auto topLevelProgram = currentProgram->topLevelProgram();
    auto it = topLevelProgramToSharedNameMap.find(topLevelProgram);
    if(it != topLevelProgramToSharedNameMap.end()){
        log->topLevelProgramName_ = it->second;
    } else {
        log->topLevelProgramName_ = make_shared<string>(topLevelProgram->name());
        topLevelProgramToSharedNameMap[topLevelProgram] = log->topLevelProgramName_;
    }
        
    if(currentProgram->isTopLevelProgram()){
        log->hierachicalPosition_.clear();
    } else if(!programStack.empty()){
        log->hierachicalPosition_ = programStack.back().hierachicalPosition;
    }
    int statementIndex = iterator - currentProgram->begin();
    log->hierachicalPosition_.push_back(statementIndex);
}


void MprControllerItemBase::pushControlFunctions
(std::function<bool()> control, std::function<void()> input, std::function<void()> output)
{
    impl->processorStack.push_back(new ControlFunctionSetProcessor(control, input, output));
}
        

void MprControllerItemBase::pushOutputOnceFunction(std::function<void()> outputOnce)
{
    impl->processorStack.push_back(new OutputOnceFunctionProcessor(outputOnce));
}


void MprControllerItemBase::output()
{
    if(!impl->processorStack.empty()){
        impl->processorStack.back()->output();
    }
}


void MprControllerItemBase::stop()
{
    impl->clear();
    onStop();
}


bool MprControllerItemBase::onStop()
{
    return true;
}


void MprControllerItemBase::setActiveControlState(bool on)
{
    impl->isActiveControlState = on;
}


bool MprControllerItemBase::isActiveControlState() const
{
    return impl->isActiveControlState;
}


void MprControllerItemBase::onDisconnectedFromRoot()
{
    impl->clear();
}


void MprControllerItemBase::Impl::clear()
{
    startupProgramItem.reset();
    startupProgram.reset();
    currentProgram.reset();
    otherProgramMap.clear();
    cloneMap.clear();
    kinematicBodySetForInternalUse.reset();
    variableLists.clear();
    programStack.clear();
    processorStack.clear();
    topLevelProgramToSharedNameMap.clear();
}


double MprControllerItemBase::speedRatio() const
{
    return impl->speedRatio;
}


void MprControllerItemBase::setSpeedRatio(double r)
{
    if(r > 0.0 && r <= 1.0){
        impl->speedRatio = r;
    }
}


void MprControllerItemBase::setFloatingPointValueRange(double min, double max)
{
    impl->floatingPointValueMin = min;
    impl->floatingPointValueMax = max;
}


bool MprControllerItemBase::Impl::interpretCommentStatement(MprCommentStatement*)
{
    ++iterator;
    return true;
}


bool MprControllerItemBase::Impl::interpretGroupStatement(MprGroupStatement* statement)
{
    auto program = statement->lowerLevelProgram();
    self->setCurrent(program, program->begin(), ++self->getCurrentIterator());
    return true;
}


bool MprControllerItemBase::Impl::interpretIfStatement(MprIfStatement* statement)
{
    auto condition = evalConditionalExpression(statement->condition());

    if(!condition){
        return false;
    }

    auto next = iterator;
    ++next;
                           
    MprElseStatement* nextElseStatement = nullptr;
    while(next != currentProgram->end()){
        if(dynamic_cast<MprEmptyStatement*>(next->get()) ||
           dynamic_cast<MprCommentStatement*>(next->get())){
            ++next;
        } else {
            nextElseStatement = dynamic_cast<MprElseStatement*>(next->get());
            if(nextElseStatement){
                ++next;
            }
            break;
        }
    }

    if(*condition){
        auto program = statement->lowerLevelProgram();
        setCurrent(program, program->begin(), next);

    } else {
        ++iterator;
        if(nextElseStatement){
            auto program = nextElseStatement->lowerLevelProgram();
            setCurrent(program, program->begin(), next);
        }
    }

    return true;
}


bool MprControllerItemBase::Impl::interpretWhileStatement(MprWhileStatement* statement)
{
    auto condition = evalConditionalExpression(statement->condition());

    if(!condition){
        return false;
    }

    if(*condition){
        auto program = statement->lowerLevelProgram();
        setCurrent(program, program->begin(), iterator);
    } else {
        ++iterator;
    }
    return true;
}


bool MprControllerItemBase::Impl::interpretCallStatement(MprCallStatement* statement)
{
    auto& programName = statement->programName();

    if(programName.empty()){
        io->os() << _("A program specified in a call statement is empty.") << endl;
        return false;
    }
        
    auto program = self->findProgram(programName);

    if(!program){
        io->os() << formatR(_("Program \"{0}\" specified in a call statement does not exist."),
                            programName) << endl;
        return false;
    }

    setCurrent(program, program->begin(), iterator + 1);

    return true;
}


template<class LhsType, class RhsType>
static std::optional<bool> checkNumericalComparison(const string& op, LhsType lhs, RhsType rhs)
{
    if(op == "="){
        return lhs == rhs;
    } else if(op == "!="){
        return lhs != rhs;
    } else if(op == "<"){
        return lhs < rhs;
    } else if(op == ">"){
        return lhs > rhs;
    } else if(op == "<="){
        return lhs <= rhs;
    } else if(op == ">="){
        return lhs >= rhs;
    }
    return std::nullopt;
}


std::optional<bool> MprControllerItemBase::Impl::evalConditionalExpression(const string& expression)
{
    if(expression.empty()){
        io->os() << _("Empty conditional expression.") << endl;
        return std::nullopt;
    }
    auto pos = expression.cbegin();
    auto end = expression.cend();

    bool isExpressionValid = false;
    std::optional<string> pCmpOp;
    std::optional<MprVariable::Value> pRhs;
    std::optional<MprVariable::Value> pLhs = getTermValue(pos, end);
    if(pLhs){
        if(pos == end){
            isExpressionValid = true;
        } else if((pCmpOp = getComparisonOperator(pos, end))){
            if(pos != end){
                if((pRhs = getTermValue(pos, end))){
                    if(pos == end){
                        isExpressionValid = true;
                    }
                }
            }
        }
    }
    if(!isExpressionValid){
        io->os() << formatR(_("Conditional expression \"{0}\" is invalid."), expression) << endl;
        return std::nullopt;
    }

    auto& lhs = *pLhs;

    if(!pRhs){
        return MprVariable::toBool(lhs);
    }
            
    std::optional<bool> pResult;
    auto& rhs = *pRhs;
    string& cmpOp = *pCmpOp;

    int rhsValueType = MprVariable::valueType(rhs);
    switch(MprVariable::valueType(lhs)){
    case MprVariable::Int:
    {
        auto lhsValue = MprVariable::intValue(lhs);
        if(rhsValueType == MprVariable::Int){
            pResult = checkNumericalComparison(cmpOp, lhsValue, MprVariable::intValue(rhs));
        } else if(rhsValueType == MprVariable::Double){
            pResult = checkNumericalComparison(cmpOp, lhsValue, MprVariable::doubleValue(rhs));
        }
        break;
    }
    case MprVariable::Double:
    {
        auto lhsValue = MprVariable::doubleValue(lhs);
        if(rhsValueType == MprVariable::Int){
            pResult = checkNumericalComparison(cmpOp, lhsValue, MprVariable::intValue(rhs));
        } else if(rhsValueType == MprVariable::Double){
            pResult = checkNumericalComparison(cmpOp, lhsValue, MprVariable::doubleValue(rhs));
        }
        break;
    }
    case MprVariable::Bool:
        if(rhsValueType == MprVariable::Bool && cmpOp == "="){
            pResult = checkNumericalComparison(
                cmpOp, MprVariable::boolValue(lhs), MprVariable::boolValue(rhs));
        }
        break;

    case MprVariable::String:
        if(rhsValueType == MprVariable::String && cmpOp == "="){
            pResult = checkNumericalComparison(
                cmpOp, MprVariable::stringValue(lhs), MprVariable::stringValue(rhs));
        }
        break;
    }

    if(!pResult){
        io->os() << _("Type / operator mismatch in the conditional expression of a While statement.") << endl;
    }

    return pResult;
}
    
    
std::optional<MprVariable::Value> MprControllerItemBase::Impl::getTermValue
(string::const_iterator& pos, string::const_iterator end)
{
    std::optional<MprVariable::Value> value;
    std::smatch match;

    if(regex_search(pos, end,  match, stringPattern)){
        value = match.str(1);
        pos = match[0].second;
                
    } else if(regex_search(pos, end, match, floatPattern)){
        double number = std::stod(match.str(0));
        if(number < floatingPointValueMin || number > floatingPointValueMax){
            io->os() << formatR(_("Real value {0} is out of range."), match.str(0)) << endl;
        } else {
            value = number;
            pos = match[0].second;
        }
            
    } else if(regex_search(pos, end, match, intPattern)){
        errno = 0;
        long number = strtol(match.str(0).c_str(), nullptr, 10);
        if(errno == ERANGE || number < INT_MIN || number > INT_MAX){
            io->os() << formatR(_("Integer value {0} is out of range."), match.str(0)) << endl;
        } else {
            value = std::stoi(match.str(0));
            pos = match[0].second;
        }
    } else if(regex_search(pos, end, match, boolPattern)){
        auto label = match.str(1);
        std::transform(label.begin(), label.end(), label.begin(), ::tolower);
        value = (label == "true") ? true : false;
        pos = match[0].second;
                
    } else {
        value = self->evalExpressionAsVariableValue(pos, end);
    }
    
    return value;
}


std::optional<string> MprControllerItemBase::Impl::getComparisonOperator
(string::const_iterator& pos, string::const_iterator end)
{
    std::smatch match;
    if(regex_search(pos, end,  match, cmpOperatorPattern)){
        pos = match[0].second;
        return match.str(1);
    }
    return std::nullopt;
}


bool MprControllerItemBase::Impl::interpretAssignStatement(MprAssignStatement* statement)
{
    auto& expression = statement->valueExpression();
    if(expression.empty()){
        io->os() << formatR(_("Expression assigned to variable {0} is empty."),
                            statement->variableExpression()) << endl;
        return false;
    }

    vector<MprVariable::Value> termValues;
    vector<char> operators;
    vector<string> termStrings;
    string invalidTerm;
    auto pos = expression.cbegin();
    auto end = expression.cend();
    std::smatch match;
    bool isValidExpression = true;
    bool isNextTermOperator = false;
        
    while(pos != end){
        if(!isNextTermOperator){
            auto pos0 = pos;
            auto termValue = getTermValue(pos, end);
            string termString(pos0, pos);
            if(termValue){
                termValues.push_back(*termValue);
                termStrings.push_back(termString);
                isNextTermOperator = true;
            } else {
                if(termString.empty()){
                    termString = string(pos0, end);
                }
                if(regex_search(termString, match, termPattern)){
                    invalidTerm = match.str(1);
                } else {
                    invalidTerm = termString;
                }
                isValidExpression = false;
            }
        } else {
            if(regex_search(pos, end, match, operatorPattern)){
                char op = match.str(1)[0];
                operators.push_back(op);
                pos = match[0].second;
                isNextTermOperator = false;
            } else {
                invalidTerm = string(match[0].first, match[0].second);
                isValidExpression = false;
            }
        }
        if(!isValidExpression){
            io->os() << formatR(_("Term \"{0}\" is invalid."), invalidTerm) << endl;
            break;
        }
    }

    if(isValidExpression){
        if(termValues.empty()){
            isValidExpression = false;
        } else if(!isNextTermOperator){
            io->os() << formatR(_("Expression ends with operator {0}."), operators.back()) << endl;
            isValidExpression = false;
        }
    }
    if(!isValidExpression){
        return false;
    }

    size_t termIndex = 0;
    size_t operatorIndex = 0;
    MprVariable::Value value = termValues[termIndex++];
    while(operatorIndex < operators.size()){
        char op = operators[operatorIndex++];
        if(!applyBinaryOperation(value, op, termValues[termIndex++])){
            isValidExpression = false;
            io->os() << formatR(_("Type mismatch in expresion \"{0} {1} {2}\""),
                                termStrings[termIndex-2],
                                op,
                                termStrings[termIndex-1]) << endl;
            break;
        }
    }
    
    bool assigned = false;
    if(isValidExpression){
        auto assignValue =
            self->evalExpressionAsVariableToAssginValue(statement->variableExpression());
        if(assignValue && assignValue(value)){
            assigned = true;
            ++iterator;
        }
    }

    return assigned;
}


template<class ResultType, class LhsType, class RhsType>
static ResultType applyNumericalOperation(char op, LhsType lhs, RhsType rhs)
{
    if(op == '+'){
        return lhs + rhs;
    } else if(op == '-'){
        return lhs - rhs;
    }
    return 0;
}


static string applyStringOperation(char op, const string& lhs, const string& rhs)
{
    if(op == '='){
        return rhs;
    } else if(op == '+'){
        return lhs + rhs;
    }
    return string();
}


bool MprControllerItemBase::Impl::applyBinaryOperation
(MprVariable::Value& lhsValue, char op, const MprVariable::Value& rhsValue)
{
    bool result = false;
    int lhsType = MprVariable::valueType(lhsValue);
    int rhsType = MprVariable::valueType(rhsValue);
    
    switch(rhsType){
        
    case MprVariable::Int:
    {
        int rhs = MprVariable::intValue(rhsValue);
        if(lhsType == MprVariable::Int){
            lhsValue = applyNumericalOperation<int>(op, MprVariable::intValue(lhsValue), rhs);
            result = true;
        } else if(lhsType == MprVariable::Double){
            lhsValue = applyNumericalOperation<double>(op, MprVariable::doubleValue(lhsValue), rhs);
            result = true;
        }
        break;
    }
    case MprVariable::Double:
    {
        double rhs = MprVariable::doubleValue(rhsValue);
        if(lhsType == MprVariable::Int){
            lhsValue = applyNumericalOperation<double>(op, MprVariable::intValue(lhsValue), rhs);
            result = true;
        } else if(lhsType == MprVariable::Double){
            lhsValue = applyNumericalOperation<double>(op, MprVariable::doubleValue(lhsValue), rhs);
            result = true;
        }
        break;
    }
    case MprVariable::Bool:
    {
        bool rhs = MprVariable::boolValue(rhsValue);
        if(lhsType == MprVariable::Bool && op == '+'){
            lhsValue = MprVariable::boolValue(lhsValue) || rhs;
            result = true;
        }
        break;
    }
    case MprVariable::String:
        if(lhsType == MprVariable::String && op == '+'){
            lhsValue = MprVariable::stringValue(lhsValue) + MprVariable::stringValue(rhsValue);
            result = true;
        }
        break;
    }

    return result;
}


bool MprControllerItemBase::Impl::interpretSetSignalStatement(MprSignalStatement* statement)
{
    if(!ioDevice){
        io->os() << formatR(_("{0} cannot be executed because {1} does not have a signal I/O device"),
                            statement->label(0), io->body()->name()) << endl;
    } else {
        self->pushOutputOnceFunction(
            [this, statement](){
                ioDevice->setOut(statement->signalIndex(), statement->on(), true); });
    }

    ++iterator;
    
    return true;
}


bool MprControllerItemBase::Impl::interpretWaitStatement(MprWaitStatement* statement)
{
    if(statement->conditionType() == MprWaitStatement::SignalInput){
        if(!ioDevice){
            self->pushControlFunctions([]() { return true; });
        } else {
            self->pushControlFunctions(
                [this, statement](){
                    return ioDevice->in(statement->signalIndex()) != statement->signalStateCondition();
                });
        }
    }

    ++iterator;
    
    return true;
}

    
bool MprControllerItemBase::Impl::interpretDelayStatement(MprDelayStatement* statement)
{
    int remainingFrames = statement->time() / io->timeStep();
    self->pushControlFunctions([remainingFrames]() mutable { return (remainingFrames-- > 0); });
    ++iterator;
    return true;
}


void MprControllerItemBase::doPutProperties(PutPropertyFunction& putProperty)
{
    ControllerItem::doPutProperties(putProperty);
    putProperty(_("Speed ratio"), impl->speedRatio, changeProperty(impl->speedRatio));
}


bool MprControllerItemBase::store(Archive& archive)
{
    if(!ControllerItem::store(archive)){
        return false;
    }
    archive.write("enabled", impl->isEnabled);
    archive.write("speed_ratio", impl->speedRatio);
    return true;
}
    

bool MprControllerItemBase::restore(const Archive& archive)
{
    if(!ControllerItem::restore(archive)){
        return false;
    }
    archive.read("enabled", impl->isEnabled);
    archive.read({ "speed_ratio", "speedRatio"}, impl->speedRatio);
    return true;
}


MprControllerLog::MprControllerLog()
{
    isErrorState_ = false;
}
