// Copyright (c) 2013, Kenton Varda <temporal@gmail.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef CAPNP_COMPILER_NODE_TRANSLATOR_H_
#define CAPNP_COMPILER_NODE_TRANSLATOR_H_

#include <capnp/orphan.h>
#include <capnp/compiler/grammar.capnp.h>
#include <capnp/schema.capnp.h>
#include <capnp/dynamic.h>
#include <kj/vector.h>
#include "error-reporter.h"

namespace capnp {
namespace compiler {

class NodeTranslator {
  // Translates one node in the schema from AST form to final schema form.  A "node" is anything
  // that has a unique ID, such as structs, enums, constants, and annotations, but not fields,
  // unions, enumerants, or methods (the latter set have 16-bit ordinals but not 64-bit global IDs).

public:
  class Resolver {
    // Callback class used to find other nodes relative to this one.

  public:
    struct ResolvedName {
      uint64_t id;
      Declaration::Which kind;
    };

    virtual kj::Maybe<ResolvedName> resolve(const DeclName::Reader& name) = 0;
    // Look up the given name, relative to this node, and return basic information about the
    // target.

    virtual kj::Maybe<Schema> resolveBootstrapSchema(uint64_t id) = 0;
    // Get the schema for the given ID.  If a schema is returned, it must be safe to traverse its
    // dependencies using Schema::getDependency().  A schema that is only at the bootstrap stage
    // is acceptable.
    //
    // Throws an exception if the id is not one that was found by calling resolve() or by
    // traversing other schemas.  Returns null if the ID is recognized, but the corresponding
    // schema node failed to be built for reasons that were already reported.

    virtual kj::Maybe<schema::Node::Reader> resolveFinalSchema(uint64_t id) = 0;
    // Get the final schema for the given ID.  A bootstrap schema is not acceptable.  A raw
    // node reader is returned rather than a Schema object because using a Schema object built
    // by the final schema loader could trigger lazy initialization of dependencies which could
    // lead to a cycle and deadlock.
    //
    // Throws an exception if the id is not one that was found by calling resolve() or by
    // traversing other schemas.  Returns null if the ID is recognized, but the corresponding
    // schema node failed to be built for reasons that were already reported.

    virtual kj::Maybe<uint64_t> resolveImport(kj::StringPtr name) = 0;
    // Get the ID of an imported file given the import path.
  };

  NodeTranslator(Resolver& resolver, ErrorReporter& errorReporter,
                 const Declaration::Reader& decl, Orphan<schema::Node> wipNode,
                 bool compileAnnotations);
  // Construct a NodeTranslator to translate the given declaration.  The wipNode starts out with
  // `displayName`, `id`, `scopeId`, and `nestedNodes` already initialized.  The `NodeTranslator`
  // fills in the rest.

  struct NodeSet {
    schema::Node::Reader node;
    // The main node.

    kj::Array<schema::Node::Reader> auxNodes;
    // Auxiliary nodes that were produced when translating this node and should be loaded along
    // with it.  In particular, structs that contain groups (or named unions) spawn extra nodes
    // representing those, and interfaces spawn struct nodes representing method params/results.
  };

  NodeSet getBootstrapNode();
  // Get an incomplete version of the node in which pointer-typed value expressions have not yet
  // been translated.  Instead, for all `schema.Value` objects representing pointer-type values,
  // the value is set to an appropriate "empty" value.  This version of the schema can be used to
  // bootstrap the dynamic API which can then in turn be used to encode the missing complex values.
  //
  // If the final node has already been built, this will actually return the final node (in fact,
  // it's the same node object).

  NodeSet finish();
  // Finish translating the node (including filling in all the pieces that are missing from the
  // bootstrap node) and return it.

private:
  Resolver& resolver;
  ErrorReporter& errorReporter;
  Orphanage orphanage;
  bool compileAnnotations;

  Orphan<schema::Node> wipNode;
  // The work-in-progress schema node.

  kj::Vector<Orphan<schema::Node>> groups;
  // If this is a struct node and it contains groups, these are the nodes for those groups,  which
  // must be loaded together with the top-level node.

  kj::Vector<Orphan<schema::Node>> paramStructs;
  // If this is an interface, these are the auto-generated structs representing params and results.

