// Copyright (c) 2016 The WebM project authors. All Rights Reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree. An additional intellectual property rights grant can be found
// in the file PATENTS.  All contributing project authors may
// be found in the AUTHORS file in the root of the source tree.
#ifndef SRC_MASTER_VALUE_PARSER_H_
#define SRC_MASTER_VALUE_PARSER_H_

#include <cassert>
#include <cstdint>
#include <functional>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>

#include "src/master_parser.h"
#include "src/skip_callback.h"
#include "webm/callback.h"
#include "webm/element.h"
#include "webm/id.h"
#include "webm/reader.h"
#include "webm/status.h"

namespace webm {

// Parses Master elements from an EBML stream, storing child values in a
// structure of type T. This class differs from MasterParser in that
// MasterParser does not collect the parsed data into an object that can then be
// retrieved.
//
// For example, consider the following Foo object, which represents a master
// element that contains two booleans:
//
// struct Foo {
//   Element<bool> bar;
//   Element<bool> baz;
// };
//
// A FooParser implemented via MasterParser, like below, could be used to parse
// the master element and the boolean children, but the boolean values could not
// be retrieved from the parser.
//
// struct FooParser : public MasterParser {
//   FooParser()
//       : MasterParser({Id::kBar, new BoolParser},     // std::pair<*> types
//                      {Id::kBaz, new BoolParser}) {}  // omitted for brevity.
// };
//
// However, if FooParser is implemented via MasterValueParser<Foo>, then the
// boolean values will be parsed into a Foo object that can be retrieved from
// FooParser via its value() and mutable_value() methods.
//
// struct FooParser : public MasterValueParser<Foo> {
//   FooParser()
//       : MasterValueParser(MakeChild<BoolParser>(Id::kBar, &Foo::bar),
//                           MakeChild<BoolParser>(Id::kBaz, &Foo::baz)) {}
// };
template <typename T>
class MasterValueParser : public ElementParser {
 public:
  Status Init(const ElementMetadata& metadata,
              std::uint64_t max_size) override {
    assert(metadata.size == kUnknownElementSize || metadata.size <= max_size);

    PreInit();

    const Status status = master_parser_.Init(metadata, max_size);
    if (!status.completed_ok()) {
      return status;
    }

    return status;
  }

  void InitAfterSeek(const Ancestory& child_ancestory,
                     const ElementMetadata& child_metadata) override {
    PreInit();
    started_done_ = true;
    master_parser_.InitAfterSeek(child_ancestory, child_metadata);
  }

  Status Feed(Callback* callback, Reader* reader,
              std::uint64_t* num_bytes_read) override {
    assert(callback != nullptr);
    assert(reader != nullptr);
    assert(num_bytes_read != nullptr);

    *num_bytes_read = 0;

    if (!parse_complete_) {
      // TODO(mjbshaw): just call Reader::Skip if element's size is known and
      // action is skip.
      SkipCallback skip_callback;
      if (action_ == Action::kSkip) {
        callback = &skip_callback;
      }

      Status status = master_parser_.Feed(callback, reader, num_bytes_read);
      // Check if we've artificially injected an error code, and if so, switch
      // into skipping mode.
      if (status.code == Status::kSwitchToSkip) {
        assert(started_done_);
        assert(action_ == Action::kSkip);
        callback = &skip_callback;
        std::uint64_t local_num_bytes_read;
        status = master_parser_.Feed(callback, reader, &local_num_bytes_read);
        *num_bytes_read += local_num_bytes_read;
      }
      if (!status.completed_ok()) {
        return status;
      }
      parse_complete_ = true;
    }

    if (!started_done_) {
      Status status = OnParseStarted(callback, &action_);
      if (!status.completed_ok()) {
        return status;
      }
      started_done_ = true;
    }

    if (action_ != Action::kSkip) {
      return OnParseCompleted(callback);
    }

    return Status(Status::kOkCompleted);
  }

  bool GetCachedMetadata(ElementMetadata* metadata) override {
    return master_parser_.GetCachedMetadata(metadata);
  }

  bool WasSkipped() const override { return action_ == Action::kSkip; }

  const T& value() const { return value_; }

