// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019 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 "module.h"
#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 "datetime/module.h"
#include "decoder/module.h"
#include "disk/module.h"
#include "filesystem/module.h"
#include "imagefile/module.h"
#include "io/module.h"
#include "model/module.h"
#include "partition/module.h"
#include "registry/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 *datetime_module = new_datetime_module ();
  PyModule_AddObject (mobius_module, "datetime", datetime_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 *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 *registry_module = new_registry_module ();
  PyModule_AddObject (mobius_module, "registry", registry_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)
{
  PyObject *ret = nullptr;

  if (dt)
    {
      auto d = dt.get_date ();
      auto t = dt.get_time ();

      ret = PyDateTime_FromDateAndTime (
              d.get_year (),
              d.get_month (),
              d.get_day (),
              t.get_hour (),
              t.get_minute (),
              t.get_second (), 0);
    }

  else
    {
      Py_INCREF (Py_None);
      ret = Py_None;
    }

  return ret;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create mobius::datetime::datetime from PyDateTime
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::datetime::datetime
PyDateTime_as_datetime (PyObject *obj)
{
  mobius::datetime::datetime dt;

  if (obj != Py_None)
    dt = 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));

  return dt;
}

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

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

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create mobius::bytearray from PyBytes
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::bytearray
PyBytes_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 bytearray"));

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

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create PyString from std::string
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
PyObject *
PyString_from_std_string (const std::string& s)
{
  return PyString_FromString (s.c_str ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create std::string from PyString
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
PyString_as_std_string (PyObject *value)
{
  char *buffer = PyString_AsString (value);

  if (buffer == nullptr)
    throw std::runtime_error (mobius::MOBIUS_EXCEPTION_MSG ("Invalid Python string"));

  return std::string (buffer);
}
