// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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/>.
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
#include "application.h"
#include "extension_set.h"
#include "log.h"
#include <mobius/exception.inc>
#include <mobius/io/path.h>
#include <mobius/io/folder.h>
#include <atomic>
#include <cstdlib>
#include <mutex>
#include <stdexcept>
#include <sys/stat.h>
#include <sys/types.h>

namespace mobius
{
namespace core
{
namespace
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Application data
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief application ID
static const std::string id_ = "mobiusft";

//! \brief application name
static const std::string name_ = "Mobius Forensic Toolkit";

//! \brief application version
static const std::string version_ = "1.34";

//! \brief application title
static const std::string title_ = "Mobius v1.34";

//! \brief application copyright
static const std::string copyright_ = "Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022 Eduardo Aguiar";

//! \brief application OS name  
static const std::string os_name_ = "x86_64-pc-linux-gnu";

//! \brief data folder path
static std::string data_folder_ = "/home/aguiar/tmp/mobius.install/share/" + id_;

//! \brief configuration folder path
static std::string config_folder_;

//! \brief cache folder path
static std::string cache_folder_;

//! \brief C++ extensions
static extension_set extensions_;

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

//! \brief Is application running
static std::atomic_bool is_running_ {false};

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Join a relative path to a absolute path
//! \param abs_path absolute path
//! \param rel_path relative path
//! \return absolute path
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static std::string
_join_path (const std::string& abs_path, const std::string& rel_path)
{
  auto path = mobius::io::path (abs_path);
  return to_string (path.get_child_by_path (rel_path));
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Initialize application data
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
_init ()
{
  const char *home = getenv ("HOME");
      
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // Config folder
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const char *xdg_config_home = getenv ("XDG_CONFIG_HOME");

  if (xdg_config_home)
    config_folder_ = xdg_config_home;
  else
    config_folder_ = home + std::string ("/.config");

  config_folder_ += std::string ("/") + id_;

  auto config_folder = mobius::io::new_folder_by_path (config_folder_);
  config_folder.create ();

  set_logfile_path (config_folder_ + "/mobius.log");
      
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // Cache folder
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const char *xdg_cache_home = getenv ("XDG_CACHE_HOME");
      
  if (xdg_cache_home)
    cache_folder_ = xdg_cache_home;

  else
    cache_folder_ = home + std::string ("/.cache");

  cache_folder_ += std::string ("/") + id_;

  auto cache_folder = mobius::io::new_folder_by_path (cache_folder_);
  cache_folder.create ();
      
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // Data folder
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  const char *mobius_data_dir = getenv ("MOBIUS_DATA_DIR");

  if (mobius_data_dir)
    data_folder_ = mobius_data_dir;
}

} // namespace

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Constructor
//! \see https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
application::application ()
{
  std::call_once (is_loaded_, _init);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Start application
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
application::start ()
{
  mobius::core::log log (__FILE__, __FUNCTION__);
  log.info (__LINE__, "starting application");

  // check if application is already running
  if (is_running_)
    throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("application is already running"));

  // search installed C++ extensions
  log.info (__LINE__, "loading C++ extensions");

  auto extensions_path = get_config_path ("extensions");
  auto folder = mobius::io::new_folder_by_path (extensions_path);
  
  for (auto& c : folder.get_children ())
    {
      if (c.is_file () && c.get_extension () == "so")
        extensions_.load (c.get_path ());
    }

  // start extensions
  for (auto& e : extensions_.get_extensions ())
    e.start ();

  // set application running
  log.info (__LINE__, "application started");
  is_running_ = true;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Stop application
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
application::stop ()
{
  mobius::core::log log (__FILE__, __FUNCTION__);

  // check if application is running
  if (!is_running_)
    throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("application is not running"));

  // stop extensions
  log.info (__LINE__, "unloading C++ extensions");

  for (auto& e : extensions_.get_extensions ())
    e.stop ();

  // remove extensions
  //extensions_.clear ();

  // set running false  
  log.info (__LINE__, "application stopped");
  is_running_ = false;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get application ID
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
application::get_id () const
{
  return id_;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get application name
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
application::get_name () const
{
  return name_;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get application version
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
application::get_version () const
{
  return version_;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get application title
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
application::get_title () const
{
  return title_;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get application copyright
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
application::get_copyright () const
{
  return copyright_;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get application host OS
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
application::get_os_name () const
{
  return os_name_;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get data path
//! \param path relative path
//! \return absolute path to data_folder sub-path
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
application::get_data_path (const std::string& path) const
{
  return _join_path (data_folder_, path);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get config path
//! \param path relative path
//! \return absolute path to config_folder sub-path
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
application::get_config_path (const std::string& path) const
{
  return _join_path (config_folder_, path);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get cache path
//! \param path relative path
//! \return absolute path to cache_folder sub-path
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
application::get_cache_path (const std::string& path) const
{
  return _join_path (cache_folder_, path);
}

} // namespace core
} // namespace mobius