  T* mutable_value() { return &value_; }

 protected:
  // Users and subclasses are not meant to use the Tag* classes; they're an
  // internal implementation detail.
  // A tag that will cause the internal ChildParser to use the parsed child as a
  // start event.
  struct TagUseAsStart {};
  // A tag that will cause the internal ChildParser to call OnChildParsed once
  // it has been fully parsed.
  struct TagNotifyOnParseComplete {};

  // A factory that will create a std::pair<Id, std::unique_ptr<ElementParser>>.
  // Users and subclasses are not meant to use this class directly, as it is an
  // internal implementation detail of this class. Subclasses should use
  // MakeChild instead of using this class directly.
  template <typename Parser, typename Value, typename... Tags>
  class SingleChildFactory {
   public:
    constexpr SingleChildFactory(Id id, Element<Value> T::*member)
        : id_(id), member_(member) {}

    // Builds a std::pair<Id, std::unique_ptr<ElementParser>>. The parent
    // pointer must be a pointer to the MasterValueParser that is being
    // constructed. The given value pointer must be the pointer to the fully
    // constructed MasterValueParser::value_ object.
    std::pair<Id, std::unique_ptr<ElementParser>> BuildParser(
        MasterValueParser* parent, T* value) {
      assert(parent != nullptr);
      assert(value != nullptr);

      Element<Value>* child_member = &(value->*member_);
      auto lambda = [child_member](Parser* parser) {
        child_member->Set(std::move(*parser->mutable_value()), true);
      };
      return {id_, MakeChildParser<Parser, Value, Tags...>(
                       parent, std::move(lambda), child_member)};
    }

    // If called, OnParseStarted will be called on the parent element when this
    // particular element is encountered.
    constexpr SingleChildFactory<Parser, Value, TagUseAsStart, Tags...>
    UseAsStartEvent() const {
      return {id_, member_};
    }

    // If called, OnChildParsed will be called on the parent element when this
    // particular element is fully parsed.
    constexpr SingleChildFactory<Parser, Value, TagNotifyOnParseComplete,
                                 Tags...>
    NotifyOnParseComplete() const {
      return {id_, member_};
    }

   private:
    Id id_;
    Element<Value> T::*member_;
  };

  template <typename Parser, typename Value, typename... Tags>
  class RepeatedChildFactory {
   public:
    constexpr RepeatedChildFactory(Id id,
                                   std::vector<Element<Value>> T::*member)
        : id_(id), member_(member) {}

    // Builds a std::pair<Id, std::unique_ptr<ElementParser>>. The parent
    // pointer must be a pointer to the MasterValueParser that is being
    // constructed. The given value pointer must be the pointer to the fully
    // constructed MasterValueParser::value_ object.
    std::pair<Id, std::unique_ptr<ElementParser>> BuildParser(
        MasterValueParser* parent, T* value) {
      assert(parent != nullptr);
      assert(value != nullptr);

      std::vector<Element<Value>>* child_member = &(value->*member_);
      auto lambda = [child_member](Parser* parser) {
        if (child_member->size() == 1 && !child_member->front().is_present()) {
          child_member->clear();
        }
        child_member->emplace_back(std::move(*parser->mutable_value()), true);
      };
      return {id_, MakeChildParser<Parser, Value, Tags...>(
                       parent, std::move(lambda), child_member)};
    }

    // If called, OnParseStarted will be called on the parent element when this
    // particular element is encountered.
    constexpr RepeatedChildFactory<Parser, Value, TagUseAsStart, Tags...>
    UseAsStartEvent() const {
      return {id_, member_};
    }

    // If called, OnChildParsed will be called on the parent element when this
    // particular element is fully parsed.
    constexpr RepeatedChildFactory<Parser, Value, TagNotifyOnParseComplete,
                                   Tags...>
    NotifyOnParseComplete() const {
      return {id_, member_};
    }

   private:
    Id id_;
    std::vector<Element<Value>> T::*member_;
  };

  // Constructs a new parser. Each argument must be a *ChildFactory, constructed
  // from the MakeChild method.
  template <typename... Args>
  explicit MasterValueParser(Args&&... args)
      : master_parser_(args.BuildParser(this, &value_)...) {}

