/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef AAPT_VALUE_VISITOR_H
#define AAPT_VALUE_VISITOR_H

#include "ResourceValues.h"
#include "ResourceTable.h"

namespace aapt {

/**
 * Visits a value and invokes the appropriate method based on its type. Does not traverse
 * into compound types. Use ValueVisitor for that.
 */
struct RawValueVisitor {
    virtual ~RawValueVisitor() = default;

    virtual void visitItem(Item* value) {}
    virtual void visit(Reference* value) { visitItem(value); }
    virtual void visit(RawString* value) { visitItem(value); }
    virtual void visit(String* value) { visitItem(value); }
    virtual void visit(StyledString* value) { visitItem(value); }
    virtual void visit(FileReference* value) { visitItem(value); }
    virtual void visit(Id* value) { visitItem(value); }
    virtual void visit(BinaryPrimitive* value) { visitItem(value); }

    virtual void visit(Attribute* value) {}
    virtual void visit(Style* value) {}
    virtual void visit(Array* value) {}
    virtual void visit(Plural* value) {}
    virtual void visit(Styleable* value) {}
};

#define DECL_VISIT_COMPOUND_VALUE(T) \
    virtual void visit(T* value) { \
        visitSubValues(value); \
    }

/**
 * Visits values, and if they are compound values, visits the components as well.
 */
struct ValueVisitor : public RawValueVisitor {
    // The compiler will think we're hiding an overload, when we actually intend
    // to call into RawValueVisitor. This will expose the visit methods in the super
    // class so the compiler knows we are trying to call them.
    using RawValueVisitor::visit;

    void visitSubValues(Attribute* attribute) {
        for (Attribute::Symbol& symbol : attribute->symbols) {
            visit(&symbol.symbol);
        }
    }

    void visitSubValues(Style* style) {
        if (style->parent) {
            visit(&style->parent.value());
        }

        for (Style::Entry& entry : style->entries) {
            visit(&entry.key);
            entry.value->accept(this);
        }
    }

    void visitSubValues(Array* array) {
        for (std::unique_ptr<Item>& item : array->items) {
            item->accept(this);
        }
    }

    void visitSubValues(Plural* plural) {
        for (std::unique_ptr<Item>& item : plural->values) {
            if (item) {
                item->accept(this);
            }
        }
    }

    void visitSubValues(Styleable* styleable) {
        for (Reference& reference : styleable->entries) {
            visit(&reference);
        }
    }

    DECL_VISIT_COMPOUND_VALUE(Attribute);
    DECL_VISIT_COMPOUND_VALUE(Style);
    DECL_VISIT_COMPOUND_VALUE(Array);
    DECL_VISIT_COMPOUND_VALUE(Plural);
    DECL_VISIT_COMPOUND_VALUE(Styleable);
};

/**
 * Do not use directly. Helper struct for dyn_cast.
 */
template <typename T>
struct DynCastVisitor : public RawValueVisitor {
    T* value = nullptr;

    void visit(T* v) override {
        value = v;
    }
};

/**
 * Specialization that checks if the value is an Item.
 */
template <>
struct DynCastVisitor<Item> : public RawValueVisitor {
    Item* value = nullptr;

    void visitItem(Item* item) override {
        value = item;
    }
};

/**
 * Returns a valid pointer to T if the Value is of subtype T.
 * Otherwise, returns nullptr.
 */
template <typename T>
T* valueCast(Value* value) {
    if (!value) {
        return nullptr;
    }
    DynCastVisitor<T> visitor;
    value->accept(&visitor);
    return visitor.value;
}


inline void visitAllValuesInPackage(ResourceTablePackage* pkg, RawValueVisitor* visitor) {
    for (auto& type : pkg->types) {
        for (auto& entry : type->entries) {
            for (auto& configValue : entry->values) {
                configValue->value->accept(visitor);
            }
        }
    }
}

inline void visitAllValuesInTable(ResourceTable* table, RawValueVisitor* visitor) {
    for (auto& pkg : table->packages) {
        visitAllValuesInPackage(pkg.get(), visitor);
    }
}

} // namespace aapt

#endif // AAPT_VALUE_VISITOR_H
