/*
 *  Copyright (c) 2012 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 TEST_WEBM_VIDEO_SOURCE_H_
#define TEST_WEBM_VIDEO_SOURCE_H_
#include <cstdarg>
#include <cstdio>
#include <cstdlib>
#include <new>
#include <string>
#include "nestegg/include/nestegg/nestegg.h"
#include "test/video_source.h"

namespace libvpx_test {

static int
nestegg_read_cb(void *buffer, size_t length, void *userdata) {
  FILE *f = reinterpret_cast<FILE *>(userdata);

  if (fread(buffer, 1, length, f) < length) {
    if (ferror(f))
      return -1;
    if (feof(f))
      return 0;
  }
  return 1;
}


static int
nestegg_seek_cb(int64_t offset, int whence, void *userdata) {
  FILE *f = reinterpret_cast<FILE *>(userdata);
  switch (whence) {
    case NESTEGG_SEEK_SET:
      whence = SEEK_SET;
      break;
    case NESTEGG_SEEK_CUR:
      whence = SEEK_CUR;
      break;
    case NESTEGG_SEEK_END:
      whence = SEEK_END;
      break;
  };
  return fseek(f, (long)offset, whence) ? -1 : 0;
}


static int64_t
nestegg_tell_cb(void *userdata) {
  FILE *f = reinterpret_cast<FILE *>(userdata);
  return ftell(f);
}


static void
nestegg_log_cb(nestegg *context, unsigned int severity, char const *format,
               ...) {
  va_list ap;

  va_start(ap, format);
  vfprintf(stderr, format, ap);
  fprintf(stderr, "\n");
  va_end(ap);
}

// This class extends VideoSource to allow parsing of WebM files,
// so that we can do actual file decodes.
class WebMVideoSource : public CompressedVideoSource {
 public:
  explicit WebMVideoSource(const std::string &file_name)
      : file_name_(file_name),
        input_file_(NULL),
        nestegg_ctx_(NULL),
        pkt_(NULL),
        video_track_(0),
        chunk_(0),
        chunks_(0),
        buf_(NULL),
        buf_sz_(0),
        frame_(0),
        end_of_file_(false) {
  }

  virtual ~WebMVideoSource() {
    if (input_file_)
      fclose(input_file_);
    if (nestegg_ctx_)
      nestegg_destroy(nestegg_ctx_);
  }

  virtual void Init() {
  }

  virtual void Begin() {
    input_file_ = OpenTestDataFile(file_name_);
    ASSERT_TRUE(input_file_ != NULL) << "Input file open failed. Filename: "
        << file_name_;

    nestegg_io io = {nestegg_read_cb, nestegg_seek_cb, nestegg_tell_cb,
                     input_file_};
    ASSERT_FALSE(nestegg_init(&nestegg_ctx_, io, NULL))
        << "nestegg_init failed";

    unsigned int n;
    ASSERT_FALSE(nestegg_track_count(nestegg_ctx_, &n))
        << "failed to get track count";

    for (unsigned int i = 0; i < n; i++) {
      int track_type = nestegg_track_type(nestegg_ctx_, i);
      ASSERT_GE(track_type, 0) << "failed to get track type";

      if (track_type == NESTEGG_TRACK_VIDEO) {
        video_track_ = i;
        break;
      }
    }

    FillFrame();
  }

  virtual void Next() {
    ++frame_;
    FillFrame();
  }

  void FillFrame() {
    ASSERT_TRUE(input_file_ != NULL);
    if (chunk_ >= chunks_) {
      unsigned int track;

      do {
        /* End of this packet, get another. */
        if (pkt_)
          nestegg_free_packet(pkt_);

        int again = nestegg_read_packet(nestegg_ctx_, &pkt_);
        ASSERT_GE(again, 0) << "nestegg_read_packet failed";
        if (!again) {
          end_of_file_ = true;
          return;
        }

        ASSERT_FALSE(nestegg_packet_track(pkt_, &track))
            << "nestegg_packet_track failed";
      } while (track != video_track_);

      ASSERT_FALSE(nestegg_packet_count(pkt_, &chunks_))
          << "nestegg_packet_count failed";
      chunk_ = 0;
    }

    ASSERT_FALSE(nestegg_packet_data(pkt_, chunk_, &buf_, &buf_sz_))
        << "nestegg_packet_data failed";
    chunk_++;
  }

  virtual const uint8_t *cxdata() const {
    return end_of_file_ ? NULL : buf_;
  }
  virtual const unsigned int frame_size() const { return buf_sz_; }
  virtual const unsigned int frame_number() const { return frame_; }

 protected:
  std::string file_name_;
  FILE *input_file_;
  nestegg *nestegg_ctx_;
  nestegg_packet *pkt_;
  unsigned int video_track_;
  unsigned int chunk_;
  unsigned int chunks_;
  uint8_t *buf_;
  size_t buf_sz_;
  unsigned int frame_;
  bool end_of_file_;
};

}  // namespace libvpx_test

#endif  // TEST_WEBM_VIDEO_SOURCE_H_
