// 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_BLOCK_PARSER_H_
#define SRC_BLOCK_PARSER_H_

#include <cassert>
#include <cstdint>
#include <type_traits>
#include <vector>

#include "block_header_parser.h"
#include "element_parser.h"
#include "var_int_parser.h"
#include "webm/callback.h"
#include "webm/dom_types.h"
#include "webm/element.h"
#include "webm/reader.h"
#include "webm/status.h"

namespace webm {

// Parses Block and SimpleBlock elements. It is recommended to use the
// BlockParser and SimpleBlockParser aliases.
// Spec reference:
// http://matroska.org/technical/specs/index.html#Block
// http://matroska.org/technical/specs/index.html#SimpleBlock
// http://www.webmproject.org/docs/container/#SimpleBlock
// http://www.webmproject.org/docs/container/#Block
// http://matroska.org/technical/specs/index.html#block_structure
// http://matroska.org/technical/specs/index.html#simpleblock_structure
template <typename T>
class BasicBlockParser : public ElementParser {
  static_assert(std::is_same<T, Block>::value ||
                    std::is_same<T, SimpleBlock>::value,
                "T must be Block or SimpleBlock");

 public:
  Status Init(const ElementMetadata& metadata, std::uint64_t max_size) override;

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

  bool WasSkipped() const override;

  // Gets the parsed block header information. The frames are not included. This
  // must not be called until the parse has been successfully completed.
  const T& value() const {
    assert(state_ == State::kDone);
    return value_;
  }

  // Gets the parsed block header information. The frames are not included. This
  // must not be called until the parse has been successfully completed.
  T* mutable_value() {
    assert(state_ == State::kDone);
    return &value_;
  }

 private:
  // The number of header bytes read (header meaning everything before the
  // frames).
  std::uint64_t header_bytes_read_ = 0;

  // The parsed header value for the element.
  T value_{};

  // Metadata for the frame that is currently being read.
  FrameMetadata frame_metadata_;

  // Parser for parsing header metadata that is common between Block and
  // SimpleBlock.
  BlockHeaderParser header_parser_;

  // Parser for parsing unsigned EBML variable-sized integers.
  VarIntParser uint_parser_;

  // The current lace size when parsing Xiph lace sizes.
  std::uint64_t xiph_lace_size_ = 0;

  // Lace (frame) sizes, where each entry represents the size of a frame.
  std::vector<std::uint64_t> lace_sizes_;

  // The current index into lace_sizes_ for the current frame being read.
  std::size_t current_lace_ = 0;

  // Parsing states for the finite-state machine.
  enum class State {
    /* clang-format off */
    // State                        Transitions to state        When
    kReadingHeader,              // kGettingAction              no lacing
                                 // kReadingLaceCount           yes lacing
    kReadingLaceCount,           // kGettingAction              no errors
    kGettingAction,              // kSkipping                   action == skip
                                 // kValidatingSize             no lacing
                                 // kReadingXiphLaceSizes       xiph lacing
                                 // kReadingFirstEbmlLaceSize   ebml lacing
                                 // kCalculatingFixedLaceSizes  fixed lacing
    kReadingXiphLaceSizes,       // kValidatingSize             all sizes read
    kReadingFirstEbmlLaceSize,   // kReadingEbmlLaceSizes       first size read
    kReadingEbmlLaceSizes,       // kValidatingSize             all sizes read
    kCalculatingFixedLaceSizes,  // kReadingFrames              no errors
    kValidatingSize,             // kReadingFrames              no errors
    kSkipping,                   // No transitions from here (must call Init)
    kReadingFrames,              // kDone                       all frames read
    kDone,                       // No transitions from here (must call Init)
    /* clang-format on */
  };

  // The current state of the parser.
  State state_ = State::kReadingHeader;
};

extern template class BasicBlockParser<Block>;
extern template class BasicBlockParser<SimpleBlock>;

using BlockParser = BasicBlockParser<Block>;
using SimpleBlockParser = BasicBlockParser<SimpleBlock>;

}  // namespace webm

#endif  // SRC_BLOCK_PARSER_H_
