// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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 "filesystem.h"
#include "filesystem_impl_null.h"
#include "filesystem_impl_exfat.h"
#include "filesystem_impl_ext2.h"
#include "filesystem_impl_hfs.h"
#include "filesystem_impl_iso.h"
#include "filesystem_impl_ntfs.h"
#include "filesystem_impl_vfat.h"
#include <map>
#include <string>
#include <utility>
#include <functional>

namespace mobius
{
namespace vfs
{
namespace filesystem
{
namespace
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Helper function to build filesystem objects
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
template <typename T> std::pair <f_is_instance_type, f_build_type>
functions ()
{
  return std::make_pair (
    [] (const mobius::io::reader& reader, std::uint64_t offset){
      return T::is_instance (reader, offset);
    },
    [] (const mobius::io::reader& reader, std::uint64_t offset){
      return std::make_shared <T> (reader, offset);
    });
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Filesystem implementations
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::map <std::string, std::pair <f_is_instance_type, f_build_type>> FILESYSTEMS =
{
  {"exfat", functions <filesystem_impl_exfat> ()},
  {"ext2", functions <filesystem_impl_ext2> ()},
  {"hfs", functions <filesystem_impl_hfs> ()},
  {"iso", functions <filesystem_impl_iso> ()},
  {"ntfs", functions <filesystem_impl_ntfs> ()},
  {"vfat", functions <filesystem_impl_vfat> ()},
};

} // namespace

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Register new filesystem type
//! \param type Type ID
//! \param f1 is_instance function pointer
//! \param f2 create function pointer
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
register_filesystem_type (
  const std::string& type,
  f_is_instance_type f1,
  f_build_type f2
)
{
  FILESYSTEMS[type] = std::make_pair (f1, f2);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Unregister new filesystem type
//! \param type Type ID
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
unregister_filesystem_type (const std::string& type)
{
  FILESYSTEMS.erase (type);
}

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

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Constructor
//! \param reader Reader object
//! \param offset Offset from the beginning of the stream
//! \param a_type Filesystem type (default = "autodetect")
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
filesystem::filesystem (
  const mobius::io::reader& reader,
  std::uint64_t offset,
  const std::string& a_type
)
{
  std::string type = a_type;

  // detect filesystem type, if type == "autodetect"
  if (type == "autodetect")
    {
      auto iter = FILESYSTEMS.begin ();
      bool found = false;
      
      while (iter != FILESYSTEMS.end () && !found)
        {
          if (iter->second.first (reader, offset))
            {
              type = iter->first;
              found = true;
            }
          else
            ++iter;
        }
    }

  // create impl_ according to type
  auto iter = FILESYSTEMS.find (type);
  
  if (iter == FILESYSTEMS.end ())
    impl_ = std::make_shared <filesystem_impl_null> ();
  
  else
    impl_ = iter->second.second (reader, offset);
}

} // namespace filesystem
} // namespace vfs
} // namespace mobius
