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

#include "remoting/test/video_frame_writer.h"

#include <stdint.h>

#include <vector>

#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/geometry/size.h"

namespace {

const base::FilePath::CharType kFrameFileName[] =
    FILE_PATH_LITERAL("frame.png");
const base::FilePath::CharType kRemotingFolder[] =
    FILE_PATH_LITERAL("remoting");
const base::FilePath::CharType kDumpFrameFolder[] =
    FILE_PATH_LITERAL("dumped_images");

// Used to create a unique folder path.
const char kDateAndTimeFormatString[] = "%d-%d-%d_%d-%d-%d";

}  // namespace

namespace remoting {
namespace test {

VideoFrameWriter::VideoFrameWriter()
    : instance_creation_time_(base::Time::Now()),
      frame_name_unique_number_(0) {}

VideoFrameWriter::~VideoFrameWriter() = default;

void VideoFrameWriter::WriteFrameToPath(const webrtc::DesktopFrame& frame,
                                        const base::FilePath& image_path) {
  unsigned char* frame_data = reinterpret_cast<unsigned char*>(frame.data());
  std::vector<unsigned char> png_encoded_data;

  if (!gfx::PNGCodec::Encode(
          frame_data, gfx::PNGCodec::FORMAT_BGRA,
          gfx::Size(frame.size().width(), frame.size().height()),
          frame.stride(), true, std::vector<gfx::PNGCodec::Comment>(),
          &png_encoded_data)) {
    LOG(WARNING) << "Failed to encode frame to PNG file";
    return;
  }

  // Dump contents (unsigned chars) to a file as a sequence of chars.
  if (!base::WriteFile(image_path, png_encoded_data)) {
    LOG(WARNING) << "Failed to write frame to disk";
  }
}

// Save video frame to path named with the |instance_creation_time|.
void VideoFrameWriter::WriteFrameToDefaultPath(
    const webrtc::DesktopFrame& frame) {
  base::FilePath dump_frame_file_path;
  if (!GetTempDir(&dump_frame_file_path)) {
    LOG(WARNING) << "Failed to retrieve temporary directory path";
    return;
  }

  dump_frame_file_path = dump_frame_file_path.Append(kRemotingFolder);
  dump_frame_file_path = dump_frame_file_path.Append(kDumpFrameFolder);
  if (!CreateDirectoryIfNotExists(dump_frame_file_path)) {
    return;
  }

  // Create a sub-folder using date and time to identify a particular test run.
  dump_frame_file_path = AppendCreationDateAndTime(dump_frame_file_path);
  if (!CreateDirectoryIfNotExists(dump_frame_file_path)) {
    return;
  }

  dump_frame_file_path = dump_frame_file_path.Append(kFrameFileName);

  dump_frame_file_path = dump_frame_file_path.InsertBeforeExtensionASCII(
      base::StringPrintf("(%d)", ++frame_name_unique_number_));

  LOG(INFO) << "Video frame dumped to: " << dump_frame_file_path.value();

  WriteFrameToPath(frame, dump_frame_file_path);
}

void VideoFrameWriter::HighlightRectInFrame(webrtc::DesktopFrame* frame,
                                            const webrtc::DesktopRect& rect) {
  if (rect.left() < 0 || rect.top() < 0 ||
      rect.right() >= frame->size().width() ||
      rect.bottom() >= frame->size().height()) {
    LOG(ERROR) << "Highlight rect lies outside of the frame.";
    return;
  }

  // Draw vertical borders.
  for (int y = rect.top(); y <= rect.bottom(); ++y) {
    ShiftPixelColor(frame, rect.left(), y, 128);
    ShiftPixelColor(frame, rect.right(), y, 128);
  }

  // Draw horizontal borders.
  for (int x = rect.left(); x <= rect.right(); ++x) {
    ShiftPixelColor(frame, x, rect.top(), 128);
    ShiftPixelColor(frame, x, rect.bottom(), 128);
  }
}

base::FilePath VideoFrameWriter::AppendCreationDateAndTime(
    const base::FilePath& file_path) {
  base::Time::Exploded exploded_time;
  instance_creation_time_.LocalExplode(&exploded_time);

  int year = exploded_time.year;
  int month = exploded_time.month;
  int day = exploded_time.day_of_month;
  int hour = exploded_time.hour;
  int minute = exploded_time.minute;
  int second = exploded_time.second;

  return file_path.AppendASCII(base::StringPrintf(
      kDateAndTimeFormatString, year, month, day, hour, minute, second));
}

bool VideoFrameWriter::CreateDirectoryIfNotExists(
    const base::FilePath& file_path) {
  if (!base::DirectoryExists(file_path) && !base::CreateDirectory(file_path)) {
    LOG(WARNING) << "Failed to create directory: " << file_path.value();
    return false;
  }
  return true;
}

void VideoFrameWriter::ShiftPixelColor(webrtc::DesktopFrame* frame,
                                       int x,
                                       int y,
                                       int shift_amount) {
  uint8_t* frame_pos = frame->data() + y * frame->stride() +
                       x * webrtc::DesktopFrame::kBytesPerPixel;
  frame_pos[2] = frame_pos[2] + shift_amount;
  frame_pos[1] = frame_pos[1] + shift_amount;
  frame_pos[0] = frame_pos[0] + shift_amount;
}

}  // namespace test
}  // namespace remoting