// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021 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 "imagefile_impl_dossier.h"
#include <mobius/io/file.h>
#include <mobius/datetime/timedelta.h>
#include <mobius/string_functions.h>
#include <mobius/exception.inc>
#include <mobius/charset.h>
#include <regex>
#include <stdexcept>

namespace mobius
{
namespace imagefile
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Check if file is an instance of imagefile dossier
//! \param f File object
//! \return true/false
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
imagefile_impl_dossier::is_instance (mobius::io::file f)
{
  bool instance = false;

  if (f && f.exists ())
    {
      auto reader = f.new_reader ();
      
      if (reader)
        {
          mobius::bytearray data = reader.read (128);
          const std::string text = conv_charset_to_utf8 (data, "ASCII");
          instance = text.find ("Forensic Dossier") != std::string::npos;
        }
    }

  return instance;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Construct object
//! \param f File object
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
imagefile_impl_dossier::imagefile_impl_dossier (mobius::io::file f)
  : file_ (f),
    split_imagefile_ (f.new_sibling_by_extension ("001"))
{
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new reader for imagefile
//! \return Reader object
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::io::reader
imagefile_impl_dossier::new_reader () const
{
  return split_imagefile_.new_reader ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Create new writer for imagefile
//! \return Writer object
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::io::writer
imagefile_impl_dossier::new_writer () const
{
  throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("writer not implemented"));
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Get metadata
//! \return imagefile metadata
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::metadata
imagefile_impl_dossier::get_metadata () const
{
  return mobius::metadata
  {
    {
      "type",
      "type",
      "std::string",
      get_type ()
    },
    {
      "size",
      "size",
      "size_type",
      std::to_string (get_size ()) + " bytes"
    },
    {
      "sectors",
      "number of sectors",
      "size_type",
      std::to_string (get_sectors ())
    },
    {
      "sector_size",
      "sector size",
      "size_type",
      std::to_string (get_sector_size ()) + " bytes"
    },
    {
      "segments",
      "segments",
      "size_type",
      std::to_string (get_segments ())
    },
    {
      "segment_size",
      "segment_size",
      "size_type",
      std::to_string (get_segment_size ()) + " bytes"
    },
    {
      "drive_vendor",
      "drive vendor",
      "std::string",
      get_drive_vendor ()
    },
    {
      "drive_model",
      "drive model",
      "std::string",
      get_drive_model ()
    },
    {
      "drive_serial_number",
      "drive serial number",
      "std::string",
      get_drive_serial_number ()
    },
    {
      "acquisition_user",
      "acquisition user name",
      "std::string",
      get_acquisition_user ()
    },
    {
      "acquisition_time",
      "acquisition date/time",
      "mobius::datetime::datetime",
      to_string (get_acquisition_time ())
    },
    {
      "acquisition_tool",
      "acquisition tool",
      "std::string",
      get_acquisition_tool ()
    },
    {
      "acquisition_platform",
      "acquisition platform",
      "std::string",
      get_acquisition_platform ()
    },
  };
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief load metadata on demand
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
imagefile_impl_dossier::_load_metadata () const
{
  if (metadata_loaded_)
    return;

  // Check if imagefile exists
  constexpr mobius::io::file::size_type LOG_MAX_SIZE = 65536;

  if (!file_ || !file_.exists ())
    throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("image file not found"));

  if (file_.get_size () > LOG_MAX_SIZE)
    throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("image file control file too large"));

  // Load metadata
  std::regex REGEX_DOSSIER_SERIAL ("Serial No.:([0-9]+)");
  std::regex REGEX_DOSSIER_SOFTWARE ("Software: ([A-Z0-9.]+)");
  std::regex REGEX_TIME_ZONE ("Time Zone: ([0-9+-]+)");
  std::regex REGEX_DRIVE_MODEL ("Model : ([^|]+?)\\|");
  std::regex REGEX_DRIVE_SERIAL ("Serial: ([0-9A-Z-]+) *\\|");
  std::regex REGEX_TOTAL_SECTORS ("Total Sectors.*\r\n\\* *([0-9]+).*\\|");
  std::regex REGEX_COMPLETION_TIME ("Completion Time: ([0-9]{2})/([0-9]{2})/([0-9]{4}) *([0-9]{2}):([0-9]{2}):([0-9]{2})");

  // parse .log file
  auto reader = file_.new_reader ();
  mobius::bytearray data = reader.read (file_.get_size ());
  const std::string text = conv_charset_to_utf8 (data, "ASCII");
  std::smatch match;

  int timezone = 0;

  if (std::regex_search (text, match, REGEX_TIME_ZONE))
    timezone = stoi (match[1].str ());

  if (std::regex_search (text, match, REGEX_DOSSIER_SERIAL))
    acquisition_platform_ = "Logicube Forensic Dossier (s/n: " + match[1].str () + ')';

  if (std::regex_search (text, match, REGEX_DOSSIER_SOFTWARE))
    acquisition_tool_ = "Dossier software " + match[1].str ();

  if (std::regex_search (text, match, REGEX_DRIVE_MODEL))
    drive_model_ = mobius::string::strip (match[1].str ());

  if (std::regex_search (text, match, REGEX_DRIVE_SERIAL))
    drive_serial_number_ = match[1].str ();

  if (std::regex_search (text, match, REGEX_TOTAL_SECTORS))
    {
      sectors_ = stoll (match[1].str ());
      size_ = sectors_ * sector_size_;
    }

  if (std::regex_search (text, match, REGEX_COMPLETION_TIME))
    {
      mobius::datetime::datetime d (stoi (match[3].str ()),
                                    stoi (match[1].str ()),
                                    stoi (match[2].str ()),
                                    stoi (match[4].str ()),
                                    stoi (match[5].str ()),
                                    stoi (match[6].str ()));

      if (timezone)
        {
          mobius::datetime::timedelta delta;
          delta.set_seconds (-timezone * 3600);
          d = d + delta;
        }

      acquisition_time_ = d;
    }

  segments_ = split_imagefile_.get_segments ();
  segment_size_ = split_imagefile_.get_segment_size ();
  acquisition_user_ = file_.get_user_name ();

  normalize_drive_info (drive_vendor_, drive_model_, drive_serial_number_);

  // set metadata loaded
  metadata_loaded_ = true;
}

} // namespace imagefile
} // namespace mobius
