/* 
 * Copyright (c) 2005, 2014, Oracle and/or its affiliates. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published by the Free Software Foundation; version 2 of the
 * License.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301  USA
 */

#include "base/common.h"

#ifndef _WIN32
#include <errno.h>
#include <sys/file.h>
#endif

#include "base/file_functions.h"
#include "base/string_utilities.h"

#include <glib/gstdio.h>

using namespace base;

//--------------------------------------------------------------------------------------------------

/**
 * @brief Wrapper around fopen that expects a filename in UTF-8 encoding
 * @param filename name of file to open
 * @param mode second argument of fopen
 * @return If successful, base_fopen returns opened FILE*.
 *           Otherwise, it returns NULL.
 */
FILE* base_fopen(const char *filename, const char *mode)
{
#ifdef _WIN32
  std::wstring wmode;
  while (*mode != '\0')
    wmode += *mode++;
  wmode += L"b"; // Always open in binary mode.
  return _wfsopen(string_to_wstring(filename).c_str(), wmode.c_str(), _SH_DENYWR);

#else
  
  FILE *file;
  char * local_filename;

  if (! (local_filename = g_filename_from_utf8(filename, -1, NULL, NULL, NULL)))
    return NULL;

  file = fopen(local_filename, mode);

  g_free(local_filename);

  return file;
#endif
}

//--------------------------------------------------------------------------------------------------

/**
 *	Similar to base_fopen but returns a file descriptor instead. The file is always opened in binary
 *	mode (only matters on Windows).
 *	Also here, the filename must be UTF-8 encoded.
 */
int base_open(const std::string &filename, int open_flag, int permissions)
{
  int fd;

#ifdef _WIN32
  int result = _wsopen_s(&fd, string_to_wstring(filename).c_str(), open_flag | O_BINARY, _SH_DENYWR, permissions);
  if (result != 0)
    return -1;
#else
  char * local_filename = g_filename_from_utf8(filename.c_str(), -1, NULL, NULL, NULL);
  if (local_filename == NULL)
    return -1;

  fd = open(local_filename, open_flag, permissions);
  g_free(local_filename);

#endif

  return fd;
}

//--------------------------------------------------------------------------------------------------

int base_remove(const std::string &filename)
{
#ifdef _WIN32
  return _wremove(string_to_wstring(filename).c_str());
#else
  char * local_filename;
  if (! (local_filename = g_filename_from_utf8(filename.c_str(), -1, NULL, NULL, NULL)))
    return -1;
  int res= remove(local_filename);
  g_free(local_filename);

  return res;
#endif
}

//--------------------------------------------------------------------------------------------------

int base_rename(const char *oldname, const char *newname)
{
#ifdef _WIN32

  int result;
  int required;
  WCHAR* converted_old;
  WCHAR* converted_new;

  required= MultiByteToWideChar(CP_UTF8, 0, oldname, -1, NULL, 0);
  if (required == 0)
    return -1;

  converted_old= g_new0(WCHAR, required);
  MultiByteToWideChar(CP_UTF8, 0, oldname, -1, converted_old, required);

  required= MultiByteToWideChar(CP_UTF8, 0, newname, -1, NULL, 0);
  if (required == 0)
  {
    g_free(converted_old);
    return -1;
  }

  converted_new= g_new0(WCHAR, required);
  MultiByteToWideChar(CP_UTF8, 0, newname, -1, converted_new, required);

  result= _wrename(converted_old, converted_new);

  g_free(converted_old);
  g_free(converted_new);

  return result;

#else
  
  char * local_oldname;
  char * local_newname;

  if (  ! (local_oldname= g_filename_from_utf8(oldname,-1,NULL,NULL,NULL)) 
     || ! (local_newname= g_filename_from_utf8(newname,-1,NULL,NULL,NULL))
     )
    return EINVAL;

  const int file= rename(local_oldname, local_newname);

  g_free(local_oldname);
  g_free(local_newname);

  return file;
#endif
}

//--------------------------------------------------------------------------------------------------

#ifdef _WIN32
int base_stat(const char *filename, struct _stat *stbuf)
{
  // Convert filename from UTF-8 to UTF-16.
  int required;
  WCHAR* converted;
  int result;

  required= MultiByteToWideChar(CP_UTF8, 0, filename, -1, NULL, 0);
  if (required == 0)
    return -1;

  // Required contains the length for the result string including the terminating 0.
  converted= g_new0(WCHAR, required);
  MultiByteToWideChar(CP_UTF8, 0, filename, -1, converted, required);

  result= _wstat(converted, stbuf);
  g_free(converted);

  return result;
}
#else
int base_stat(const char *filename, struct stat *stbuf)
{
  return g_stat(filename, stbuf);
}
#endif

//--------------------------------------------------------------------------------------------------

int base_rmdir_recursively(const char *path)
{
  int res= 0;
  GError *error= NULL;
  GDir* dir;
  const char *dir_entry;
  gchar *entry_path;
  
  dir= g_dir_open(path, 0, &error);
  if (!dir && error)
    return error->code;
  
  while ((dir_entry= g_dir_read_name(dir)))
  {
    entry_path= g_build_filename(path, dir_entry, NULL);
    if (g_file_test(entry_path, G_FILE_TEST_IS_DIR))
      (void) base_rmdir_recursively(entry_path);
    else
      (void) ::g_remove(entry_path);
    g_free(entry_path);
  }
  
  (void) g_rmdir(path);
  
  g_dir_close(dir);
  return res;
}

//--------------------------------------------------------------------------------------------------

/**
 * Returns the size of the specified file (if it exists and can be accessed, otherwise 0).
 */
long base_get_file_size(const char *filename)
{
  long result = 0;

#ifdef _WIN32
  struct _stat file_stat;
  if (base_stat(filename, &file_stat) == 0)
    result = file_stat.st_size;
#else
  struct stat file_stat;
  if (base_stat(filename, &file_stat) == 0)
    result = file_stat.st_size;
#endif

  return result;
}

//--------------------------------------------------------------------------------------------------

