// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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 "disk_impl_device.h"
#include <mobius/io/file.h>
#include <mobius/system/device_list.h>
#include <mobius/string_functions.h>

namespace mobius
{
namespace disk
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get device by UID
//! \param UID
//! \return device
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
static mobius::system::device
get_device_by_uid (const std::string& uid)
{
  mobius::system::device_list dev_list;
  dev_list.scan ();

  for (auto dev : dev_list)
    if (dev.get_property ("ID_SERIAL") == uid)
      return dev;

  return mobius::system::device ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief constructor
//! \param uid device UID
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
disk_impl_device::disk_impl_device (const std::string& uid)
  : uid_ (uid),
    device_ (get_device_by_uid (uid))
{
  set_name ("device " + uid);

  if (is_available ())
    {
      const std::string sector_size_str = device_.get_sysattr ("queue/logical_block_size");
      set_sector_size (std::strtoull (sector_size_str.c_str (), nullptr, 0));

      const std::string sectors_str = device_.get_sysattr ("size");
      set_size (std::strtoull (sectors_str.c_str (), nullptr, 0) * get_sector_size ());
    }
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief check whether disk is available
//! \return true/false
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
bool
disk_impl_device::is_available () const
{
  bool rc = false;

  if (device_)
    {
      auto f = mobius::io::new_file_by_path (device_.get_node ());
      rc = f.exists ();
    }

  return rc;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief create new reader
//! \return new reader
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::io::reader
disk_impl_device::new_reader () const
{
  return device_.new_reader ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief (re)scan disk
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
disk_impl_device::scan ()
{
  device_ = get_device_by_uid (uid_);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief get metadata
//! \return disk metadata
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::metadata
disk_impl_device::get_metadata () const
{
  std::string vendor = device_.get_property ("ID_VENDOR");
  std::string model = device_.get_property ("ID_MODEL");
  std::string serial = device_.get_property ("ID_SERIAL_SHORT");

  // format vendor
  if (vendor.empty () && (mobius::string::startswith (serial, "WD-") || mobius::string::startswith (model, "WDC_")))
    vendor = "Western Digital";

  // format serial
  if (mobius::string::startswith (serial, "WD-"))
    serial = serial.substr (3);

  return mobius::metadata
  {
    {"name", "name", "std::string", get_name ()},
    {"uid", "UID", "std::string", uid_},
    {"vendor", "drive vendor", "std::string", vendor},
    {"model", "drive model", "std::string", model},
    {"firmware", "firmware revision", "std::string", device_.get_property ("ID_REVISION")},
    {"serial", "drive serial number", "std::string", serial},
    {"wwn", "world wide name", "std::string", device_.get_property ("ID_WWN")},
    {"size", "size", "std::uint64_t", std::to_string (get_size ()) + " bytes"},
    {"sectors", "sectors", "std::uint64_t", device_.get_sysattr ("size")},
    {"sector_size", "sector size", "std::uint64_t", std::to_string (get_sector_size ()) + " bytes"},
  };
}

} // namespace disk
} // namespace mobius
