#ifndef CAFFE_UTIL_IO_H_
#define CAFFE_UTIL_IO_H_

#include <boost/filesystem.hpp>
#include <iomanip>
#include <iostream>  // NOLINT(readability/streams)
#include <string>

#include "google/protobuf/message.h"

#include "caffe/common.hpp"
#include "caffe/proto/caffe.pb.h"
#include "caffe/util/format.hpp"

#ifndef CAFFE_TMP_DIR_RETRIES
#define CAFFE_TMP_DIR_RETRIES 100
#endif

namespace caffe {

using ::google::protobuf::Message;
using ::boost::filesystem::path;

inline void MakeTempFilename(string* temp_filename) {
  temp_filename->clear();
  *temp_filename = "/tmp/caffe_test.XXXXXX";
  char* temp_filename_cstr = new char[temp_filename->size() + 1];
  // NOLINT_NEXT_LINE(runtime/printf)
  strcpy(temp_filename_cstr, temp_filename->c_str());
  int fd = mkstemp(temp_filename_cstr);
  CHECK_GE(fd, 0) << "Failed to open a temporary file at: " << *temp_filename;
  close(fd);
  *temp_filename = temp_filename_cstr;
  delete[] temp_filename_cstr;
}

inline void MakeTempDir(string* temp_dirname) {
  temp_dirname->clear();
  *temp_dirname = "/tmp/caffe_test.XXXXXX";
  char* temp_dirname_cstr = new char[temp_dirname->size() + 1];
  // NOLINT_NEXT_LINE(runtime/printf)
  strcpy(temp_dirname_cstr, temp_dirname->c_str());
  char* mkdtemp_result = mkdtemp(temp_dirname_cstr);
  CHECK(mkdtemp_result != NULL)
      << "Failed to create a temporary directory at: " << *temp_dirname;
  *temp_dirname = temp_dirname_cstr;
  delete[] temp_dirname_cstr;
}

bool ReadProtoFromTextFile(const char* filename, Message* proto);

inline bool ReadProtoFromTextFile(const string& filename, Message* proto) {
  return ReadProtoFromTextFile(filename.c_str(), proto);
}

inline void ReadProtoFromTextFileOrDie(const char* filename, Message* proto) {
  CHECK(ReadProtoFromTextFile(filename, proto));
}

inline void ReadProtoFromTextFileOrDie(const string& filename, Message* proto) {
  ReadProtoFromTextFileOrDie(filename.c_str(), proto);
}

void WriteProtoToTextFile(const Message& proto, const char* filename);
inline void WriteProtoToTextFile(const Message& proto, const string& filename) {
  WriteProtoToTextFile(proto, filename.c_str());
}

bool ReadProtoFromBinaryFile(const char* filename, Message* proto);

inline bool ReadProtoFromBinaryFile(const string& filename, Message* proto) {
  return ReadProtoFromBinaryFile(filename.c_str(), proto);
}

inline void ReadProtoFromBinaryFileOrDie(const char* filename, Message* proto) {
  CHECK(ReadProtoFromBinaryFile(filename, proto));
}

inline void ReadProtoFromBinaryFileOrDie(const string& filename,
                                         Message* proto) {
  ReadProtoFromBinaryFileOrDie(filename.c_str(), proto);
}


void WriteProtoToBinaryFile(const Message& proto, const char* filename);
inline void WriteProtoToBinaryFile(
    const Message& proto, const string& filename) {
  WriteProtoToBinaryFile(proto, filename.c_str());
}

bool ReadFileToDatum(const string& filename, const int label, Datum* datum);

inline bool ReadFileToDatum(const string& filename, Datum* datum) {
  return ReadFileToDatum(filename, -1, datum);
}

bool ReadImageToDatum(const string& filename, const int label,
    const int height, const int width, const bool is_color,
    const std::string & encoding, Datum* datum);

inline bool ReadImageToDatum(const string& filename, const int label,
    const int height, const int width, const bool is_color, Datum* datum) {
  return ReadImageToDatum(filename, label, height, width, is_color,
                          "", datum);
}

inline bool ReadImageToDatum(const string& filename, const int label,
    const int height, const int width, Datum* datum) {
  return ReadImageToDatum(filename, label, height, width, true, datum);
}

inline bool ReadImageToDatum(const string& filename, const int label,
    const bool is_color, Datum* datum) {
  return ReadImageToDatum(filename, label, 0, 0, is_color, datum);
}

inline bool ReadImageToDatum(const string& filename, const int label,
    Datum* datum) {
  return ReadImageToDatum(filename, label, 0, 0, true, datum);
}

inline bool ReadImageToDatum(const string& filename, const int label,
    const std::string & encoding, Datum* datum) {
  return ReadImageToDatum(filename, label, 0, 0, true, encoding, datum);
}


bool ReadImageToDatumSeq(const string& filename, vector<int>& label,
    const int height, const int width, const bool is_color,
    const std::string & encoding, DatumSeq* datum);

inline bool ReadImageToDatumSeq(const string& filename, vector<int>& label,
    const int height, const int width, const bool is_color, DatumSeq* datum) {
  return ReadImageToDatumSeq(filename, label, height, width, is_color,
                          "", datum);
}

inline bool ReadImageToDatumSeq(const string& filename, vector<int>& label,
    const int height, const int width, DatumSeq* datum) {
  return ReadImageToDatumSeq(filename, label, height, width, true, datum);
}

inline bool ReadImageToDatumSeq(const string& filename, vector<int>& label,
    const bool is_color, DatumSeq* datum) {
  return ReadImageToDatumSeq(filename, label, 0, 0, is_color, datum);
}

inline bool ReadImageToDatumSeq(const string& filename, vector<int>& label,
    DatumSeq* datum) {
  return ReadImageToDatumSeq(filename, label, 0, 0, true, datum);
}

inline bool ReadImageToDatumSeq(const string& filename, vector<int>& label,
    const std::string & encoding, DatumSeq* datum) {
  return ReadImageToDatumSeq(filename, label, 0, 0, true, encoding, datum);
}


bool DecodeDatumNative(Datum* datum);
bool DecodeDatum(Datum* datum, bool is_color);

cv::Mat ReadImageToCVMat(const string& filename,
    const int height, const int width, const bool is_color);

cv::Mat ReadImageToCVMat(const string& filename,
    const int height, const int width);

cv::Mat ReadImageToCVMat(const string& filename,
    const bool is_color);

cv::Mat ReadImageToCVMat(const string& filename);

cv::Mat DecodeDatumToCVMatNative(const Datum& datum);
cv::Mat DecodeDatumToCVMatNative(const DatumSeq& datum);
cv::Mat DecodeDatumToCVMat(const Datum& datum, bool is_color);
cv::Mat DecodeDatumToCVMat(const DatumSeq& datum, bool is_color);

void CVMatToDatum(const cv::Mat& cv_img, Datum* datum);
void CVMatToDatumSeq(const cv::Mat& cv_img, DatumSeq* datum);
cv::Mat DatumSeqtoCVMat(const DatumSeq& datum, bool is_color);
bool ReadImageToDatumSeq_constomized(const string& filename, vector<int>& label,
    const int height, const int width, const bool is_color,
    const std::string & encoding, DatumSeq* datum);

inline bool ReadImageToDatumSeq_constomized(const string& filename, vector<int>& label,
    const int height, const int width, const bool is_color, DatumSeq* datum) {
  return ReadImageToDatumSeq_constomized(filename, label, height, width, is_color,
                          "", datum);
}

inline bool ReadImageToDatumSeq_constomized(const string& filename, vector<int>& label,
    const int height, const int width, DatumSeq* datum) {
  return ReadImageToDatumSeq_constomized(filename, label, height, width, true, datum);
}

inline bool ReadImageToDatumSeq_constomized(const string& filename, vector<int>& label,
    const bool is_color, DatumSeq* datum) {
  return ReadImageToDatumSeq_constomized(filename, label, 0, 0, is_color, datum);
}

inline bool ReadImageToDatumSeq_constomized(const string& filename, vector<int>& label,
    DatumSeq* datum) {
  return ReadImageToDatumSeq_constomized(filename, label, 0, 0, true, datum);
}

inline bool ReadImageToDatumSeq_constomized(const string& filename, vector<int>& label,
    const std::string & encoding, DatumSeq* datum) {
  return ReadImageToDatumSeq_constomized(filename, label, 0, 0, true, encoding, datum);
}

}  // namespace caffe

#endif   // CAFFE_UTIL_IO_H_
