/*
 * Copyright 2019 ScyllaDB
 */

/*
 * This file is part of Scylla.
 *
 * Scylla is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Scylla is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with Scylla.  If not, see <http://www.gnu.org/licenses/>.
 */

#pragma once

#include <vector>
#include <string>
#include <variant>

/*
 * Parsed representation of expressions and their components.
 *
 * Types in alternator::parse namespace are used for holding the parse
 * tree - objects generated by the Antlr rules after parsing an expression.
 * Because of the way Antlr works, all these objects are default-constructed
 * first, and then assigned when the rule is completed, so all these types
 * have only default constructors - but setter functions to set them later.
 */

namespace alternator {
namespace parsed {

// "path" is an attribute's path in a document, e.g., a.b[3].c.
class path {
    // All paths have a "root", a top-level attribute, and any number of
    // "dereference operators" - each either an index (e.g., "[2]") or a
    // dot (e.g., ".xyz").
    std::string _root;
    std::vector<std::variant<std::string, unsigned>> _operators;
public:
    void set_root(std::string root) {
        _root = std::move(root);
    }
    void add_index(unsigned i) {
        _operators.emplace_back(i);
    }
    void add_dot(std::string(name)) {
        _operators.emplace_back(std::move(name));
    }
    const std::string& root() const {
        return _root;
    }
    bool has_operators() const {
        return !_operators.empty();
    }
};

// "value" is is a value used in the right hand side of an assignment
// expression, "SET a = ...". It can be a reference to a value included in
// the request (":val"), a path to an attribute from the existing item
// (e.g., "a.b[3].c"), or a function of other such values.
// Note that the real right-hand-side of an assignment is actually a bit
// more general - it allows either a value, or a value+value or value-value -
// see class set_rhs below.
struct value {
    struct function_call {
        std::string _function_name;
        std::vector<value> _parameters;
    };
    std::variant<std::string, path, function_call> _value;
    void set_valref(std::string s) {
        _value = std::move(s);
    }
    void set_path(path p) {
        _value = std::move(p);
    }
    void set_func_name(std::string s) {
        _value = function_call {std::move(s), {}};
    }
    void add_func_parameter(value v) {
        std::get<function_call>(_value)._parameters.emplace_back(std::move(v));
    }
};

// The right-hand-side of a SET in an update expression can be either a
// single value (see above), or value+value, or value-value.
class set_rhs {
public:
    char _op;  // '+', '-', or 'v''
    value _v1;
    value _v2;
    void set_value(value&& v1) {
        _op = 'v';
        _v1 = std::move(v1);
    }
    void set_plus(value&& v2) {
        _op = '+';
        _v2 = std::move(v2);
    }
    void set_minus(value&& v2) {
        _op = '-';
        _v2 = std::move(v2);
    }
};

class update_expression {
public:
    struct action {
        path _path;
        struct set {
            set_rhs _rhs;
        };
        struct remove {
        };
        struct add {
            std::string _valref;
        };
        struct del {
            std::string _valref;
        };
        std::variant<set, remove, add, del> _action;

        void assign_set(path p, set_rhs rhs) {
            _path = std::move(p);
            _action = set { std::move(rhs) };
        }
        void assign_remove(path p) {
            _path = std::move(p);
            _action = remove { };
        }
        void assign_add(path p, std::string v) {
            _path = std::move(p);
            _action = add { std::move(v) };
        }
        void assign_del(path p, std::string v) {
            _path = std::move(p);
            _action = del { std::move(v) };
        }
    };
private:
    std::vector<action> _actions;
    bool seen_set = false;
    bool seen_remove = false;
    bool seen_add = false;
    bool seen_del = false;
public:
    void add(action a);
    void append(update_expression other);
    bool empty() const {
        return _actions.empty();
    }
    const std::vector<action>& actions() const {
        return _actions;
    }
};

} // namespace parsed
} // namespace alternator
