// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023 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/>.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "config.h"
#include "application.h"
#include <mobius/database/connection_pool.h>
#include <mutex>

namespace mobius
{
namespace core
{
namespace
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Config data
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

//! \brief module's connection pool
static mobius::database::connection_pool connection_pool_;

//! \brief Is application data loaded
static std::once_flag is_loaded_;

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Initialize configuration data
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static void
_init ()
{
  mobius::core::application app;
  connection_pool_.set_path (app.get_config_path ("mobiusft.sqlite"));

  auto db = connection_pool_.get_database ();
  auto transaction = db.new_transaction ();

  db.execute (
      "CREATE TABLE IF NOT EXISTS configuration"
                            "(var TEXT PRIMARY KEY,"
                           "value BLOB NULL);"
             );
  
  transaction.commit ();
}

} // namespace

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new connection to config database
//! \return New connection object
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::database::connection
new_config_connection ()
{
  std::call_once (is_loaded_, _init);

  return connection_pool_.acquire ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new transaction to config database
//! \return New database transaction
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::database::transaction
new_config_transaction ()
{
  std::call_once (is_loaded_, _init);

  auto db = connection_pool_.get_database ();
  return db.new_transaction ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Check if config value exists
//! \param name Value name
//! \return true/false
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
has_config (const std::string& name)
{
  std::call_once (is_loaded_, _init);

  auto db = connection_pool_.get_database ();

  auto statement = db.new_statement (
         "SELECT 1 "
           "FROM configuration "
          "WHERE var = ?"
       );

  statement.bind (1, name);

  return statement.fetch_row ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Get config value
//! \param name Value name
//! \return Value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::pod::data
get_config (const std::string& name)
{
  std::call_once (is_loaded_, _init);

  auto db = connection_pool_.get_database ();

  auto statement = db.new_statement (
         "SELECT value "
           "FROM configuration "
          "WHERE var = ?"
       );

  statement.bind (1, name);
  
  mobius::pod::data value;

  if (statement.fetch_row ())
    value = statement.get_column_pod (0);;
  
  return value;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Set config value
//! \param name Value name
//! \param value Value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
set_config (const std::string& name, const mobius::pod::data& value)
{
  std::call_once (is_loaded_, _init);

  auto db = connection_pool_.get_database ();

  auto statement = db.new_statement (
         "INSERT OR REPLACE INTO configuration "
                         "VALUES (?, ?)"
       );

  statement.bind (1, name);
  statement.bind (2, value);
  statement.execute ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Remove config value
//! \param name Value name
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
remove_config (const std::string& name)
{
  std::call_once (is_loaded_, _init);

  auto db = connection_pool_.get_database ();

  auto statement = db.new_statement (
         "DELETE FROM configuration "
          "WHERE var = ?"
       );

  statement.bind (1, name);
  statement.execute ();
}

} // namespace core
} // namespace mobius
