// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 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 module wrapper
//! \author Eduardo Aguiar
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "api.h"
#include <Python.h>
#include <datetime.h>
#include "api_dataholder.h"

#include "core/module.h"
#include "crypt/module.h"
#include "database/module.h"
#include "decoder/module.h"
#include "disk/module.h"
#include "filesystem/module.h"
#include "forensics/module.h"
#include "imagefile/module.h"
#include "io/module.h"
#include "model/module.h"
#include "partition/module.h"
#include "system/module.h"
#include "turing/module.h"
#include <mobius/exception.inc>
#include <stdexcept>

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

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

  // initialize module
  PyObject* mobius_module = Py_InitModule3 (
                              "mobius",
                              module_methods,
                              "Mobius Forensic Toolkit API wrapper"
                            );

  if (mobius_module == nullptr)
    return;

  // add types
  PyDateTime_IMPORT;

  // build submodules
  PyObject *core_module = new_core_module ();
  PyModule_AddObject (mobius_module, "core", core_module);

  PyObject *crypt_module = new_crypt_module ();
  PyModule_AddObject (mobius_module, "crypt", crypt_module);

  PyObject *database_module = new_database_module ();
  PyModule_AddObject (mobius_module, "database", database_module);

  PyObject *decoder_module = new_decoder_module ();
  PyModule_AddObject (mobius_module, "decoder", decoder_module);

  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);

  PyObject *forensics_module = new_forensics_module ();
  PyModule_AddObject (mobius_module, "forensics", forensics_module);

  PyObject *imagefile_module = new_imagefile_module ();
  PyModule_AddObject (mobius_module, "imagefile", imagefile_module);

  PyObject *io_module = new_io_module ();
  PyModule_AddObject (mobius_module, "io", io_module);

  PyObject *model_module = new_model_module ();
  PyModule_AddObject (mobius_module, "model", model_module);

  PyObject *partition_module = new_partition_module ();
  PyModule_AddObject (mobius_module, "partition", partition_module);

  PyObject *system_module = new_system_module ();
  PyModule_AddObject (mobius_module, "system", system_module);

  PyObject *turing_module = new_turing_module ();
  PyModule_AddObject (mobius_module, "turing", turing_module);

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

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::datetime::datetime
PyDateTime_as_datetime (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);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief create PyString from mobius::bytearray
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
PyObject *
PyString_from_bytearray (const mobius::bytearray& array)
{
  return PyString_FromStringAndSize ((const char *) array.data (), array.size ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief create mobius::bytearray from PyString
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::bytearray
PyString_as_bytearray (PyObject *value)
{
  char *buffer;
  Py_ssize_t length;

  if (PyString_AsStringAndSize (value, &buffer, &length) == -1)
    throw std::runtime_error (mobius::MOBIUS_EXCEPTION_MSG ("invalid Python string"));

  return mobius::bytearray (reinterpret_cast <const std::uint8_t *> (buffer), length);
}