  struct UnfinishedValue {
    ValueExpression::Reader source;
    schema::Type::Reader type;
    schema::Value::Builder target;
  };
  kj::Vector<UnfinishedValue> unfinishedValues;
  // List of values in `wipNode` which have not yet been interpreted, because they are structs
  // or lists and as such interpreting them require using the types' schemas (to take advantage
  // of the dynamic API).  Once bootstrap schemas have been built, they can be used to interpret
  // these values.

  void compileNode(Declaration::Reader decl, schema::Node::Builder builder);

  void compileConst(Declaration::Const::Reader decl, schema::Node::Const::Builder builder);
  void compileAnnotation(Declaration::Annotation::Reader decl,
                         schema::Node::Annotation::Builder builder);

  class DuplicateNameDetector;
  class DuplicateOrdinalDetector;
  class StructLayout;
  class StructTranslator;

  void compileEnum(Void decl, List<Declaration>::Reader members,
                   schema::Node::Builder builder);
  void compileStruct(Void decl, List<Declaration>::Reader members,
                     schema::Node::Builder builder);
  void compileInterface(Declaration::Interface::Reader decl,
                        List<Declaration>::Reader members,
                        schema::Node::Builder builder);
  // The `members` arrays contain only members with ordinal numbers, in code order.  Other members
  // are handled elsewhere.

  uint64_t compileParamList(kj::StringPtr methodName, uint16_t ordinal, bool isResults,
                            Declaration::ParamList::Reader paramList);
  // Compile a param (or result) list and return the type ID of the struct type.

  bool compileType(TypeExpression::Reader source, schema::Type::Builder target);
  // Returns false if there was a problem, in which case value expressions of this type should
  // not be parsed.

  void compileDefaultDefaultValue(schema::Type::Reader type, schema::Value::Builder target);
  // Initializes `target` to contain the "default default" value for `type`.

  void compileBootstrapValue(ValueExpression::Reader source, schema::Type::Reader type,
                             schema::Value::Builder target);
  // Calls compileValue() if this value should be interpreted at bootstrap time.  Otheriwse,
  // adds the value to `unfinishedValues` for later evaluation.

  void compileValue(ValueExpression::Reader source, schema::Type::Reader type,
                    schema::Value::Builder target, bool isBootstrap);
  // Interprets the value expression and initializes `target` with the result.

  kj::Maybe<DynamicValue::Reader> readConstant(DeclName::Reader name, bool isBootstrap);
  // Get the value of the given constant.  May return null if some error occurs, which will already
  // have been reported.

  kj::Maybe<ListSchema> makeListSchemaOf(schema::Type::Reader elementType);
  // Construct a list schema representing a list of elements of the given type.  May return null if
  // some error occurs, which will already have been reported.

  Orphan<List<schema::Annotation>> compileAnnotationApplications(
      List<Declaration::AnnotationApplication>::Reader annotations,
      kj::StringPtr targetsFlagName);
};

class ValueTranslator {
public:
  class Resolver {
  public:
    virtual kj::Maybe<Schema> resolveType(uint64_t id) = 0;
    virtual kj::Maybe<DynamicValue::Reader> resolveConstant(DeclName::Reader name) = 0;
  };

  ValueTranslator(Resolver& resolver, ErrorReporter& errorReporter, Orphanage orphanage)
      : resolver(resolver), errorReporter(errorReporter), orphanage(orphanage) {}

  kj::Maybe<Orphan<DynamicValue>> compileValue(
      ValueExpression::Reader src, schema::Type::Reader type);

private:
  Resolver& resolver;
  ErrorReporter& errorReporter;
  Orphanage orphanage;

  Orphan<DynamicValue> compileValueInner(ValueExpression::Reader src, schema::Type::Reader type);
  // Helper for compileValue().

  void fillStructValue(DynamicStruct::Builder builder,
                       List<ValueExpression::FieldAssignment>::Reader assignments);
  // Interprets the given assignments and uses them to fill in the given struct builder.

  kj::String makeNodeName(uint64_t id);
  kj::String makeTypeName(schema::Type::Reader type);

  kj::Maybe<ListSchema> makeListSchemaOf(schema::Type::Reader elementType);
};

}  // namespace compiler
}  // namespace capnp

#endif  // CAPNP_COMPILER_NODE_TRANSLATOR_H_