/*
 *  Copyright (c) 2012 The WebRTC 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.
 */
#include <stdlib.h>
#include <sys/stat.h>

#include <cstdio>
#include <iomanip>
#include <sstream>

#include "tools/converter/converter.h"

#ifdef WIN32
#define SEPARATOR '\\'
#define STAT _stat
#else
#define SEPARATOR '/'
#define STAT stat
#endif

namespace webrtc {
namespace test {

Converter::Converter(int width, int height)
    : width_(width),
      height_(height) {
}

bool Converter::ConvertRGBAToI420Video(std::string frames_dir,
                                       std::string output_file_name,
                                       bool delete_frames) {
  FILE* output_file = fopen(output_file_name.c_str(), "wb");

  // Open output file in append mode.
  if (output_file == NULL) {
    fprintf(stderr, "Couldn't open input file for reading: %s\n",
            output_file_name.c_str());
    return false;
  }

  int input_frame_size = InputFrameSize();
  uint8* rgba_buffer = new uint8[input_frame_size];
  int y_plane_size = YPlaneSize();
  uint8* dst_y = new uint8[y_plane_size];
  int u_plane_size = UPlaneSize();
  uint8* dst_u = new uint8[u_plane_size];
  int v_plane_size = VPlaneSize();
  uint8* dst_v = new uint8[v_plane_size];

  int counter = 0;  // Counter to form frame names.
  bool success = false;  // Is conversion successful.

  while (true) {
    std::string file_name = FormFrameName(4, counter);
    // Get full path file name.
    std::string input_file_name = FindFullFileName(frames_dir, file_name);

    if (FileExists(input_file_name)) {
      ++counter;  // Update counter for the next round.
    } else {
      fprintf(stdout, "Reached end of frames list\n");
      break;
    }

    // Read the RGBA frame into rgba_buffer.
    ReadRGBAFrame(input_file_name.c_str(), input_frame_size, rgba_buffer);

    // Delete the input frame.
    if (delete_frames) {
      if (remove(input_file_name.c_str()) != 0) {
        fprintf(stderr, "Cannot delete file %s\n", input_file_name.c_str());
      }
    }

    // Convert to I420 frame.
    libyuv::ABGRToI420(rgba_buffer, SrcStrideFrame(),
                       dst_y, DstStrideY(),
                       dst_u, DstStrideU(),
                       dst_v, DstStrideV(),
                       width_, height_);

    // Add the I420 frame to the YUV video file.
    success = AddYUVToFile(dst_y, y_plane_size, dst_u, u_plane_size,
                           dst_v, v_plane_size, output_file);


    if (!success) {
      fprintf(stderr, "LibYUV error during RGBA to I420 frame conversion\n");
      break;
    }
  }

  delete[] rgba_buffer;
  delete[] dst_y;
  delete[] dst_u;
  delete[] dst_v;

  fclose(output_file);

  return success;
}

bool Converter::AddYUVToFile(uint8* y_plane, int y_plane_size,
                             uint8* u_plane, int u_plane_size,
                             uint8* v_plane, int v_plane_size,
                             FILE* output_file) {
  bool success = AddYUVPlaneToFile(y_plane, y_plane_size, output_file) &&
                 AddYUVPlaneToFile(u_plane, u_plane_size, output_file) &&
                 AddYUVPlaneToFile(v_plane, v_plane_size, output_file);
  return success;
}

bool Converter::AddYUVPlaneToFile(uint8* yuv_plane, int yuv_plane_size,
                                  FILE* file) {
  size_t bytes_written = fwrite(yuv_plane, 1, yuv_plane_size, file);

  if (bytes_written != static_cast<size_t>(yuv_plane_size)) {
    fprintf(stderr, "Number of bytes written (%d) doesn't match size of y plane"
            " (%d)\n", static_cast<int>(bytes_written), yuv_plane_size);
    return false;
  }
  return true;
}

bool Converter::ReadRGBAFrame(const char* input_file_name, int input_frame_size,
                              unsigned char* buffer) {
  FILE* input_file = fopen(input_file_name, "rb");
  if (input_file == NULL) {
    fprintf(stderr, "Couldn't open input file for reading: %s\n",
            input_file_name);
    return false;
  }

  size_t nbr_read = fread(buffer, 1, input_frame_size, input_file);
  fclose(input_file);

  if (nbr_read != static_cast<size_t>(input_frame_size)) {
    fprintf(stderr, "Error reading from input file: %s\n", input_file_name);
    return false;
  }

  return true;
}

std::string Converter::FindFullFileName(std::string dir_name,
                                        std::string file_name) {
  return dir_name + SEPARATOR + file_name;
}

bool Converter:: FileExists(std::string file_name_to_check) {
  struct STAT file_info;
  int result = STAT(file_name_to_check.c_str(), &file_info);
  return (result == 0);
}

std::string Converter::FormFrameName(int width, int number) {
  std::stringstream tmp;

  // Zero-pad number to a string.
  tmp << std::setfill('0') << std::setw(width) << number;

  return "frame_" + tmp.str();
}

}  // namespace test
}  // namespace webrtc


