// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/bind.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/decoder_buffer.h"
#include "media/base/stream_parser_buffer.h"
#include "media/base/test_data_util.h"
#include "media/base/text_track_config.h"
#include "media/base/video_decoder_config.h"
#include "media/mp3/mp3_stream_parser.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace media {

class MP3StreamParserTest : public testing::Test {
 public:
  MP3StreamParserTest() {}

 protected:
  MP3StreamParser parser_;
  std::stringstream results_stream_;

  bool AppendData(const uint8* data, size_t length) {
    return parser_.Parse(data, length);
  }

  bool AppendDataInPieces(const uint8* data, size_t length, size_t piece_size) {
    const uint8* start = data;
    const uint8* end = data + length;
    while (start < end) {
      size_t append_size =
          std::min(piece_size, static_cast<size_t>(end - start));
      if (!AppendData(start, append_size))
        return false;
      start += append_size;
    }
    return true;
  }

  void OnInitDone(bool success, base::TimeDelta duration) {
    DVLOG(1) << __FUNCTION__ << "(" << success << ", "
             << duration.InMilliseconds() << ")";
  }

  bool OnNewConfig(const AudioDecoderConfig& audio_config,
                   const VideoDecoderConfig& video_config,
                   const StreamParser::TextTrackConfigMap& text_config) {
    DVLOG(1) << __FUNCTION__ << "(" << audio_config.IsValidConfig() << ", "
             << video_config.IsValidConfig() << ")";
    EXPECT_TRUE(audio_config.IsValidConfig());
    EXPECT_FALSE(video_config.IsValidConfig());
    return true;
  }

  std::string BufferQueueToString(const StreamParser::BufferQueue& buffers) {
    std::stringstream ss;

    ss << "{";
    for (StreamParser::BufferQueue::const_iterator itr = buffers.begin();
         itr != buffers.end();
         ++itr) {
      ss << " " << (*itr)->timestamp().InMilliseconds();
      if ((*itr)->IsKeyframe())
        ss << "K";
    }
    ss << " }";

    return ss.str();
  }

  bool OnNewBuffers(const StreamParser::BufferQueue& audio_buffers,
                    const StreamParser::BufferQueue& video_buffers) {
    EXPECT_FALSE(audio_buffers.empty());
    EXPECT_TRUE(video_buffers.empty());

    std::string buffers_str = BufferQueueToString(audio_buffers);
    DVLOG(1) << __FUNCTION__ << " : " << buffers_str;
    results_stream_ << buffers_str;
    return true;
  }

  void OnKeyNeeded(const std::string& type,
                   const std::vector<uint8>& init_data) {
    DVLOG(1) << __FUNCTION__ << "(" << type << ", " << init_data.size() << ")";
  }

  void OnNewSegment() {
    DVLOG(1) << __FUNCTION__;
    results_stream_ << "NewSegment";
  }

  void OnEndOfSegment() {
    DVLOG(1) << __FUNCTION__;
    results_stream_ << "EndOfSegment";
  }

  void InitializeParser() {
    parser_.Init(
        base::Bind(&MP3StreamParserTest::OnInitDone, base::Unretained(this)),
        base::Bind(&MP3StreamParserTest::OnNewConfig, base::Unretained(this)),
        base::Bind(&MP3StreamParserTest::OnNewBuffers, base::Unretained(this)),
        StreamParser::NewTextBuffersCB(),
        base::Bind(&MP3StreamParserTest::OnKeyNeeded, base::Unretained(this)),
        base::Bind(&MP3StreamParserTest::OnNewSegment, base::Unretained(this)),
        base::Bind(&MP3StreamParserTest::OnEndOfSegment,
                   base::Unretained(this)),
        LogCB());
  }

  std::string ParseFile(const std::string& filename, int append_bytes) {
    results_stream_.clear();
    InitializeParser();

    scoped_refptr<DecoderBuffer> buffer = ReadTestDataFile(filename);
    EXPECT_TRUE(
        AppendDataInPieces(buffer->data(), buffer->data_size(), append_bytes));
    return results_stream_.str();
  }
};

// Test parsing with small prime sized chunks to smoke out "power of
// 2" field size assumptions.
TEST_F(MP3StreamParserTest, UnalignedAppend) {
  std::string expected =
      "NewSegment"
      "{ 0K }"
      "{ 26K }"
      "{ 52K }"
      "{ 78K }"
      "{ 104K }"
      "{ 130K }"
      "{ 156K }"
      "{ 182K }"
      "EndOfSegment"
      "NewSegment"
      "{ 208K }"
      "{ 235K }"
      "{ 261K }"
      "EndOfSegment"
      "NewSegment"
      "{ 287K }"
      "{ 313K }"
      "EndOfSegment";
  EXPECT_EQ(expected, ParseFile("sfx.mp3", 17));
}

// Test parsing with a larger piece size to verify that multiple buffers
// are passed to |new_buffer_cb_|.
TEST_F(MP3StreamParserTest, UnalignedAppend512) {
  std::string expected =
      "NewSegment"
      "{ 0K }"
      "{ 26K 52K 78K 104K }"
      "EndOfSegment"
      "NewSegment"
      "{ 130K 156K 182K }"
      "{ 208K 235K 261K 287K }"
      "{ 313K }"
      "EndOfSegment";
  EXPECT_EQ(expected, ParseFile("sfx.mp3", 512));
}

}  // namespace media