#pragma once

#include <balancer/serval/core/function.h>

#include <util/generic/strbuf.h>
#include <util/generic/vector.h>

namespace YAML {
    class Node;
}

namespace NSv::NAppHost {
    // A boolean with an "undetermined" state. Used for edge expression evaluation
    // because it may occur before the values of some flags are known; but if the
    // known flags force a particular value for that expression, some optimizations
    // are possible.
    struct TTernary {
    public:
        TTernary(int c = 0)
            : V_(c)
        {}
        TTernary(bool b)
            : V_(b ? 1 : -1)
        {}
        TTernary operator&(TTernary other) const noexcept {
            return V_ > other.V_ ? other.V_ : V_;
        }
        TTernary operator|(TTernary other) const noexcept {
            return V_ > other.V_ ? V_ : other.V_;
        }
        TTernary operator!() const noexcept {
            return -V_;
        }
        bool IsNil() const noexcept {
            return !V_;
        }
        bool IsTrue() const noexcept {
            return V_ > 0;
        }
        bool IsFalse() const noexcept {
            return V_ < 0;
        }

    private:
        signed char V_;
    };

    using TItemResolver = NSv::TFunction<size_t(TStringBuf node, TStringBuf item)>;

    // expr      ::= or-expr ('?' expr ':' expr)?
    // or-expr   ::= and-expr ('||' and-expr)*
    // and-expr  ::= not-expr ('&&' not-expr)*
    // not-expr  ::= '!' not-expr | '(' expr ')' | 'true' | 'false' | node-name '[' item-name ']'
    // node-name ::= <any character except &, !, |, (, ), [, ], ?, ^, $, @, :>+
    // item-name ::= <any character except [, ]>+
    struct TExpression {
    public:
        TExpression() = default;
        TExpression(const YAML::Node& arg, TItemResolver);

        // Compute the value of the expression. If `coalesce` is true and a flag's value
        // is unknown, use the default. An empty expression always evals to "unknown".
        TTernary operator()(const TVector<TTernary>& flags, bool coalesce = false) const;

    private:
        TVector<size_t> E_;
    };
}
