// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017 Eduardo Aguiar
//
// 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; either version 2, or (at your option) any later
// version.
//
// 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, see <http://www.gnu.org/licenses/>.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief  C++ API api module wrapper
//!\author Eduardo Aguiar
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include <Python.h>
#include <datetime.h>
#include <stdexcept>
#include "api_dataholder.h"
#include "api_application.h"
#include "api_device.h"
#include "api_file.h"
#include "api_folder.h"
#include "api_partition.h"
#include "api_imagefile.h"
#include "api_reader.h"
#include "api_writer.h"

#include "disk/module.h"
#include "filesystem/module.h"

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief functions prototypes
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
PyObject* func_get_devices (PyObject *, PyObject *);
PyObject* func_get_partition_table (PyObject *, PyObject *);
PyObject* func_get_partitions (PyObject *, PyObject *);

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief api module methods
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static PyMethodDef api_methods[] =
{
  {
    "get_devices",
    func_get_devices,
    METH_VARARGS,
    "get system's device list"
  },
  {
    "get_partition_table",
    func_get_partition_table,
    METH_VARARGS,
    "get partition table from a given stream"
  },
  {
    "get_partitions",
    func_get_partitions,
    METH_VARARGS,
    "get partitions from a given stream"
  },
  {
    NULL,
    NULL,
    0,
    NULL
  } // sentinel
};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief api module initialisation function
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
PyMODINIT_FUNC
initapi ()
{
  // build types
  if (PyType_Ready (&api_dataholder_t) < 0)
    return;

  if (PyType_Ready (&api_application_t) < 0)
    return;

  if (PyType_Ready (&api_device_t) < 0)
    return;

  if (PyType_Ready (&api_file_t) < 0)
    return;

  if (PyType_Ready (&api_folder_t) < 0)
    return;
  
  if (PyType_Ready (&api_partition_t) < 0)
    return;
  
  if (PyType_Ready (&api_imagefile_t) < 0)
    return;

  if (PyType_Ready (&api_reader_t) < 0)
    return;

  if (PyType_Ready (&api_writer_t) < 0)
    return;

  // initialize module
  PyObject* module = Py_InitModule3 (
    "api",
    api_methods,
    "Mobius Forensic Toolkit API wrapper"
  );

  if (module == nullptr)
    return;

  // add types  
  PyDateTime_IMPORT;

  // get mobius module
  PyObject *mobius_module = PyImport_Import (PyString_FromString ("mobius"));

  if (mobius_module == nullptr)
    return;

  // build submodules
  PyObject *disk_module = new_disk_module ();
  PyModule_AddObject (mobius_module, "disk", disk_module);

  PyObject *filesystem_module = new_filesystem_module ();
  PyModule_AddObject (mobius_module, "filesystem", filesystem_module);

  // deprecated: this types must be enclosed in submodules
  Py_INCREF (&api_dataholder_t);
  PyModule_AddObject (module, "dataholder", (PyObject *) &api_dataholder_t);

  Py_INCREF (&api_application_t);
  PyModule_AddObject (module, "application", (PyObject *) &api_application_t);

  Py_INCREF (&api_device_t);
  PyModule_AddObject (module, "device", (PyObject *) &api_device_t);

  Py_INCREF (&api_file_t);
  PyModule_AddObject (module, "file", (PyObject *) &api_file_t);

  Py_INCREF (&api_folder_t);
  PyModule_AddObject (module, "folder", (PyObject *) &api_folder_t);

  Py_INCREF (&api_imagefile_t);
  PyModule_AddObject (module, "imagefile", (PyObject *) &api_imagefile_t);

  Py_INCREF (&api_partition_t);
  PyModule_AddObject (module, "partition", (PyObject *) &api_partition_t);

  Py_INCREF (&api_reader_t);
  PyModule_AddObject (module, "reader", (PyObject *) &api_reader_t);

  Py_INCREF (&api_writer_t);
  PyModule_AddObject (module, "writer", (PyObject *) &api_writer_t);
  
  // add api.imagefile_typelist var
  PyObject *typelist = PyList_New (0);
  if (typelist == nullptr)
    return;
  
  for (auto i : mobius::imagefile::imagefile::typelist)
    {
      PyObject *t = PyTuple_New (4);
      PyTuple_SetItem (t, 0, PyString_FromString (i.type.c_str ()));
      PyTuple_SetItem (t, 1, PyString_FromString (i.description.c_str ()));
      PyTuple_SetItem (t, 2, PyString_FromString (i.extensions.c_str ()));
      PyTuple_SetItem (t, 3, PyBool_FromLong (i.is_writeable));
      PyList_Append (typelist, t);
    }

  PyModule_AddObject (module, "imagefile_typelist", (PyObject *) typelist);  
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// These three PyDateTime functions must be located here, because PyDateTime_IMPORT
// creates a static variable, and as so, with local scope to this file only.
// These functions are declared into "api_datetime.h" header file and can
// be used throughout the api_xxx.cc source files.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief create PyDateTime from mobius::datetime::datetime
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
PyObject *
PyDateTime_from_datetime (const mobius::datetime::datetime& dt)
{
  auto ret_date = dt.get_date ();
  auto ret_time = dt.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 create mobius::datetime::datetime from PyDateTime
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
const mobius::datetime::datetime
datetime_from_PyDateTime (PyObject *obj)
{
  return mobius::datetime::datetime (
    PyDateTime_GET_YEAR (obj),
    PyDateTime_GET_MONTH (obj),
    PyDateTime_GET_DAY (obj),
    PyDateTime_DATE_GET_HOUR (obj),
    PyDateTime_DATE_GET_MINUTE (obj),
    PyDateTime_DATE_GET_SECOND (obj));
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//!\brief check if object is PyDateTime
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
check_PyDateTime (PyObject *obj)
{
  return PyDateTime_Check (obj);
}