  // Returns a factory that will produce a
  // std::pair<Id, std::unique_ptr<ElementParser>>. When a child element of the
  // given ID is encountered, a parser of type Parser will be used to parse it,
  // and store its value in the member pointer when the parse is complete. The
  // given default value will be used in the event that the child element has a
  // zero size. This method is only meant to be used by subclasses to provide
  // the necessary factories to the constructor.
  template <typename Parser, typename Value>
  static SingleChildFactory<Parser, Value> MakeChild(
      Id id, Element<Value> T::*member) {
    static_assert(std::is_base_of<ElementParser, Parser>::value,
                  "Parser must derive from ElementParser");
    return SingleChildFactory<Parser, Value>(id, member);
  }

  template <typename Parser, typename Value>
  static RepeatedChildFactory<Parser, Value> MakeChild(
      Id id, std::vector<Element<Value>> T::*member) {
    static_assert(std::is_base_of<ElementParser, Parser>::value,
                  "Parser must derive from ElementParser");
    return RepeatedChildFactory<Parser, Value>(id, member);
  }

  // Gets the metadata for this element, setting the EBML element ID to id. Only
  // call after Init() has been called.
  ElementMetadata metadata(Id id) const {
    return {id, master_parser_.header_size(), master_parser_.size(),
            master_parser_.position()};
  }

  // This method will be called once the element has been fully parsed, or a
  // particular child element of interest (see UseAsStartEvent()) is
  // encountered. By default it just sets *action to Action::kRead and returns
  // Status::kOkCompleted. May be overridden (i.e. in order to call a Callback
  // method). Returning anything other than Status::kOkCompleted will stop
  // parsing and the status to be returned by Init or Feed (whichever originated
  // the call to this method). In this case, resuming parsing will result in
  // this method being called again.
  virtual Status OnParseStarted(Callback* callback, Action* action) {
    assert(callback != nullptr);
    assert(action != nullptr);
    *action = Action::kRead;
    return Status(Status::kOkCompleted);
  }

  // This method is the companion to OnParseStarted, and will only be called
  // after OnParseStarted has already been called and parsing has completed.
  // This will not be called if OnParseStarted set the action to Action::kSkip.
  // By default it just returns Status::kOkCompleted. Returning anything other
  // than Status::kOkCompleted will stop parsing and the status to be returned
  // by Init or Feed (whichever originated the call to this method). In this
  // case, resuming parsing will result in this method being called again.
  virtual Status OnParseCompleted(Callback* callback) {
    assert(callback != nullptr);
    return Status(Status::kOkCompleted);
  }

  // Returns true if the OnParseStarted method has already been called and has
  // completed.
  bool parse_started_event_completed() const { return started_done_; }

  // Derived classes may manually call OnParseStarted before calling Feed, in
  // which case this method should be called to inform this class that
  // OnParseStarted has already been called and it should not be called again.
  void set_parse_started_event_completed_with_action(Action action) {
    assert(!started_done_);

    action_ = action;
    started_done_ = true;
  }

  // This method will be called for each child element that has been fully
  // parsed for which NotifyOnParseComplete() was requested. The provided
  // metadata is for the child element that has just completed parsing. By
  // default this method does nothing.
  virtual void OnChildParsed(const ElementMetadata& /* metadata */) {}

 private:
  T value_;
  Action action_;
  bool parse_complete_;
  bool started_done_;
  // master_parser_ must be after value_ to ensure correct initialization order.
  MasterParser master_parser_;

  const ElementMetadata& child_metadata() const {
    return master_parser_.child_metadata();
  }

  // Helper struct that will be std::true_type if Tag is in Tags, or
  // std::false_type otherwise.
  template <typename Tag, typename... Tags>
  struct HasTag;

  // Base condition: Tags is empty, so it trivially does not contain Tag.
  template <typename Tag>
  struct HasTag<Tag> : std::false_type {};

  // If the head of the Tags list is a different tag, skip it and check the
  // remaining tags.
  template <typename Tag, typename DifferentTag, typename... Tags>
  struct HasTag<Tag, DifferentTag, Tags...> : HasTag<Tag, Tags...> {};

