#ifndef ARRAY_HPP
#define ARRAY_HPP

#include "symbol.hpp"
#include <vector>

class array : public symbol_impl {
    std::vector<symbol> vals;

public :
    array() {}

    void push(symbol&& s) {
        vals.push_back(std::move(s));
    }

    size_t size() const {
        return vals.size();
    }

    symbol& operator [] (size_t i) {
        return vals[i];
    }

    const symbol& operator [] (size_t i) const {
        return vals[i];
    }

    template<typename T>
    T& as(size_t i) {
        return vals[i].as<T>();
    }

    template<typename T>
    const T& as(size_t i) const {
        return vals[i].as<T>();
    }

    array* clone() const override {
        array* a = new array();
        for (const symbol& s : vals)
            a->push(s.clone());

        return a;
    }

    array* evaluate(const interpreter_context& context) const override {
        array* a = new array();
        try {
            for (const symbol& s : vals)
                a->push(s.evaluate(context));
        } catch (...) {
            delete a;
            throw;
        }
        return a;
    }

    std::string serialize(const interpreter_context& context) const override {
        if (vals.empty())
            return "()";

        std::string s = "(" + vals[0].serialize(context);

        for (size_t i = 1; i < vals.size(); ++i)
            s += ", " + vals[i].serialize(context);

        s += ")";

        return s;
    }

    const std::string& type() const override {
        return type_name;
    }

    bool is_litteral() const override {
        for (const symbol& s : vals) {
            if (!s.is_litteral())
                return false;
        }

        return true;
    }

    static const std::string type_name;

    static void reg(interpreter_context& context);
};

#endif
