/*
 * Copyright (C) 2016 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 AST_H_

#define AST_H_

#include <android-base/macros.h>
#include <hidl-util/FQName.h>
#include <map>
#include <set>
#include <string>
#include <vector>

#include "Type.h"

namespace android {

struct Coordinator;
struct Formatter;
struct Interface;
struct Location;
struct Method;
struct NamedType;
struct TypedVar;
struct Scope;
struct EnumValue;

struct AST {
    AST(Coordinator *coordinator, const std::string &path);
    ~AST();

    bool setPackage(const char *package);
    bool addImport(const char *import);

    // package and version really.
    FQName package() const;
    bool isInterface(std::string *ifaceName) const;
    bool containsInterfaces() const;

    void enterScope(Scope *container);
    void leaveScope();
    Scope *scope();

    // Returns true iff successful.
    bool addTypeDef(const char *localName, Type *type, const Location &location,
            std::string *errorMsg);

    // Returns true iff successful.
    bool addScopedType(NamedType *type, std::string *errorMsg);

    void *scanner();
    void setScanner(void *scanner);

    const std::string &getFilename() const;

    // Look up an enum value by "FQName:valueName".
    EnumValue *lookupEnumValue(const FQName &fqName, std::string *errorMsg);

    // Look up a type by FQName, "pure" names, i.e. those without package
    // or version are first looked up in the current scope chain.
    // After that lookup proceeds to imports.
    Type *lookupType(const FQName &fqName);

    void addImportedAST(AST *ast);

    status_t generateCpp(const std::string &outputPath) const;
    status_t generateCppHeaders(const std::string &outputPath) const;
    status_t generateCppSources(const std::string &outputPath) const;
    status_t generateCppImpl(const std::string &outputPath) const;

    status_t generateJava(
            const std::string &outputPath,
            const std::string &limitToType) const;

    status_t generateJavaTypes(
            const std::string &outputPath,
            const std::string &limitToType) const;

    void getImportedPackages(std::set<FQName> *importSet) const;

    // Run getImportedPackages on this, then run getImportedPackages on
    // each AST in each package referenced in importSet.
    void getImportedPackagesHierarchy(std::set<FQName> *importSet) const;

    status_t generateVts(const std::string &outputPath) const;

    bool isJavaCompatible() const;

    // Return the set of FQNames for those interfaces and types that are
    // actually referenced in the AST, not merely imported.
    const std::set<FQName>& getImportedNames() const {
        return mImportedNames;
    }

    // Get transitive closure of imported interface/types.
    void getAllImportedNames(std::set<FQName> *allImportSet) const;

    void appendToExportedTypesVector(
            std::vector<const Type *> *exportedTypes) const;

    // used by the parser.
    void addSyntaxError();
    size_t syntaxErrors() const;

    bool isIBase() const;

    const Interface *getInterface() const;

private:
    Coordinator *mCoordinator;
    std::string mPath;
    std::vector<Scope *> mScopePath;

    void *mScanner;
    Scope *mRootScope;

    FQName mPackage;

    // A set of all external interfaces/types that are _actually_ referenced
    // in this AST, this is a subset of those specified in import statements.
    std::set<FQName> mImportedNames;

    // A set of all ASTs we explicitly or implicitly (types.hal) import.
    std::set<AST *> mImportedASTs;

    // If a single type (instead of the whole AST) is imported, the AST will be
    // present as a key to this map, with the value being a list of types
    // imported from this AST. If an AST appears in mImportedASTs but not in
    // mImportedTypes, then the whole AST is imported.
    std::map<AST *, std::set<Type *>> mImportedTypes;

    // Types keyed by full names defined in this AST.
    std::map<FQName, Type *> mDefinedTypesByFullName;

    // used by the parser.
    size_t mSyntaxErrors = 0;

    bool addScopedTypeInternal(
            NamedType *type,
            std::string *errorMsg);

    // Helper functions for lookupType.
    Type *lookupTypeLocally(const FQName &fqName);
    status_t lookupAutofilledType(const FQName &fqName, Type **returnedType);
    Type *lookupTypeFromImports(const FQName &fqName);

    // Find a type matching fqName (which may be partial) and if found
    // return the associated type and fill in the full "matchingName".
    // Only types defined in this very AST are considered.
    Type *findDefinedType(const FQName &fqName, FQName *matchingName) const;

    void getPackageComponents(std::vector<std::string> *components) const;

    void getPackageAndVersionComponents(
            std::vector<std::string> *components, bool cpp_compatible) const;

    static void generateCppPackageInclude(
            Formatter &out,
            const FQName &package,
            const std::string &klass);

    std::string makeHeaderGuard(const std::string &baseName,
                                bool indicateGenerated = true) const;
    void enterLeaveNamespace(Formatter &out, bool enter) const;

    static void generateCheckNonNull(Formatter &out, const std::string &nonNull);

    status_t generateInterfaceHeader(const std::string &outputPath) const;
    status_t generateHwBinderHeader(const std::string &outputPath) const;
    status_t generateStubHeader(const std::string &outputPath) const;
    status_t generateProxyHeader(const std::string &outputPath) const;
    status_t generatePassthroughHeader(const std::string &outputPath) const;

    status_t generateTypeSource(
            Formatter &out, const std::string &ifaceName) const;

    // a method, and in which interface is it originally defined.
    // be careful of the case where method.isHidlReserved(), where interface
    // is effectively useless.
    using MethodGenerator = std::function<status_t(const Method *, const Interface *)>;

    status_t generateStubImplHeader(const std::string &outputPath) const;
    status_t generateStubImplSource(const std::string &outputPath) const;

    status_t generateMethods(Formatter &out, MethodGenerator gen) const;
    status_t generateStubImplMethod(Formatter &out,
                                    const std::string &className,
                                    const Method *method) const;
    status_t generatePassthroughMethod(Formatter &out,
                                       const Method *method) const;
    status_t generateProxyMethodSource(Formatter &out,
                                       const std::string &className,
                                       const Method *method,
                                       const Interface *superInterface) const;

    void generateFetchSymbol(Formatter &out, const std::string &ifaceName) const;

    status_t generateProxySource(
            Formatter &out, const FQName &fqName) const;

    status_t generateStubSource(
            Formatter &out, const Interface *iface) const;

    status_t generateStubSourceForMethod(
            Formatter &out, const Interface *iface, const Method *method) const;

    status_t generatePassthroughSource(Formatter &out) const;

    status_t generateInterfaceSource(Formatter &out) const;

    enum InstrumentationEvent {
        SERVER_API_ENTRY = 0,
        SERVER_API_EXIT,
        CLIENT_API_ENTRY,
        CLIENT_API_EXIT,
        SYNC_CALLBACK_ENTRY,
        SYNC_CALLBACK_EXIT,
        ASYNC_CALLBACK_ENTRY,
        ASYNC_CALLBACK_EXIT,
        PASSTHROUGH_ENTRY,
        PASSTHROUGH_EXIT,
    };

    status_t generateCppAtraceCall(
            Formatter &out,
            InstrumentationEvent event,
            const Method *method) const;

    status_t generateCppInstrumentationCall(
            Formatter &out,
            InstrumentationEvent event,
            const Method *method) const;

    void declareCppReaderLocals(
            Formatter &out,
            const std::vector<TypedVar *> &arg,
            bool forResults) const;

    void emitCppReaderWriter(
            Formatter &out,
            const std::string &parcelObj,
            bool parcelObjIsPointer,
            const TypedVar *arg,
            bool isReader,
            Type::ErrorMode mode,
            bool addPrefixToName) const;

    void emitCppResolveReferences(
            Formatter &out,
            const std::string &parcelObj,
            bool parcelObjIsPointer,
            const TypedVar *arg,
            bool isReader,
            Type::ErrorMode mode,
            bool addPrefixToName) const;

    void emitJavaReaderWriter(
            Formatter &out,
            const std::string &parcelObj,
            const TypedVar *arg,
            bool isReader,
            bool addPrefixToName) const;

    status_t emitTypeDeclarations(Formatter &out) const;
    status_t emitJavaTypeDeclarations(Formatter &out) const;
    status_t emitVtsTypeDeclarations(Formatter &out) const;

    DISALLOW_COPY_AND_ASSIGN(AST);
};

}  // namespace android

#endif  // AST_H_