#include "stdafx.h"
#include "util.h"
#include "glib.h"
#include "glib/gstdio.h"
#include "myx_shared_util_functions.h"
#include <list>

#ifdef _WIN32
#include <windows.h>
#endif

const char *PATH_DELIMITERS= "/\\";


void adjust_path(std::string &path)
{
  std::string::size_type boff= path.find_first_of(PATH_DELIMITERS);
  while (std::string::npos != boff)
  {
    std::string::size_type eoff= path.find_first_not_of(PATH_DELIMITERS, boff);
    if ((1 != eoff - boff) || ('/' != path[boff]))
      path= path.replace(boff, (eoff - boff), "/");
    boff= path.find_first_of(PATH_DELIMITERS, boff + 1);
  }
}


File::File()
:
_stream(NULL)
{
}


File::File(const std::string &filepath)
:
_stream(NULL)
{
  path(filepath); 
}


File::~File()
{
  close();
}


bool File::exists()
{
  return g_file_test(_path.c_str(), G_FILE_TEST_EXISTS) != 0;
}


std::string File::ext() const
{
  std::string ext;
  std::string::const_reverse_iterator i1=
    std::find_first_of(_path.rbegin(), _path.rend(),
      PATH_DELIMITERS, &PATH_DELIMITERS[strlen(PATH_DELIMITERS)]);
  std::string::const_reverse_iterator i2= std::find(_path.rbegin(), i1, '.');
  if (i1 != i2)
    ext.assign(i2.base(), _path.end());
  return ext;
}


File_dir File::parent_dir() const
{
  std::string::size_type pos= _path.find_last_of(PATH_DELIMITERS);
  return File_dir(_path.substr(0, pos));
}


void File::ensure_parent_dir_exists()
{
  parent_dir().ensure_exists();
}


void File::path(const std::string &path)
{
  _path= path;
  adjust_path(_path);
}


std::string File::quoted_path()
{
  return "\"" + path() + "\"";
}


const std::string File::name() const
{
  std::string::size_type offset= _path.find_last_of(PATH_DELIMITERS);
  return _path.substr(offset + 1);
}


bool File::open(const char *flags)
{
  if (!_stream)
    _stream= myx_fopen(_path.c_str(), flags);
  return (NULL != _stream);
}


void File::close()
{
  if (_stream)
  {
    fclose(_stream);
    _stream= NULL;
  }
}


bool File::remove()
{
  return !g_remove(_path.c_str());
}


bool File::rename(const std::string &src_filepath, const std::string &dest_filepath)
{
  return !g_rename(src_filepath.c_str(), dest_filepath.c_str());
}


bool File::copy(const std::string &src_filepath, const std::string &dest_filepath)
{
#ifdef _WIN32
  const int cch_buf= MAX_PATH;
  WCHAR src_path[MAX_PATH];
  WCHAR dest_path[MAX_PATH];

  MultiByteToWideChar(CP_UTF8, 0, src_filepath.c_str(), -1, src_path, cch_buf);
  MultiByteToWideChar(CP_UTF8, 0, dest_filepath.c_str(), -1, dest_path, cch_buf);

  if (!CopyFileW(src_path, dest_path, 0))
    return false;
  return true;
#else
  // copy contents of original archive to new
  char buffer[1024*4];
  size_t c;
  FILE *in, *out;
  in= myx_fopen(src_filepath.c_str(), "r");
  if (!in) 
    return false;

  out= myx_fopen(dest_filepath.c_str(), "w+");
  if (!out) 
  { 
    fclose(in); 
    return false;
  }

  while ((c= fread(buffer, 1, sizeof(buffer), in)) > 0 && c != (size_t)-1)
  {
    if (fwrite(buffer, 1, c, out) < c)
    {
      int e= errno;
      fclose(in);
      fclose(out);
      return false;
    }
  }
  fclose(in);
  fclose(out);

  return true;
#endif
}


File::operator const std::string &() const
{
  return path();
}


void File_dir::path(const std::string &path)
{
  _path= path;
  adjust_path(_path);
}


std::string File_dir::quoted_path()
{
  return "\"" + path() + "\"";
}


File_dir File_dir::parent_dir() const
{
  std::string path= _path;
  std::string::reverse_iterator i= path.rbegin();
  if (!path.empty() && ('/' == *i))
    *i= '\0';
  std::string::size_type pos= path.find_last_of(PATH_DELIMITERS);
  if (std::string::npos != pos)
    path= path.substr(0, pos);
  else
    path.clear();
  return File_dir(path);
}


bool File_dir::remove(bool force)
{
  return !remove(_path.c_str(), force);
}


int File_dir::remove(const char *dirpath, bool force)
{
  int res= 0;
  GError *error= NULL;
  Auto_handler<GDir> dir(&g_dir_close);
  const gchar *dir_entry;
  Auto_handler<gchar, void, gpointer> entry_path(&g_free);
  
  dir= g_dir_open(dirpath, 0, &error);
  if (!dir)
    return -1; // error->code

  if (force)
  {
    while (dir_entry= g_dir_read_name(dir))
    {
      entry_path= g_build_filename(dirpath, dir_entry, NULL);
      if (g_file_test(entry_path, G_FILE_TEST_IS_DIR))
        res= remove(entry_path, force);
      else
        res= g_remove(entry_path);
      if (res)
        break;
    }
  }

  if (!res)
    res= g_rmdir(dirpath);

  return res;
}


bool File_dir::exists()
{
  GError *error= NULL;
  GDir* dir= g_dir_open(_path.c_str(), 0, &error);
  bool res= (NULL != dir);
  if (!dir)
    g_dir_close(dir);
  return res;
}


bool File_dir::make()
{
  return !g_mkdir(_path.c_str(), 0);
}


bool File_dir::make_with_parents()
{
  return !g_mkdir_with_parents(_path.c_str(), 0);
}


void File_dir::ensure_exists()
{
  if (!exists() && !make_with_parents())
    throw std::runtime_error("Failed to create directory: " + quoted_path() + ".");
}


bool File_dir::get_file_list(File_list &file_list)
{
  GError *error= NULL;
  Auto_handler<GDir> dir(&g_dir_close);
  const gchar *dir_entry;
  Auto_handler<gchar, void, gpointer> entry_path(&g_free);

  dir= g_dir_open(_path.c_str(), 0, &error);
  if (!dir)
    return false;

  while (dir_entry= g_dir_read_name(dir))
  {
    entry_path= g_build_filename(_path.c_str(), dir_entry, NULL);
    if (!g_file_test(entry_path, G_FILE_TEST_IS_DIR))
      file_list.push_back(File((const char *)entry_path));
  }

  return true;
}


File_dir::operator const std::string &() const
{
  return path();
}
