// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024 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 "thread_guard.h"
#include <mobius/exception.inc>
#include <any>
#include <mutex>
#include <stdexcept>
#include <thread>
#include <unordered_map>

namespace
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Module data
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=

//! \brief Resources by thread
static std::unordered_map<std::thread::id, std::unordered_map<std::string, std::any>> resources_;

//! \brief Mutex for resources map
static std::mutex mutex_;

//! brief Main thread guard
static mobius::core::thread_guard main_thread_guard_;

} // namespace


namespace mobius::core
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Thread guard implementation class
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class thread_guard::impl
{
public:
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // Constructors and destructor
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  impl();
  impl(const impl&) = delete;
  impl(impl&&) = delete;
  ~impl();

  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  // Operators
  // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
  impl& operator=(const impl&) = delete;
  impl& operator=(impl&&) = delete;

private:
  std::thread::id thread_id_;
};


// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Default constructor
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
thread_guard::impl::impl()
  : thread_id_(std::this_thread::get_id ())
{
  std::lock_guard <std::mutex> lock (mutex_);

  if (resources_.find (thread_id_) != resources_.end ())
    throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("thread_guard already created"));

  resources_[thread_id_] = {};
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Destructor
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
thread_guard::impl::~impl()
{
  std::lock_guard <std::mutex> lock (mutex_);

  resources_.erase (thread_id_);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Default constructor
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
thread_guard::thread_guard()
  : impl_ (std::make_shared<impl> ())
{
}


// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Check if thread resource exists
//! \param resource_id Resource ID
//! \return true/false
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
has_thread_resource (const std::string& resource_id)
{
  auto thread_id = std::this_thread::get_id ();
  std::lock_guard <std::mutex> lock (mutex_);

  auto iter = resources_.find (thread_id);
  if (iter == resources_.end ())
    throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("thread_guard object must be created first"));

  return iter->second.find(resource_id) != iter->second.end();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Get thread resource
//! \param resource_id Resource ID
//! \return any object
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::any
get_thread_resource (const std::string& resource_id)
{
  auto thread_id = std::this_thread::get_id ();
  std::lock_guard <std::mutex> lock (mutex_);

  auto iter = resources_.find (thread_id);
  if (iter == resources_.end ())
    throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("thread_guard object must be created first"));

  std::any resource;

  auto resource_iter = iter->second.find (resource_id);
  if (resource_iter != iter->second.end())
    resource = resource_iter->second;

  return resource;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Set thread resource
//! \param resource_id Resource ID
//! \param resource_value Resource value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
set_thread_resource (const std::string& resource_id, const std::any& resource_value)
{
  auto thread_id = std::this_thread::get_id ();
  std::lock_guard <std::mutex> lock (mutex_);

  auto iter = resources_.find (thread_id);
  if (iter == resources_.end ())
    throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("thread_guard object must be created first"));

  iter->second[resource_id] = resource_value;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Remove thread resource
//! \param resource_id Resource ID
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
remove_thread_resource (const std::string& resource_id)
{
  auto thread_id = std::this_thread::get_id ();
  std::lock_guard <std::mutex> lock (mutex_);

  auto iter = resources_.find (thread_id);
  if (iter == resources_.end ())
    throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("thread_guard object must be created first"));

  iter->second.erase (resource_id);
}

} // namespace mobius::core