/*
 *  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 "webrtc/system_wrappers/source/file_impl.h"

#include <assert.h>

#ifdef _WIN32
#include <Windows.h>
#else
#include <stdarg.h>
#include <string.h>
#endif

#include "webrtc/base/checks.h"
#include "webrtc/system_wrappers/include/rw_lock_wrapper.h"

namespace webrtc {

FileWrapper* FileWrapper::Create() {
  return new FileWrapperImpl();
}

FileWrapperImpl::FileWrapperImpl()
    : rw_lock_(RWLockWrapper::CreateRWLock()),
      id_(NULL),
      managed_file_handle_(true),
      open_(false),
      looping_(false),
      read_only_(false),
      max_size_in_bytes_(0),
      size_in_bytes_(0) {
  memset(file_name_utf8_, 0, kMaxFileNameSize);
}

FileWrapperImpl::~FileWrapperImpl() {
  if (id_ != NULL && managed_file_handle_) {
    fclose(id_);
  }
}

int FileWrapperImpl::CloseFile() {
  WriteLockScoped write(*rw_lock_);
  return CloseFileImpl();
}

int FileWrapperImpl::Rewind() {
  WriteLockScoped write(*rw_lock_);
  if (looping_ || !read_only_) {
    if (id_ != NULL) {
      size_in_bytes_ = 0;
      return fseek(id_, 0, SEEK_SET);
    }
  }
  return -1;
}

int FileWrapperImpl::SetMaxFileSize(size_t bytes) {
  WriteLockScoped write(*rw_lock_);
  max_size_in_bytes_ = bytes;
  return 0;
}

int FileWrapperImpl::Flush() {
  WriteLockScoped write(*rw_lock_);
  return FlushImpl();
}

int FileWrapperImpl::FileName(char* file_name_utf8, size_t size) const {
  ReadLockScoped read(*rw_lock_);
  size_t length = strlen(file_name_utf8_);
  if (length > kMaxFileNameSize) {
    assert(false);
    return -1;
  }
  if (length < 1) {
    return -1;
  }

  // Make sure to NULL terminate
  if (size < length) {
    length = size - 1;
  }
  memcpy(file_name_utf8, file_name_utf8_, length);
  file_name_utf8[length] = 0;
  return 0;
}

bool FileWrapperImpl::Open() const {
  ReadLockScoped read(*rw_lock_);
  return open_;
}

int FileWrapperImpl::OpenFile(const char* file_name_utf8, bool read_only,
                              bool loop, bool text) {
  WriteLockScoped write(*rw_lock_);
  if (id_ != NULL && !managed_file_handle_)
    return -1;
  size_t length = strlen(file_name_utf8);
  if (length > kMaxFileNameSize - 1) {
    return -1;
  }

  read_only_ = read_only;

  FILE* tmp_id = NULL;
#if defined _WIN32
  wchar_t wide_file_name[kMaxFileNameSize];
  wide_file_name[0] = 0;

  MultiByteToWideChar(CP_UTF8,
                      0,  // UTF8 flag
                      file_name_utf8,
                      -1,  // Null terminated string
                      wide_file_name,
                      kMaxFileNameSize);
  if (text) {
    if (read_only) {
      tmp_id = _wfopen(wide_file_name, L"rt");
    } else {
      tmp_id = _wfopen(wide_file_name, L"wt");
    }
  } else {
    if (read_only) {
      tmp_id = _wfopen(wide_file_name, L"rb");
    } else {
      tmp_id = _wfopen(wide_file_name, L"wb");
    }
  }
#else
  if (text) {
    if (read_only) {
      tmp_id = fopen(file_name_utf8, "rt");
    } else {
      tmp_id = fopen(file_name_utf8, "wt");
    }
  } else {
    if (read_only) {
      tmp_id = fopen(file_name_utf8, "rb");
    } else {
      tmp_id = fopen(file_name_utf8, "wb");
    }
  }
#endif

  if (tmp_id != NULL) {
    // +1 comes from copying the NULL termination character.
    memcpy(file_name_utf8_, file_name_utf8, length + 1);
    if (id_ != NULL) {
      fclose(id_);
    }
    id_ = tmp_id;
    managed_file_handle_ = true;
    looping_ = loop;
    open_ = true;
    return 0;
  }
  return -1;
}

int FileWrapperImpl::OpenFromFileHandle(FILE* handle,
                                        bool manage_file,
                                        bool read_only,
                                        bool loop) {
  WriteLockScoped write(*rw_lock_);
  if (!handle)
    return -1;

  if (id_ != NULL) {
    if (managed_file_handle_)
      fclose(id_);
    else
      return -1;
  }

  id_ = handle;
  managed_file_handle_ = manage_file;
  read_only_ = read_only;
  looping_ = loop;
  open_ = true;
  return 0;
}

int FileWrapperImpl::Read(void* buf, size_t length) {
  WriteLockScoped write(*rw_lock_);
  if (id_ == NULL)
    return -1;

  size_t bytes_read = fread(buf, 1, length, id_);
  if (bytes_read != length && !looping_) {
    CloseFileImpl();
  }
  return static_cast<int>(bytes_read);
}

int FileWrapperImpl::WriteText(const char* format, ...) {
  WriteLockScoped write(*rw_lock_);
  if (format == NULL)
    return -1;

  if (read_only_)
    return -1;

  if (id_ == NULL)
    return -1;

  va_list args;
  va_start(args, format);
  int num_chars = vfprintf(id_, format, args);
  va_end(args);

  if (num_chars >= 0) {
    return num_chars;
  } else {
    CloseFileImpl();
    return -1;
  }
}

bool FileWrapperImpl::Write(const void* buf, size_t length) {
  WriteLockScoped write(*rw_lock_);
  if (buf == NULL)
    return false;

  if (read_only_)
    return false;

  if (id_ == NULL)
    return false;

  // Check if it's time to stop writing.
  if (max_size_in_bytes_ > 0 &&
      (size_in_bytes_ + length) > max_size_in_bytes_) {
    FlushImpl();
    return false;
  }

  size_t num_bytes = fwrite(buf, 1, length, id_);
  if (num_bytes > 0) {
    size_in_bytes_ += num_bytes;
    return true;
  }

  CloseFileImpl();
  return false;
}

int FileWrapperImpl::CloseFileImpl() {
  if (id_ != NULL) {
    if (managed_file_handle_)
      fclose(id_);
    id_ = NULL;
  }
  memset(file_name_utf8_, 0, kMaxFileNameSize);
  open_ = false;
  return 0;
}

int FileWrapperImpl::FlushImpl() {
  if (id_ != NULL) {
    return fflush(id_);
  }
  return -1;
}

int FileWrapper::Rewind() {
  RTC_DCHECK(false);
  return -1;
}

}  // namespace webrtc
