OsmAnd::BinaryOperator::BinaryOperator(const QString& lvalue, const QString& rvalue, const QString& type)
    : type(type)
{
    if(lvalue.startsWith(":"))
        _lVariableRef = lvalue.mid(1);
    else if (lvalue.startsWith("$"))
        _lTagRef = lvalue.mid(1);
    else
    {
        const auto wasParsed = RoutingConfiguration::parseTypedValue(lvalue.trimmed(), type, _lValue);
        OSMAND_ASSERT(wasParsed, "LValue '" << qPrintable(lvalue) << "' can not be parsed");
    }

    if(rvalue.startsWith(":"))
        _rVariableRef = rvalue.mid(1);
    else if (rvalue.startsWith("$"))
        _rTagRef = rvalue.mid(1);
    else
    {
        const auto wasParsed = RoutingConfiguration::parseTypedValue(rvalue.trimmed(), type, _rValue);
        OSMAND_ASSERT(wasParsed, "RValue '" << qPrintable(rvalue) << "' can not be parsed");
    }
}
OsmAnd::RoutingRuleExpression::RoutingRuleExpression( RoutingRuleset* ruleset, const QString& value, const QString& type )
    : ruleset(ruleset)
    , _type(type)
    , type(_type)
{
    if(value.startsWith(":"))
        _variableRef = value.mid(1);
    else if (value.startsWith("$"))
        _tagRef = value.mid(1);
    else
    {
        const auto wasParsed = RoutingConfiguration::parseTypedValue(value.trimmed(), type, _value);
        OSMAND_ASSERT(wasParsed, "Value '" << qPrintable(value) << "' can not be parsed");
    }
}
void OsmAnd::RoutingConfiguration::parseRoutingParameter( QXmlStreamReader* xmlParser, RoutingProfile* routingProfile )
{
    const auto& attribs = xmlParser->attributes();
    auto descriptionAttrib = attribs.value("description");
    auto nameAttrib = attribs.value("name");
    auto idAttrib = attribs.value("id");
    auto typeAttrib = attribs.value("type");

    if(typeAttrib == "boolean")
    {
        routingProfile->registerBooleanParameter(idAttrib.toString(), nameAttrib.toString(), descriptionAttrib.toString());
    }
    else if(typeAttrib == "numeric")
    {
        auto combinedValues = attribs.value("values");
        auto valueDescriptions = attribs.value("valueDescriptions");
        const auto& stringifiedValues = combinedValues.toString().split(',');
        QList<double> values;
        for(const auto& value_ : constOf(stringifiedValues))
        {
            bool ok;
            auto value = value_.trimmed().toDouble(&ok);
            if(!ok)
                LogPrintf(LogSeverityLevel::Error, "'%s' is not a valid integer", qPrintable(value_));
            values.push_back(value);
        }

        auto valuesDescriptions = valueDescriptions.toString().split(',');
        assert(valuesDescriptions.size() == values.size());
        routingProfile->registerNumericParameter(idAttrib.toString(), nameAttrib.toString(), descriptionAttrib.toString(), values, valuesDescriptions);
    }
    else
    {
        OSMAND_ASSERT(0, QString("Unsupported routing parameter type - ").arg(typeAttrib.toString()));
    }
}
bool OsmAnd::RoutingConfiguration::parseConfiguration( QIODevice* data, RoutingConfiguration& outConfig )
{
    QXmlStreamReader xmlReader(data);

    std::shared_ptr<RoutingProfile> routingProfile;
    auto rulesetType = RoutingRuleset::Type::Invalid;
    QStack< std::shared_ptr<RoutingRule> > ruleset;
    while (!xmlReader.atEnd() && !xmlReader.hasError())
    {
        xmlReader.readNext();
        const auto tagName = xmlReader.name();
        if (xmlReader.isStartElement())
        {
            if (tagName == "osmand_routing_config")
            {
                outConfig._defaultRoutingProfileName = xmlReader.attributes().value("defaultProfile").toString();
            }
            else if (tagName == "routingProfile")
            {
                routingProfile.reset(new RoutingProfile());
                parseRoutingProfile(&xmlReader, routingProfile.get());
                if(routingProfile->_name == outConfig._defaultRoutingProfileName)
                    outConfig._defaultRoutingProfile = routingProfile;
                outConfig._routingProfiles.insert(routingProfile->_name, routingProfile);
            }
            else if (tagName == "attribute")
            {
                auto name = xmlReader.attributes().value("name").toString();
                auto value = xmlReader.attributes().value("value").toString();
                if(routingProfile)
                    routingProfile->addAttribute(name, value);
                else
                    outConfig._attributes.insert(name, value);
            }
            else if (tagName == "parameter")
            {
                if(!routingProfile)
                {
                    LogPrintf(LogSeverityLevel::Warning, "<parameter> is not inside <routingProfile> (%d, %d)", xmlReader.lineNumber(), xmlReader.columnNumber());
                    return false;
                }
                parseRoutingParameter(&xmlReader, routingProfile.get());
            }
            else if (tagName == "point" || tagName == "way")
            {
                assert(rulesetType == RoutingRuleset::Type::Invalid);
                auto attributeName = xmlReader.attributes().value("attribute");
                if (attributeName == "priority")
                    rulesetType = RoutingRuleset::Type::RoadPriorities;
                else if (attributeName == "speed")
                    rulesetType = RoutingRuleset::Type::RoadSpeed;
                else if (attributeName == "access")
                    rulesetType = RoutingRuleset::Type::Access;
                else if (attributeName == "obstacle_time")
                    rulesetType = RoutingRuleset::Type::Obstacles;
                else if (attributeName == "obstacle")
                    rulesetType = RoutingRuleset::Type::RoutingObstacles;
                else if (attributeName == "oneway")
                    rulesetType = RoutingRuleset::Type::OneWay;

                OSMAND_ASSERT(rulesetType != RoutingRuleset::Type::Invalid, QString("Route data object attribute '%1' is unknown").arg(attributeName.toString()));
            }
            else if(rulesetType != RoutingRuleset::Type::Invalid)
            {
                if(isConditionTag(tagName))
                    parseRoutingRuleset(&xmlReader, routingProfile.get(), rulesetType, ruleset);
            }
        }
        else if (xmlReader.isEndElement())
        {
            if(tagName == "routingProfile")
            {
                routingProfile.reset();
            }
            else if (tagName == "point" || tagName == "way")
            {
                rulesetType = RoutingRuleset::Type::Invalid;
            }
            else if(isConditionTag(tagName))
            {
                ruleset.pop();
            }
        }
    }
    if(xmlReader.hasError())
    {
        LogPrintf(LogSeverityLevel::Warning, "XML error: %s (%d, %d)", qPrintable(xmlReader.errorString()), xmlReader.lineNumber(), xmlReader.columnNumber());
        return false;
    }

    return true;
}