  // If the head of the Tags list is the same as Tag, then we're done.
  template <typename Tag, typename... Tags>
  struct HasTag<Tag, Tag, Tags...> : std::true_type {};

  template <typename Base, typename F, typename... Tags>
  class ChildParser : public Base {
   public:
    using Base::WasSkipped;

    template <typename... Args>
    explicit ChildParser(MasterValueParser* parent, F consume_element_value,
                         Args&&... base_args)
        : Base(std::forward<Args>(base_args)...),
          parent_(parent),
          consume_element_value_(std::move(consume_element_value)) {}

    ChildParser() = delete;
    ChildParser(const ChildParser&) = delete;
    ChildParser& operator=(const ChildParser&) = delete;

    Status Feed(Callback* callback, Reader* reader,
                std::uint64_t* num_bytes_read) override {
      *num_bytes_read = 0;

      Status status = Prepare(callback);
      if (!status.completed_ok()) {
        return status;
      }

      status = Base::Feed(callback, reader, num_bytes_read);
      if (status.completed_ok() && parent_->action_ != Action::kSkip &&
          !WasSkipped()) {
        consume_element_value_(this);
        if (has_tag<TagNotifyOnParseComplete>()) {
          parent_->OnChildParsed(parent_->child_metadata());
        }
      }
      return status;
    }

   private:
    MasterValueParser* parent_;
    F consume_element_value_;

    Status Prepare(Callback* callback) {
      if (has_tag<TagUseAsStart>() && !parent_->started_done_) {
        const Status status =
            parent_->OnParseStarted(callback, &parent_->action_);
        if (!status.completed_ok()) {
          return status;
        }
        parent_->started_done_ = true;
        if (parent_->action_ == Action::kSkip) {
          return Status(Status::kSwitchToSkip);
        }
      }
      return Status(Status::kOkCompleted);
    }

    template <typename Tag>
    constexpr static bool has_tag() {
      return HasTag<Tag, Tags...>::value;
    }
  };

  // Returns a std::unique_ptr<ElementParser> that points to a ChildParser
  // when the Parser's constructor does not take a Value parameter.
  template <typename Parser, typename Value, typename... Tags, typename F>
  static typename std::enable_if<!std::is_constructible<Parser, Value>::value,
                                 std::unique_ptr<ElementParser>>::type
  MakeChildParser(MasterValueParser* parent, F consume_element_value, ...) {
    return std::unique_ptr<ElementParser>(new ChildParser<Parser, F, Tags...>(
        parent, std::move(consume_element_value)));
  }

  // Returns a std::unique_ptr<ElementParser> that points to a ChildParser
  // when the Parser's constructor does take a Value parameter.
  template <typename Parser, typename Value, typename... Tags, typename F>
  static typename std::enable_if<std::is_constructible<Parser, Value>::value,
                                 std::unique_ptr<ElementParser>>::type
  MakeChildParser(MasterValueParser* parent, F consume_element_value,
                  const Element<Value>* default_value) {
    return std::unique_ptr<ElementParser>(new ChildParser<Parser, F, Tags...>(
        parent, std::move(consume_element_value), default_value->value()));
  }

  // Returns a std::unique_ptr<ElementParser> that points to a ChildParser
  // when the Parser's constructor does take a Value parameter.
  template <typename Parser, typename Value, typename... Tags, typename F>
  static typename std::enable_if<std::is_constructible<Parser, Value>::value,
                                 std::unique_ptr<ElementParser>>::type
  MakeChildParser(MasterValueParser* parent, F consume_element_value,
                  const std::vector<Element<Value>>* member) {
    Value default_value{};
    if (!member->empty()) {
      default_value = member->front().value();
    }
    return std::unique_ptr<ElementParser>(new ChildParser<Parser, F, Tags...>(
        parent, std::move(consume_element_value), std::move(default_value)));
  }

  // Initializes object state. Call immediately before initializing
  // master_parser_.
  void PreInit() {
    value_ = {};
    action_ = Action::kRead;
    parse_complete_ = false;
    started_done_ = false;
  }
};

}  // namespace webm

#endif  // SRC_MASTER_VALUE_PARSER_H_
