// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022 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 "pymobius.h"
#include "module.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 "encoder/module.h"
#include "imagefile/module.h"
#include "io/module.h"
#include "model/module.h"
#include "os/module.h"
#include "partition/module.h"
#include "pod/module.h"
#include "system/module.h"
#include "turing/module.h"
#include "ui/module.h"
#include "vfs/module.h"

#include <functional>
#include <stdexcept>
#include <utility>

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

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Sub-modules
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static std::pair <std::string, std::function<PyObject *()>> sub_modules[] =
{
  {"core", new_core_module },
  {"crypt", new_crypt_module },
  {"database", new_database_module },
  {"datetime", new_datetime_module },
  {"decoder", new_decoder_module },
  {"disk", new_disk_module },
  {"encoder", new_encoder_module },
  {"imagefile", new_imagefile_module },
  {"io", new_io_module },
  {"model", new_model_module },
  {"os", new_os_module },
  {"partition", new_partition_module },
  {"pod", new_pod_module },
  {"system", new_system_module },
  {"turing", new_turing_module },
  {"ui", new_ui_module },
  {"vfs", new_vfs_module },
};

} // namespace

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \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
  for (const auto& p : sub_modules)
    {
      PyObject *s_module = p.second ();
      PyModule_AddObject (mobius_module, p.first.c_str (), s_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 "pymobius.h"
// header file and can be used throughout the api_xxx.cc source files.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
namespace mobius
{
namespace py
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Check if object is PyDateTime
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
pydatetime_check (PyObject *obj)
{
  return PyDateTime_Check (obj);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \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
    ret = mobius::py::pynone ();

  return ret;
}

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

  if (!mobius::py::pynone_check (obj))
    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;
}

} // namespace py
} // namespace mobius

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