// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief  C++ API io module wrapper
//!\author Eduardo Aguiar
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "io.h"
#include <Python.h>
#include <datetime.h>
#include <mobius/io/file.h>
#include <mobius/io/folder.h>

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io module methods
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyMethodDef io_methods[] =
{
  {NULL, NULL, 0, NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: static prototypes
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void io_reader_tp_dealloc (io_reader_o *);
static PyObject *io_reader_get_is_seekable (io_reader_o *);
static PyObject *io_reader_get_is_rewindable (io_reader_o *);
static PyObject *io_reader_get_size (io_reader_o *);
static PyObject *io_reader_f_read (io_reader_o *, PyObject *);
static PyObject *io_reader_f_tell (io_reader_o *, PyObject *);
static PyObject *io_reader_f_seek (io_reader_o *, PyObject *);
static PyObject *io_reader_f_rewind (io_reader_o *, PyObject *);
static PyObject *io_reader_f_skip (io_reader_o *, PyObject *);

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: getters and setters structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyGetSetDef io_reader_getsetters[] =
{
  {
    (char *) "is_seekable",
    (getter) io_reader_get_is_seekable,
    (setter) 0,
    (char *) "check if reader is seekable", NULL
  },
  {
    (char *) "is_rewindable",
    (getter) io_reader_get_is_rewindable,
    (setter) 0,
    (char *) "check if reader is rewindable", NULL
  },
  {
    (char *) "size",
    (getter) io_reader_get_size,
    (setter) 0,
    (char *) "data size in bytes", NULL
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: methods structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyMethodDef io_reader_methods[] =
{
  {
    (char *) "read",
    (PyCFunction) io_reader_f_read,
    METH_VARARGS,
    "read bytes from reader"
  },
  {
    (char *) "tell",
    (PyCFunction) io_reader_f_tell,
    METH_VARARGS,
    "get current read position"
  },
  {
    (char *) "seek",
    (PyCFunction) io_reader_f_seek,
    METH_VARARGS,
    "set current read position"
  },
  {
    (char *) "rewind",
    (PyCFunction) io_reader_f_rewind,
    METH_VARARGS,
    "set current read position to the beginning of data"
  },
  {
    (char *) "skip",
    (PyCFunction) io_reader_f_skip,
    METH_VARARGS,
    "set read position n bytes ahead"
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: type structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyTypeObject io_reader_t =
{
  PyObject_HEAD_INIT (0)
  0,                                       		// ob_size
  "io.reader",                             		// tp_name
  sizeof (io_reader_o),                    		// tp_basicsize
  0,                                       		// tp_itemsize
  (destructor) io_reader_tp_dealloc,       		// tp_dealloc
  0,                                       		// tp_print
  0,                                       		// tp_getattr
  0,                                       		// tp_setattr
  0,                                       		// tp_compare
  0,                                       		// tp_repr
  0,                                       		// tp_as_number
  0,                                       		// tp_as_sequence
  0,                                       		// tp_as_mapping
  0,                                       		// tp_hash
  0,                                       		// tp_call
  0,                                       		// tp_str
  0,                                       		// tp_getattro
  0,                                       		// tp_setattro
  0,                                       		// tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,		// tp_flags
  "reader for mobius.io.file",             		// tp_doc
  0,                                       		// tp_traverse
  0,                                       		// tp_clear
  0,                                       		// tp_richcompare
  0,                                       		// tp_weaklistoffset
  0,                                       		// tp_iter
  0,                                       		// tp_iternext
  io_reader_methods,                       		// tp_methods
  0,                                       		// tp_members
  io_reader_getsetters,                    		// tp_getset
  0,                                       		// tp_base
  0,                                       		// tp_dict
  0,                                       		// tp_descr_get
  0,                                       		// tp_descr_set
  0,                                       		// tp_dictoffset
  0,                                       		// tp_init
  0,                                       		// tp_alloc
  0                                        		// tp_new
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: prototypes
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void io_writer_tp_dealloc (io_writer_o *);
static PyObject *io_writer_get_is_seekable (io_writer_o *);
static PyObject *io_writer_get_is_rewindable (io_writer_o *);
static PyObject *io_writer_f_write (io_writer_o *, PyObject *);
static PyObject *io_writer_f_tell (io_writer_o *, PyObject *);
static PyObject *io_writer_f_seek (io_writer_o *, PyObject *);
static PyObject *io_writer_f_rewind (io_writer_o *, PyObject *);
static PyObject *io_writer_f_skip (io_writer_o *, PyObject *);
static PyObject *io_writer_f_flush (io_writer_o *, PyObject *);

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: getters and setters structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyGetSetDef io_writer_getsetters[] =
{
  {
    (char *) "is_seekable",
    (getter) io_writer_get_is_seekable,
    (setter) 0,
    (char *) "check if writer is seekable", NULL
  },
  {
    (char *) "is_rewindable",
    (getter) io_writer_get_is_rewindable,
    (setter) 0,
    (char *) "check if writer is rewindable", NULL
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: methods structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyMethodDef io_writer_methods[] =
{
  {
    (char *) "write",
    (PyCFunction) io_writer_f_write,
    METH_VARARGS,
    "writes bytes to writer"
  },
  {
    (char *) "tell",
    (PyCFunction) io_writer_f_tell,
    METH_VARARGS,
    "get current write position"
  },
  {
    (char *) "seek",
    (PyCFunction) io_writer_f_seek,
    METH_VARARGS,
    "set current write position"
  },
  {
    (char *) "rewind",
    (PyCFunction) io_writer_f_rewind,
    METH_VARARGS,
    "set current write position to the beginning of data"
  },
  {
    (char *) "skip",
    (PyCFunction) io_writer_f_skip,
    METH_VARARGS,
    "set write position n bytes ahead"
  },
  {
    (char *) "flush",
    (PyCFunction) io_writer_f_flush,
    METH_VARARGS,
    "write down data"
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: type structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyTypeObject io_writer_t =
{
  PyObject_HEAD_INIT (0)
  0,                                       		// ob_size
  "io.writer",                             		// tp_name
  sizeof (io_writer_o),                    		// tp_basicsize
  0,                                       		// tp_itemsize
  (destructor) io_writer_tp_dealloc,       		// tp_dealloc
  0,                                       		// tp_print
  0,                                       		// tp_getattr
  0,                                       		// tp_setattr
  0,                                       		// tp_compare
  0,                                       		// tp_repr
  0,                                       		// tp_as_number
  0,                                       		// tp_as_sequence
  0,                                       		// tp_as_mapping
  0,                                       		// tp_hash
  0,                                       		// tp_call
  0,                                       		// tp_str
  0,                                       		// tp_getattro
  0,                                       		// tp_setattro
  0,                                       		// tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,		// tp_flags
  "writer for mobius.io.file",             		// tp_doc
  0,                                       		// tp_traverse
  0,                                       		// tp_clear
  0,                                       		// tp_richcompare
  0,                                       		// tp_weaklistoffset
  0,                                       		// tp_iter
  0,                                       		// tp_iternext
  io_writer_methods,                       		// tp_methods
  0,                                       		// tp_members
  io_writer_getsetters,                    		// tp_getset
  0,                                       		// tp_base
  0,                                       		// tp_dict
  0,                                       		// tp_descr_get
  0,                                       		// tp_descr_set
  0,                                       		// tp_dictoffset
  0,                                       		// tp_init
  0,                                       		// tp_alloc
  0                                        		// tp_new
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: data structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
typedef struct
{
  PyObject_HEAD
  mobius::io::file *obj;
} io_file_o;

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: prototypes
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void io_file_tp_dealloc (io_file_o *);
static PyObject *io_file_tp_new (PyTypeObject *, PyObject *, PyObject *);
static PyObject *io_file_get_is_valid (io_file_o *);
static PyObject *io_file_get_url (io_file_o *);
static PyObject *io_file_get_is_file (io_file_o *);
static PyObject *io_file_get_is_folder (io_file_o *);
static PyObject *io_file_get_exists (io_file_o *);
static PyObject *io_file_get_size (io_file_o *);
static PyObject *io_file_get_user_id (io_file_o *);
static PyObject *io_file_get_user_name (io_file_o *);
static PyObject *io_file_get_group_id (io_file_o *);
static PyObject *io_file_get_group_name (io_file_o *);
static PyObject *io_file_get_permissions (io_file_o *);
static PyObject *io_file_get_last_access_time (io_file_o *);
static PyObject *io_file_get_last_modification_time (io_file_o *);
static PyObject *io_file_get_last_metadata_time (io_file_o *);
static PyObject *io_file_f_new_reader (io_file_o *, PyObject *);
static PyObject *io_file_f_new_writer (io_file_o *, PyObject *);

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: getters and setters structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyGetSetDef io_file_getsetters[] =
{
  {
    (char *) "is_valid",
    (getter) io_file_get_is_valid,
    (setter) 0,
    (char *) "check if file is valid", NULL
  },
  {
    (char *) "url",
    (getter) io_file_get_url,
    (setter) 0,
    (char *) "URL as string", NULL
  },
  {
    (char *) "is_file",
    (getter) io_file_get_is_file,
    (setter) 0,
    (char *) "check if entry is a file", NULL
  },
  {
    (char *) "is_folder",
    (getter) io_file_get_is_folder,
    (setter) 0,
    (char *) "check if entry is a folder", NULL
  },
  {
    (char *) "exists",
    (getter) io_file_get_exists,
    (setter) 0,
    (char *) "check if file exists", NULL
  },
  {
    (char *) "size",
    (getter) io_file_get_size,
    (setter) 0,
    (char *) "file size in bytes", NULL
  },
  {
    (char *) "user_id",
    (getter) io_file_get_user_id,
    (setter) 0,
    (char *) "owner's user ID", NULL
  },
  {
    (char *) "user_name",
    (getter) io_file_get_user_name,
    (setter) 0,
    (char *) "owner's user name", NULL
  },
  {
    (char *) "group_id",
    (getter) io_file_get_group_id,
    (setter) 0,
    (char *) "group ID", NULL
  },
  {
    (char *) "group_name",
    (getter) io_file_get_group_name,
    (setter) 0,
    (char *) "group name", NULL
  },
  {
    (char *) "permissions",
    (getter) io_file_get_permissions,
    (setter) 0,
    (char *) "access permission mask", NULL
  },
  {
    (char *) "last_access_time",
    (getter) io_file_get_last_access_time,
    (setter) 0,
    (char *) "last access time", NULL
  },
  {
    (char *) "last_modification_time",
    (getter) io_file_get_last_modification_time,
    (setter) 0,
    (char *) "last data modification time", NULL
  },
  {
    (char *) "last_metadata_time",
    (getter) io_file_get_last_metadata_time,
    (setter) 0,
    (char *) "last metadata modification time", NULL
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: methods structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyMethodDef io_file_methods[] =
{
  {
    (char *) "new_reader",
    (PyCFunction) io_file_f_new_reader,
    METH_VARARGS,
    "create new reader"
  },
  {
    (char *) "new_writer",
    (PyCFunction) io_file_f_new_writer,
    METH_VARARGS,
    "create new writer"
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: type structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyTypeObject io_file_t =
{
  PyObject_HEAD_INIT (0)
  0,                                       		// ob_size
  "io.file",                               		// tp_name
  sizeof (io_file_o),                      		// tp_basicsize
  0,                                       		// tp_itemsize
  (destructor) io_file_tp_dealloc,         		// tp_dealloc
  0,                                       		// tp_print
  0,                                       		// tp_getattr
  0,                                       		// tp_setattr
  0,                                       		// tp_compare
  0,                                       		// tp_repr
  0,                                       		// tp_as_number
  0,                                       		// tp_as_sequence
  0,                                       		// tp_as_mapping
  0,                                       		// tp_hash
  0,                                       		// tp_call
  0,                                       		// tp_str
  0,                                       		// tp_getattro
  0,                                       		// tp_setattro
  0,                                       		// tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,		// tp_flags
  "file class with support for URL's",     		// tp_doc
  0,                                       		// tp_traverse
  0,                                       		// tp_clear
  0,                                       		// tp_richcompare
  0,                                       		// tp_weaklistoffset
  0,                                       		// tp_iter
  0,                                       		// tp_iternext
  io_file_methods,                         		// tp_methods
  0,                                       		// tp_members
  io_file_getsetters,                      		// tp_getset
  0,                                       		// tp_base
  0,                                       		// tp_dict
  0,                                       		// tp_descr_get
  0,                                       		// tp_descr_set
  0,                                       		// tp_dictoffset
  0,                                       		// tp_init
  0,                                       		// tp_alloc
  io_file_tp_new                           		// tp_new
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: data structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
typedef struct
{
  PyObject_HEAD
  mobius::io::folder *obj;
} io_folder_o;

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: prototypes
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void io_folder_tp_dealloc (io_folder_o *);
static PyObject *io_folder_tp_new (PyTypeObject *, PyObject *, PyObject *);
static PyObject *io_folder_get_is_valid (io_folder_o *);
static PyObject *io_folder_get_url (io_folder_o *);
static PyObject *io_folder_get_is_file (io_folder_o *);
static PyObject *io_folder_get_is_folder (io_folder_o *);
static PyObject *io_folder_get_exists (io_folder_o *);
static PyObject *io_folder_get_size (io_folder_o *);
static PyObject *io_folder_get_user_id (io_folder_o *);
static PyObject *io_folder_get_user_name (io_folder_o *);
static PyObject *io_folder_get_group_id (io_folder_o *);
static PyObject *io_folder_get_group_name (io_folder_o *);
static PyObject *io_folder_get_permissions (io_folder_o *);
static PyObject *io_folder_get_last_access_time (io_folder_o *);
static PyObject *io_folder_get_last_modification_time (io_folder_o *);
static PyObject *io_folder_get_last_metadata_time (io_folder_o *);
static PyObject *io_folder_get_children (io_folder_o *);

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: getters and setters structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyGetSetDef io_folder_getsetters[] =
{
  {
    (char *) "is_valid",
    (getter) io_folder_get_is_valid,
    (setter) 0,
    (char *) "check if folder is valid", NULL
  },
  {
    (char *) "url",
    (getter) io_folder_get_url,
    (setter) 0,
    (char *) "URL as string", NULL
  },
  {
    (char *) "is_file",
    (getter) io_folder_get_is_file,
    (setter) 0,
    (char *) "check if entry is a file", NULL
  },
  {
    (char *) "is_folder",
    (getter) io_folder_get_is_folder,
    (setter) 0,
    (char *) "check if entry is a folder", NULL
  },
  {
    (char *) "exists",
    (getter) io_folder_get_exists,
    (setter) 0,
    (char *) "check if folder exists", NULL
  },
  {
    (char *) "size",
    (getter) io_folder_get_size,
    (setter) 0,
    (char *) "folder size in bytes", NULL
  },
  {
    (char *) "user_id",
    (getter) io_folder_get_user_id,
    (setter) 0,
    (char *) "owner's user ID", NULL
  },
  {
    (char *) "user_name",
    (getter) io_folder_get_user_name,
    (setter) 0,
    (char *) "owner's user name", NULL
  },
  {
    (char *) "group_id",
    (getter) io_folder_get_group_id,
    (setter) 0,
    (char *) "group ID", NULL
  },
  {
    (char *) "group_name",
    (getter) io_folder_get_group_name,
    (setter) 0,
    (char *) "group name", NULL
  },
  {
    (char *) "permissions",
    (getter) io_folder_get_permissions,
    (setter) 0,
    (char *) "access permission mask", NULL
  },
  {
    (char *) "last_access_time",
    (getter) io_folder_get_last_access_time,
    (setter) 0,
    (char *) "last access time", NULL
  },
  {
    (char *) "last_modification_time",
    (getter) io_folder_get_last_modification_time,
    (setter) 0,
    (char *) "last data modification time", NULL
  },
  {
    (char *) "last_metadata_time",
    (getter) io_folder_get_last_metadata_time,
    (setter) 0,
    (char *) "last metadata modification time", NULL
  },
  {
    (char *) "children",
    (getter) io_folder_get_children,
    (setter) 0,
    (char *) "folder entries", NULL
  },
  {NULL} // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: type structure
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyTypeObject io_folder_t =
{
  PyObject_HEAD_INIT (0)
  0,                                       		// ob_size
  "io.folder",                             		// tp_name
  sizeof (io_folder_o),                    		// tp_basicsize
  0,                                       		// tp_itemsize
  (destructor) io_folder_tp_dealloc,       		// tp_dealloc
  0,                                       		// tp_print
  0,                                       		// tp_getattr
  0,                                       		// tp_setattr
  0,                                       		// tp_compare
  0,                                       		// tp_repr
  0,                                       		// tp_as_number
  0,                                       		// tp_as_sequence
  0,                                       		// tp_as_mapping
  0,                                       		// tp_hash
  0,                                       		// tp_call
  0,                                       		// tp_str
  0,                                       		// tp_getattro
  0,                                       		// tp_setattro
  0,                                       		// tp_as_buffer
  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,		// tp_flags
  "folder class with support for URL's",   		// tp_doc
  0,                                       		// tp_traverse
  0,                                       		// tp_clear
  0,                                       		// tp_richcompare
  0,                                       		// tp_weaklistoffset
  0,                                       		// tp_iter
  0,                                       		// tp_iternext
  0,                                       		// tp_methods
  0,                                       		// tp_members
  io_folder_getsetters,                    		// tp_getset
  0,                                       		// tp_base
  0,                                       		// tp_dict
  0,                                       		// tp_descr_get
  0,                                       		// tp_descr_set
  0,                                       		// tp_dictoffset
  0,                                       		// tp_init
  0,                                       		// tp_alloc
  io_folder_tp_new                         		// tp_new
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: tp_alloc
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
io_reader_o *
io_reader_tp_alloc ()
{
  return (io_reader_o *) io_reader_t.tp_alloc (&io_reader_t, 0);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: tp_dealloc
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
io_reader_tp_dealloc (io_reader_o *self)
{
  delete self->obj;
  self->ob_type->tp_free ((PyObject*) self);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: is_seekable getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_reader_get_is_seekable (io_reader_o *self)
{
  return PyBool_FromLong (self->obj->is_seekable ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: is_rewindable getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_reader_get_is_rewindable (io_reader_o *self)
{
  return PyBool_FromLong (self->obj->is_rewindable ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: size getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_reader_get_size (io_reader_o *self)
{
  return PyInt_FromLong (self->obj->get_size ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: read
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_reader_f_read (io_reader_o *self, PyObject *args)
{
  // parse input args
  std::uint64_t arg_size;

  if (!PyArg_ParseTuple (args, "K", &arg_size))
    return NULL;

  // build and return value
  try
    {
      auto ret_value = self->obj->read (arg_size);

      return PyString_FromStringAndSize ((const char *) ret_value.data (), ret_value.size ());
    }
  catch (const std::runtime_error& e)
    {
      PyErr_SetString (PyExc_IOError, e.what ());
      return NULL;
    }
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: tell
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_reader_f_tell (io_reader_o *self, PyObject *args)
{
  return PyInt_FromLong (self->obj->tell ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: seek
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_reader_f_seek (io_reader_o *self, PyObject *args)
{
  // parse input args
  std::int64_t arg_offset;
  int arg_whence = 0;

  if (!PyArg_ParseTuple (args, "L|i", &arg_offset, &arg_whence))
    return NULL;

  // execute C++ function
  mobius::io::reader::whence_type w;

  if (arg_whence == 0)
    w = mobius::io::reader::whence_type::beginning;

  else if (arg_whence == 1)
    w = mobius::io::reader::whence_type::current;

  else if (arg_whence == 2)
    w = mobius::io::reader::whence_type::end;

  self->obj->seek (arg_offset, w);

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: rewind
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_reader_f_rewind (io_reader_o *self, PyObject *args)
{
  self->obj->rewind ();

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.reader: skip
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_reader_f_skip (io_reader_o *self, PyObject *args)
{
  // parse input args
  std::int64_t arg_size;

  if (!PyArg_ParseTuple (args, "L", &arg_size))
    return NULL;

  // execute C++ function
  self->obj->skip (arg_size);

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: tp_alloc
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
io_writer_o *
io_writer_tp_alloc ()
{
  return (io_writer_o *) io_writer_t.tp_alloc (&io_writer_t, 0);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: tp_dealloc
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
io_writer_tp_dealloc (io_writer_o *self)
{
  delete self->obj;
  self->ob_type->tp_free ((PyObject*) self);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: is_seekable getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_writer_get_is_seekable (io_writer_o *self)
{
  return PyBool_FromLong (self->obj->is_seekable ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: is_rewindable getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_writer_get_is_rewindable (io_writer_o *self)
{
  return PyBool_FromLong (self->obj->is_rewindable ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: write
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_writer_f_write (io_writer_o *self, PyObject *args)
{
  // parse input args
  const std::uint8_t *arg_data_buffer;
  int arg_data_size;

  if (!PyArg_ParseTuple (args, "s#", &arg_data_buffer, &arg_data_size))
    return NULL;

  // execute C++ function
  try
    {
      self->obj->write (mobius::bytearray (arg_data_buffer, arg_data_size));
    }
  catch (const std::runtime_error& e)
    {
      PyErr_SetString (PyExc_IOError, e.what ());
      return NULL;
    }

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: tell
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_writer_f_tell (io_writer_o *self, PyObject *args)
{
  return PyInt_FromLong (self->obj->tell ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: seek
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_writer_f_seek (io_writer_o *self, PyObject *args)
{
  // parse input args
  std::int64_t arg_offset;
  int arg_whence = 0;

  if (!PyArg_ParseTuple (args, "L|i", &arg_offset, &arg_whence))
    return NULL;

  // execute C++ function
  mobius::io::writer::whence_type w;

  if (arg_whence == 0)
    w = mobius::io::writer::whence_type::beginning;

  else if (arg_whence == 1)
    w = mobius::io::writer::whence_type::current;

  else if (arg_whence == 2)
    w = mobius::io::writer::whence_type::end;

  self->obj->seek (arg_offset, w);

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: rewind
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_writer_f_rewind (io_writer_o *self, PyObject *args)
{
  self->obj->rewind ();

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: skip
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_writer_f_skip (io_writer_o *self, PyObject *args)
{
  // parse input args
  std::int64_t arg_size;

  if (!PyArg_ParseTuple (args, "L", &arg_size))
    return NULL;

  // execute C++ function
  self->obj->skip (arg_size);

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.writer: flush
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_writer_f_flush (io_writer_o *self, PyObject *args)
{
  self->obj->flush ();

  Py_INCREF (Py_None);
  return Py_None;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: tp_new
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_tp_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
{
  const char *arg_uri = nullptr;

  if (!PyArg_ParseTuple (args, "|s", &arg_uri))
    return NULL;

  io_file_o *self = (io_file_o *) type->tp_alloc (type, 0);
  if (self != NULL)
    {
      try
        {
          if (arg_uri)
            self->obj = new mobius::io::file (arg_uri);
          else
            self->obj = new mobius::io::file ();
        }
      catch (const std::runtime_error& e)
        {
          Py_DECREF (self);
          PyErr_SetString (PyExc_IOError, e.what ());
          self = NULL;
        }
    }

  return (PyObject *) self;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: tp_dealloc
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
io_file_tp_dealloc (io_file_o *self)
{
  delete self->obj;
  self->ob_type->tp_free ((PyObject*) self);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: is_valid getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_is_valid (io_file_o *self)
{
  return PyBool_FromLong (self->obj->operator bool ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: url getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_url (io_file_o *self)
{
  return PyString_FromString (self->obj->get_url ().c_str ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: is_file getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_is_file (io_file_o *self)
{
  return PyBool_FromLong (self->obj->is_file ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: is_folder getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_is_folder (io_file_o *self)
{
  return PyBool_FromLong (self->obj->is_folder ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: exists getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_exists (io_file_o *self)
{
  return PyBool_FromLong (self->obj->exists ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: size getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_size (io_file_o *self)
{
  return PyInt_FromLong (self->obj->get_size ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: user_id getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_user_id (io_file_o *self)
{
  return PyInt_FromLong (self->obj->get_user_id ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: user_name getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_user_name (io_file_o *self)
{
  return PyString_FromString (self->obj->get_user_name ().c_str ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: group_id getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_group_id (io_file_o *self)
{
  return PyInt_FromLong (self->obj->get_group_id ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: group_name getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_group_name (io_file_o *self)
{
  return PyString_FromString (self->obj->get_group_name ().c_str ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: permissions getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_permissions (io_file_o *self)
{
  return PyInt_FromLong (self->obj->get_permissions ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: last_access_time getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_last_access_time (io_file_o *self)
{
  auto ret_value = self->obj->get_last_access_time ();
  auto ret_date = ret_value.get_date ();
  auto ret_time = ret_value.get_time ();

  return PyDateTime_FromDateAndTime (ret_date.get_year (), ret_date.get_month (), ret_date.get_day (), ret_time.get_hour (), ret_time.get_minute (), ret_time.get_second (), 0);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: last_modification_time getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_last_modification_time (io_file_o *self)
{
  auto ret_value = self->obj->get_last_modification_time ();
  auto ret_date = ret_value.get_date ();
  auto ret_time = ret_value.get_time ();

  return PyDateTime_FromDateAndTime (ret_date.get_year (), ret_date.get_month (), ret_date.get_day (), ret_time.get_hour (), ret_time.get_minute (), ret_time.get_second (), 0);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: last_metadata_time getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_get_last_metadata_time (io_file_o *self)
{
  auto ret_value = self->obj->get_last_metadata_time ();
  auto ret_date = ret_value.get_date ();
  auto ret_time = ret_value.get_time ();

  return PyDateTime_FromDateAndTime (ret_date.get_year (), ret_date.get_month (), ret_date.get_day (), ret_time.get_hour (), ret_time.get_minute (), ret_time.get_second (), 0);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: new_reader
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_f_new_reader (io_file_o *self, PyObject *args)
{
  io_reader_o *ret = nullptr;

  try
    {
      ret = io_reader_tp_alloc ();
      if (ret != nullptr)
        ret->obj = new mobius::io::reader (self->obj->new_reader ());
    }
  catch (const std::runtime_error& e)
    {
      PyErr_SetString (PyExc_IOError, e.what ());
    }
    
  return (PyObject *) ret;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.file: new_writer
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_file_f_new_writer (io_file_o *self, PyObject *args)
{
  // parse input args
  int arg_overwrite = 1;

  if (!PyArg_ParseTuple (args, "|i", &arg_overwrite))
    return nullptr;

  // build and return value
  io_writer_o *ret = nullptr;

  try
    {
      ret = io_writer_tp_alloc ();
      if (ret != nullptr)
        ret->obj = new mobius::io::writer (self->obj->new_writer (arg_overwrite));
    }
  catch (const std::runtime_error& e)
    {
      PyErr_SetString (PyExc_IOError, e.what ());
    }
    
  return (PyObject *) ret;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: tp_new
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_tp_new (PyTypeObject *type, PyObject *args, PyObject *kwds)
{
  const char *arg_uri = nullptr;

  if (!PyArg_ParseTuple (args, "|s", &arg_uri))
    return NULL;

  io_folder_o *self = (io_folder_o *) type->tp_alloc (type, 0);
  if (self != NULL)
    {
      try
        {
          if (arg_uri)
            self->obj = new mobius::io::folder (arg_uri);
          else
            self->obj = new mobius::io::folder ();
        }
      catch (const std::runtime_error& e)
        {
          Py_DECREF (self);
          PyErr_SetString (PyExc_IOError, e.what ());
          self = NULL;
        }
    }

  return (PyObject *) self;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: tp_dealloc
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
io_folder_tp_dealloc (io_folder_o *self)
{
  delete self->obj;
  self->ob_type->tp_free ((PyObject*) self);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: is_valid getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_is_valid (io_folder_o *self)
{
  return PyBool_FromLong (self->obj->operator bool ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: url getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_url (io_folder_o *self)
{
  return PyString_FromString (self->obj->get_url ().c_str ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: is_file getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_is_file (io_folder_o *self)
{
  return PyBool_FromLong (self->obj->is_file ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: is_folder getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_is_folder (io_folder_o *self)
{
  return PyBool_FromLong (self->obj->is_folder ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: exists getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_exists (io_folder_o *self)
{
  return PyBool_FromLong (self->obj->exists ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: size getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_size (io_folder_o *self)
{
  return PyInt_FromLong (self->obj->get_size ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: user_id getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_user_id (io_folder_o *self)
{
  return PyInt_FromLong (self->obj->get_user_id ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: user_name getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_user_name (io_folder_o *self)
{
  return PyString_FromString (self->obj->get_user_name ().c_str ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: group_id getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_group_id (io_folder_o *self)
{
  return PyInt_FromLong (self->obj->get_group_id ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: group_name getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_group_name (io_folder_o *self)
{
  return PyString_FromString (self->obj->get_group_name ().c_str ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: permissions getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_permissions (io_folder_o *self)
{
  return PyInt_FromLong (self->obj->get_permissions ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: last_access_time getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_last_access_time (io_folder_o *self)
{
  auto ret_value = self->obj->get_last_access_time ();
  auto ret_date = ret_value.get_date ();
  auto ret_time = ret_value.get_time ();

  return PyDateTime_FromDateAndTime (ret_date.get_year (), ret_date.get_month (), ret_date.get_day (), ret_time.get_hour (), ret_time.get_minute (), ret_time.get_second (), 0);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: last_modification_time getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_last_modification_time (io_folder_o *self)
{
  auto ret_value = self->obj->get_last_modification_time ();
  auto ret_date = ret_value.get_date ();
  auto ret_time = ret_value.get_time ();

  return PyDateTime_FromDateAndTime (ret_date.get_year (), ret_date.get_month (), ret_date.get_day (), ret_time.get_hour (), ret_time.get_minute (), ret_time.get_second (), 0);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: last_metadata_time getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_last_metadata_time (io_folder_o *self)
{
  auto ret_value = self->obj->get_last_metadata_time ();
  auto ret_date = ret_value.get_date ();
  auto ret_time = ret_value.get_time ();

  return PyDateTime_FromDateAndTime (ret_date.get_year (), ret_date.get_month (), ret_date.get_day (), ret_time.get_hour (), ret_time.get_minute (), ret_time.get_second (), 0);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io.folder: children getter
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyObject *
io_folder_get_children (io_folder_o *self)
{
  PyObject *ret = PyList_New (0);
  if (ret == NULL)
    return NULL;

  try
    {
      for (const auto& child : *(self->obj))
        {
          if (child.is_file ())
            {
              io_file_o *item = (io_file_o *) io_file_t.tp_alloc (&io_file_t, 0);

              if (item == NULL)
                {
                  Py_CLEAR (ret);
                  return NULL;
                }

              item->obj = new mobius::io::file (child);
              PyList_Append (ret, (PyObject *) item);
            }
          else
            {
              io_folder_o *item = (io_folder_o *) io_folder_t.tp_alloc (&io_folder_t, 0);

              if (item == NULL)
                {
                  Py_CLEAR (ret);
                  return NULL;
                }

              item->obj = new mobius::io::folder (child);
              PyList_Append (ret, (PyObject *) item);
            }
        }
    }
  catch (const std::runtime_error& e)
    {
      PyErr_SetString (PyExc_IOError, e.what ());
      Py_CLEAR (ret);
      return NULL;
    }

  return ret;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief Capsule C API instance
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static mobius_io_capi CAPI = {
    &io_reader_tp_alloc,
    &io_writer_tp_alloc
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief io module initialisation function
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
PyMODINIT_FUNC
initio ()
{
  if (PyType_Ready (&io_reader_t) < 0) return;
  if (PyType_Ready (&io_writer_t) < 0) return;
  if (PyType_Ready (&io_file_t) < 0) return;
  if (PyType_Ready (&io_folder_t) < 0) return;

  PyObject* module = Py_InitModule3 ("io", io_methods, "Mobius Forensic Toolkit API wrapper");
  if (module == nullptr)
    return;

  PyDateTime_IMPORT;

  Py_INCREF (&io_reader_t);
  PyModule_AddObject (module, "reader", (PyObject *) &io_reader_t);
  Py_INCREF (&io_writer_t);
  PyModule_AddObject (module, "writer", (PyObject *) &io_writer_t);
  Py_INCREF (&io_file_t);
  PyModule_AddObject (module, "file", (PyObject *) &io_file_t);
  Py_INCREF (&io_folder_t);
  PyModule_AddObject (module, "folder", (PyObject *) &io_folder_t);
  
  PyObject *capi = PyCapsule_New (&CAPI, "mobius.io.capi", nullptr);
  if (capi == nullptr)
    return;
  
  PyModule_AddObject (module, "capi", capi);
}
