#ifndef _UTIL_H_
#define _UTIL_H_


#include <cstdio>
#include <string>
#include <list>


#define PROPERTY_RE(type, prop, member) \
  void prop(type const &val) { member(val); } \
  type const & prop() const { return member(); }

#define PROPERTY_IMPL(type, prop, member) \
  void prop(type const &val) { member= val; } \
  type const & prop() const { return member; }

#define PROPERTY(type, prop) PROPERTY_IMPL(type, prop, _##prop)

#define PROPERTY_I_RE(type, prop, member) \
  void prop(type const &val) { member(val); } \
  type & prop() const { return member(); }

#define PROPERTY_I_IMPL(type, prop, member) \
  void prop(type const &val) { member= val; } \
  type & prop() { return member; }

#define PROPERTY_I(type, prop) PROPERTY_I_IMPL(type, prop, _##prop)


template <typename T, typename R= void, typename V= T*>
class Auto_handler
{
public:
  typedef R (*Close_handler_cb)(V);
  Auto_handler(Close_handler_cb close_handler_cb) : _handler(NULL), _close_handler_cb(close_handler_cb) {}
  ~Auto_handler() { reset(NULL); }
  Auto_handler& operator=(T *handler) { reset(handler); return *this; }
  operator T *() { return _handler; }
  void reset(T *new_handler)
  {
    if (_handler)
    {
      if (_close_handler_cb)
        _close_handler_cb(_handler);
    }
    _handler= new_handler;
  }
private:
  Close_handler_cb _close_handler_cb;
  Auto_handler(const Auto_handler &handler) : _handler(NULL) {} // no effect: that would allow to share the same handler
  Auto_handler& operator=(const Auto_handler &handler) { return *this; } // no effect: that would allow to share the same handler
  T *_handler;
};


class File_dir;
class File;
typedef std::list<File_dir> File_dir_list;
typedef std::list<File> File_list;


class File
{
public:
  static bool copy(const std::string &src_filepath, const std::string &dest_filepath);
  static bool rename(const std::string &src_filepath, const std::string &dest_filepath);

  File();
  File(const std::string &filepath);
  ~File();
  bool valid() { return !_path.empty(); }
  bool exists();
  std::string ext() const;
  File_dir parent_dir() const;
  void ensure_parent_dir_exists();
  bool open(const char *flags= "w");
  void close();
  bool remove();
  const std::string name() const;
  FILE * stream () { return _stream; }

  const std::string & path() const { return _path; }
  void path(const std::string &path);
  std::string quoted_path();
  operator const std::string &() const;

private:
  File & operator=(File &) { return *this; }
  std::string _path;
  FILE *_stream;
};


class File_dir
{
public:
  File_dir() {}
  File_dir(const std::string &dirpath) { path(dirpath); }

  const std::string & path() const { return _path; }
  void path(const std::string &path);
  std::string quoted_path();
  operator const std::string &() const;

  bool valid() { return !_path.empty(); }
  void ensure_exists();
  bool get_file_list(File_list &file_list);
  bool make();
  bool make_with_parents();
  bool remove(bool force);
  bool exists();
  File_dir parent_dir() const;

private:
  static int remove(const char *dirpath, bool force);
  std::string _path;
};


#endif // _UTIL_H_
