/*
 * libjingle
 * Copyright 2004 Google Inc.
 *
 * 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.
 *  3. The name of the author may not be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 TALK_SESSION_PHONE_TESTUTILS_H_
#define TALK_SESSION_PHONE_TESTUTILS_H_

#include <string>
#include <vector>

#include "talk/base/basictypes.h"
#include "talk/base/sigslot.h"
#include "talk/base/window.h"
#include "talk/session/phone/mediachannel.h"
#include "talk/session/phone/videocommon.h"
#include "talk/session/phone/videocapturer.h"

namespace talk_base {
class ByteBuffer;
class StreamInterface;
}

namespace cricket {

// Returns size of 420 image with rounding on chroma for odd sizes.
#define I420_SIZE(w, h) (w * h + (((w + 1) / 2) * ((h + 1) / 2)) * 2)
// Returns size of ARGB image.
#define ARGB_SIZE(w, h) (w * h * 4)

template <class T> inline std::vector<T> MakeVector(const T a[], size_t s) {
  return std::vector<T>(a, a + s);
}
#define MAKE_VECTOR(a) cricket::MakeVector(a, ARRAY_SIZE(a))

struct RtpDumpPacket;
class RtpDumpWriter;
class VideoFrame;

struct RawRtpPacket {
  void WriteToByteBuffer(uint32 in_ssrc, talk_base::ByteBuffer* buf) const;
  bool ReadFromByteBuffer(talk_base::ByteBuffer* buf);
  // Check if this packet is the same as the specified packet except the
  // sequence number and timestamp, which should be the same as the specified
  // parameters.
  bool SameExceptSeqNumTimestampSsrc(
      const RawRtpPacket& packet, uint16 seq, uint32 ts, uint32 ssc) const;
  int size() const { return 28; }

  uint8 ver_to_cc;
  uint8 m_to_pt;
  uint16 sequence_number;
  uint32 timestamp;
  uint32 ssrc;
  char payload[16];
};

struct RawRtcpPacket {
  void WriteToByteBuffer(talk_base::ByteBuffer* buf) const;
  bool ReadFromByteBuffer(talk_base::ByteBuffer* buf);
  bool EqualsTo(const RawRtcpPacket& packet) const;

  uint8 ver_to_count;
  uint8 type;
  uint16 length;
  char payload[16];
};

class RtpTestUtility {
 public:
  static size_t GetTestPacketCount();

  // Write the first count number of kTestRawRtcpPackets or kTestRawRtpPackets,
  // depending on the flag rtcp. If it is RTP, use the specified SSRC. Return
  // true if successful.
  static bool WriteTestPackets(
      size_t count, bool rtcp, uint32 rtp_ssrc, RtpDumpWriter* writer);

  // Loop read the first count number of packets from the specified stream.
  // Verify the elapsed time of the dump packets increase monotonically. If the
  // stream is a RTP stream, verify the RTP sequence number, timestamp, and
  // payload. If the stream is a RTCP stream, verify the RTCP header and
  // payload.
  static bool VerifyTestPacketsFromStream(
      size_t count, talk_base::StreamInterface* stream, uint32 ssrc);

  // Verify the dump packet is the same as the raw RTP packet.
  static bool VerifyPacket(const RtpDumpPacket* dump,
                           const RawRtpPacket* raw,
                           bool header_only);

  static const uint32 kDefaultSsrc = 1;
  static const uint32 kRtpTimestampIncrease = 90;
  static const uint32 kDefaultTimeIncrease = 30;
  static const uint32 kElapsedTimeInterval = 10;
  static const RawRtpPacket kTestRawRtpPackets[];
  static const RawRtcpPacket kTestRawRtcpPackets[];

 private:
  RtpTestUtility() {}
};

// Test helper for testing VideoCapturer implementations.
class VideoCapturerListener : public sigslot::has_slots<> {
 public:
  explicit VideoCapturerListener(VideoCapturer* cap);

  CaptureResult start_result() const { return start_result_; }
  int frame_count() const { return frame_count_; }
  uint32 frame_fourcc() const { return frame_fourcc_; }
  int frame_width() const { return frame_width_; }
  int frame_height() const { return frame_height_; }
  uint32 frame_size() const { return frame_size_; }
  bool resolution_changed() const { return resolution_changed_; }

  void OnStartResult(VideoCapturer* capturer, CaptureResult result);
  void OnFrameCaptured(VideoCapturer* capturer, const CapturedFrame* frame);
  void OnCaptureEvent(VideoCapturer* capturer, CaptureEvent ev);

 private:
  CaptureResult start_result_;
  int frame_count_;
  uint32 frame_fourcc_;
  int frame_width_;
  int frame_height_;
  uint32 frame_size_;
  bool resolution_changed_;
};

class ScreencastEventCatcher : public sigslot::has_slots<> {
 public:
  ScreencastEventCatcher() : ssrc_(0), ev_(talk_base::WE_RESIZE) { }
  uint32 ssrc() const { return ssrc_; }
  talk_base::WindowEvent event() const { return ev_; }
  void OnEvent(uint32 ssrc, talk_base::WindowEvent ev) {
    ssrc_ = ssrc;
    ev_ = ev;
  }
 private:
  uint32 ssrc_;
  talk_base::WindowEvent ev_;
};

class VideoMediaErrorCatcher : public sigslot::has_slots<> {
 public:
  VideoMediaErrorCatcher() : ssrc_(0), error_(VideoMediaChannel::ERROR_NONE) { }
  uint32 ssrc() const { return ssrc_; }
  VideoMediaChannel::Error error() const { return error_; }
  void OnError(uint32 ssrc, VideoMediaChannel::Error error) {
    ssrc_ = ssrc;
    error_ = error;
  }
 private:
  uint32 ssrc_;
  VideoMediaChannel::Error error_;
};

// Returns the absolute path to a file in the testdata/ directory.
std::string GetTestFilePath(const std::string& filename);

// PSNR formula: psnr = 10 * log10 (Peak Signal^2 / mse)
// sse is set to a small number for identical frames or sse == 0
double ComputePSNR(double sse, double size);

double ComputeSumSquareError(const uint8 *org, const uint8 *rec,
                             int size);

// Loads the image with the specified prefix and size into |out|.
bool LoadPlanarYuvTestImage(const std::string& prefix,
                            int width, int height, uint8* out);

// Dumps the YUV image out to a file, for visual inspection.
// PYUV tool can be used to view dump files.
void DumpPlanarYuvTestImage(const std::string& prefix, const uint8* img,
                            int w, int h);

// Dumps the ARGB image out to a file, for visual inspection.
// ffplay tool can be used to view dump files.
void DumpPlanarArgbTestImage(const std::string& prefix, const uint8* img,
                             int w, int h);

// Compare two I420 frames.
bool VideoFrameEqual(const VideoFrame* frame0, const VideoFrame* frame1);

}  // namespace cricket

#endif  // TALK_SESSION_PHONE_TESTUTILS_H_
