/*
 * 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_TEST_CONTEXT_H
#define AAPT_TEST_CONTEXT_H

#include "NameMangler.h"
#include "util/Util.h"

#include "process/IResourceTableConsumer.h"
#include "process/SymbolTable.h"
#include "test/Common.h"

#include <cassert>
#include <list>

namespace aapt {
namespace test {

class Context : public IAaptContext {
public:
    SymbolTable* getExternalSymbols() override {
        return &mSymbols;
    }

    IDiagnostics* getDiagnostics() override {
        return &mDiagnostics;
    }

    const std::u16string& getCompilationPackage() override {
        assert(mCompilationPackage && "package name not set");
        return mCompilationPackage.value();
    }

    uint8_t getPackageId() override {
        assert(mPackageId && "package ID not set");
        return mPackageId.value();
    }

    NameMangler* getNameMangler() override {
        return &mNameMangler;
    }

    bool verbose() override {
        return false;
    }

private:
    friend class ContextBuilder;

    Context() : mNameMangler({}) {
    }

    Maybe<std::u16string> mCompilationPackage;
    Maybe<uint8_t> mPackageId;
    StdErrDiagnostics mDiagnostics;
    SymbolTable mSymbols;
    NameMangler mNameMangler;
};

class ContextBuilder {
private:
    std::unique_ptr<Context> mContext = std::unique_ptr<Context>(new Context());

public:
    ContextBuilder& setCompilationPackage(const StringPiece16& package) {
        mContext->mCompilationPackage = package.toString();
        return *this;
    }

    ContextBuilder& setPackageId(uint8_t id) {
        mContext->mPackageId = id;
        return *this;
    }

    ContextBuilder& setNameManglerPolicy(NameManglerPolicy policy) {
        mContext->mNameMangler = NameMangler(policy);
        return *this;
    }

    ContextBuilder& addSymbolSource(std::unique_ptr<ISymbolSource> src) {
        mContext->getExternalSymbols()->appendSource(std::move(src));
        return *this;
    }

    std::unique_ptr<Context> build() {
        return std::move(mContext);
    }
};

class StaticSymbolSourceBuilder {
public:
    StaticSymbolSourceBuilder& addPublicSymbol(const StringPiece16& name, ResourceId id,
                                               std::unique_ptr<Attribute> attr = {}) {
        std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(
                id, std::move(attr), true);
        mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
        mSymbolSource->mIdMap[id] = symbol.get();
        mSymbolSource->mSymbols.push_back(std::move(symbol));
        return *this;
    }

    StaticSymbolSourceBuilder& addSymbol(const StringPiece16& name, ResourceId id,
                                         std::unique_ptr<Attribute> attr = {}) {
        std::unique_ptr<SymbolTable::Symbol> symbol = util::make_unique<SymbolTable::Symbol>(
                id, std::move(attr), false);
        mSymbolSource->mNameMap[parseNameOrDie(name)] = symbol.get();
        mSymbolSource->mIdMap[id] = symbol.get();
        mSymbolSource->mSymbols.push_back(std::move(symbol));
        return *this;
    }

    std::unique_ptr<ISymbolSource> build() {
        return std::move(mSymbolSource);
    }

private:
    class StaticSymbolSource : public ISymbolSource {
    public:
        StaticSymbolSource() = default;

        std::unique_ptr<SymbolTable::Symbol> findByName(const ResourceName& name) override {
            auto iter = mNameMap.find(name);
            if (iter != mNameMap.end()) {
                return cloneSymbol(iter->second);
            }
            return nullptr;
        }

        std::unique_ptr<SymbolTable::Symbol> findById(ResourceId id) override {
            auto iter = mIdMap.find(id);
            if (iter != mIdMap.end()) {
                return cloneSymbol(iter->second);
            }
            return nullptr;
        }

        std::list<std::unique_ptr<SymbolTable::Symbol>> mSymbols;
        std::map<ResourceName, SymbolTable::Symbol*> mNameMap;
        std::map<ResourceId, SymbolTable::Symbol*> mIdMap;

    private:
        std::unique_ptr<SymbolTable::Symbol> cloneSymbol(SymbolTable::Symbol* sym) {
            std::unique_ptr<SymbolTable::Symbol> clone = util::make_unique<SymbolTable::Symbol>();
            clone->id = sym->id;
            if (sym->attribute) {
                clone->attribute = std::unique_ptr<Attribute>(sym->attribute->clone(nullptr));
            }
            clone->isPublic = sym->isPublic;
            return clone;
        }

        DISALLOW_COPY_AND_ASSIGN(StaticSymbolSource);
    };

    std::unique_ptr<StaticSymbolSource> mSymbolSource = util::make_unique<StaticSymbolSource>();
};

} // namespace test
} // namespace aapt

#endif /* AAPT_TEST_CONTEXT_H */