// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C) 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018 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 <mobius/imagefile/ewf/imagefile_impl.h>
#include <mobius/imagefile/ewf/segment_array.h>
#include <mobius/imagefile/ewf/segment_decoder.h>
#include <mobius/imagefile/ewf/reader_impl.h>
#include <mobius/imagefile/ewf/writer_impl.h>
#include <mobius/io/file.h>
#include <mobius/exception.inc>
#include <stdexcept>

namespace mobius
{
namespace imagefile
{
namespace ewf
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief check if URL is an instance of imagefile EWF
//! \return true/false
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
imagefile_impl::is_instance (const std::string& url)
{
  bool instance = false;

  mobius::io::file f (url);

  if (f && f.exists ())
    {
      auto reader = f.new_reader ();
      segment_decoder decoder (reader);
      instance = bool (decoder);
    }

  return instance;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief construct object
//! \param url imagefile URL
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
imagefile_impl::imagefile_impl (const std::string& url)
  : imagefile_impl_base (url)
{
  // scan segment files
  segment_array segments (url);
  segments.scan ();

  if (segments.get_size () == 0)
    return;

  // fill metadata
  const auto& first_segment = segments[0];
  set_acquisition_user (first_segment.get_user_name ());
  set_acquisition_time (first_segment.get_last_modification_time ());
  set_segments (segments.get_size ());

  // walk through segment files
  bool header_loaded = false;
  size_type next_offset = 0;

  for (const auto& segment : segments)
    {
      segment_decoder decoder (segment.new_reader ());

      chunk_offset_table offset_table;
      offset_table.start = next_offset;
      offset_table.end = 0;

      // walk through sections, retrieving imagefile metadata
      for (const auto& section : decoder)
        {
          if (section.get_name () == "hash")
            {
              auto hash_section = decoder.decode_hash_section (section);
              set_hash_md5 (hash_section.get_md5_hash ());
            }

          else if (section.get_name () == "volume" || section.get_name () == "disk")
            {
              auto volume_section = decoder.decode_volume_section (section);
              set_sectors (volume_section.get_sectors ());
              set_sector_size (volume_section.get_sector_size ());
              set_size (get_sectors () * get_sector_size ());
              chunk_size_ = volume_section.get_chunk_sectors () * get_sector_size ();
              offset_table.offsets.reserve (volume_section.get_chunk_count ());
            }

          else if (!header_loaded && (section.get_name () == "header2" || section.get_name () == "header"))
            {
              auto header_section = decoder.decode_header_section (section);
              set_drive_model (header_section.get_drive_model ());
              set_drive_serial_number (header_section.get_drive_serial_number ());

              if (!header_section.get_acquisition_user ().empty ())
                set_acquisition_user (header_section.get_acquisition_user ());

              if (header_section.get_acquisition_time ())
                set_acquisition_time (header_section.get_acquisition_time ());

              if (!header_section.get_acquisition_tool ().empty ())
                set_acquisition_tool (header_section.get_acquisition_tool ());

              set_acquisition_platform (header_section.get_acquisition_platform ());
              header_loaded = true;
            }

          else if (section.get_name () == "table")
            {
              auto table_section = decoder.decode_table_section (section);
              chunk_count_ += table_section.get_chunk_count ();
              size_type size = table_section.get_chunk_count () * chunk_size_;

              if (offset_table.end)
                offset_table.end += size;
              else
                offset_table.end = offset_table.start + size - 1;

              auto table_offset_list = table_section.get_chunk_offset_list ();

              offset_table.offsets.insert (
                offset_table.offsets.end (),
                std::make_move_iterator (table_offset_list.begin ()),
                std::make_move_iterator (table_offset_list.end ())
              );

              next_offset += size;
            }

        }

      chunk_offset_table_.push_back (offset_table);
    }

  // if there is only one segment, segment_size equals to size
  if (get_segments () == 1)
    set_segment_size (get_size ());
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief create new reader for imagefile
//! \return reader
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
const mobius::io::reader
imagefile_impl::new_reader () const
{
  return mobius::io::reader (std::make_shared <reader_impl> (*this));
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief create new writer for imagefile
//! \return writer
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
const mobius::io::writer
imagefile_impl::new_writer () const
{
  return mobius::io::writer (std::make_shared <writer_impl> (*this));
}

} // namespace ewf
} // namespace imagefile
} // namespace mobius
