// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// 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 "qdatastream.h"
#include <mobius/exception.inc>
#include <stdexcept>

namespace mobius::decoder
{
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Constructor
//! \param reader Reader object
//! \param version Serialization version
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
qdatastream::qdatastream (const mobius::io::reader& reader, std::uint32_t version)
  : decoder_ (reader), version_ (version)
{
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Decode QString
//! \return value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
qdatastream::get_qstring ()
{
  std::string s;

  auto size = decoder_.get_uint32_be ();

  if (size != 0xffffffff)
    s = decoder_.get_string_by_size (size, "UTF-16BE");

  return s;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Decode Qbytearray
//! \return value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::bytearray
qdatastream::get_qbytearray ()
{
  mobius::bytearray data;

  auto size = decoder_.get_uint32_be ();

  if (size != 0xffffffff)
    data = decoder_.get_bytearray_by_size (size);

  return data;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Decode Qdatetime
//! \return value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::datetime::datetime
qdatastream::get_qdatetime ()
{
  auto value = decoder_.get_uint64_be ();

  return mobius::datetime::datetime (1970, 1, 1, 0, 0, 0) +
         mobius::datetime::timedelta (0, 0, value / 1000);
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Decode QImage
//! \return value
//! \see https://github.com/qt/qtbase/blob/dev/src/gui/image/qimage.cpp (operator>>)
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::bytearray
qdatastream::get_qimage ()
{
  mobius::bytearray data;

  // check if image is null
  std::int32_t data_marker = 1;

  if (version_ >= 5)
    data_marker = get_qint32 ();

  // read PNG data
  if (data_marker)
    {
      auto pos = decoder_.tell ();

      // check signature
      auto signature = decoder_.get_bytearray_by_size (8);

      if (signature != "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a")
        throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("invalid PNG data"));

      // read chunks until type == IEND
      std::string type;

      do
        {
          auto length = decoder_.get_uint32_be ();
          type = decoder_.get_string_by_size (4);
          decoder_.skip (length + 4);
        } while (type != "IEND");

      // read complete data
      auto size = decoder_.tell () - pos;
      decoder_.seek (pos);
      data = decoder_.get_bytearray_by_size (size);
    }

  return data;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Decode QPixmap
//! \return value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::bytearray
qdatastream::get_qpixmap ()
{
  return get_qimage ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
//! \brief Decode QVariant
//! \return value
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
mobius::pod::data
qdatastream::get_qvariant ()
{
  mobius::pod::data data;

  auto datatype = decoder_.get_uint32_be ();
  bool is_null = decoder_.get_uint8 ();

  switch (datatype)
  {
    case 12:            // QBytearray
        data = is_null ? mobius::bytearray () : get_qbytearray ();
        break;

    default:
        throw std::runtime_error (MOBIUS_EXCEPTION_MSG ("unhandled datatype: " + std::to_string (datatype)));
  };

  return data;
}

} // namespace mobius::decoder
