// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// Mobius Forensic Toolkit
// Copyright (C)
// 2008,2009,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022,2023,2024,2025
// 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 "../segment_decoder.hpp"
#include <iomanip>
#include <iostream>
#include <mobius/core/application.hpp>
#include <mobius/core/io/file.hpp>
#include <mobius/core/string_functions.hpp>
#include <sstream>
#include <unistd.h>

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// @brief show usage text
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
void
usage ()
{
    std::cerr << std::endl;
    std::cerr << "use: ewf_decoder [OPTIONS] <URL>" << std::endl;
    std::cerr << "e.g: ewf_decoder -v file://disk.E01" << std::endl;
    std::cerr << "     ewf_decoder file://disk.ewf" << std::endl;
    std::cerr << std::endl;
    std::cerr << "options are:" << std::endl;
    std::cerr << "  -v show section's metadata" << std::endl;
    std::cerr << std::endl;
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// @brief show uint32_t as hexstring
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
to_hexstring (std::uint32_t value)
{
    std::ostringstream stream;
    stream << "0x" << std::hex << std::setfill ('0') << std::setw (8) << value
           << std::dec << std::setfill (' ');
    return stream.str ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// @brief show uint64_t as hexstring
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
std::string
to_hex64 (std::uint64_t value)
{
    std::ostringstream stream;
    stream << "0x" << std::hex << std::setfill ('0') << std::setw (16) << value
           << std::dec << std::setfill (' ');
    return stream.str ();
}

// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
// @brief main function
// =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
int
main (int argc, char **argv)
{
    mobius::core::application app;
    std::cerr << app.get_name () << " v" << app.get_version () << std::endl;
    std::cerr << app.get_copyright () << std::endl;
    std::cerr << "EWF decoder v1.0" << std::endl;
    std::cerr << "by Eduardo Aguiar" << std::endl;

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // parse command line
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    int opt;
    int verbose_level = 0;

    while ((opt = getopt (argc, argv, "hv")) != EOF)
    {
        switch (opt)
        {
        case 'h':
            usage ();
            exit (EXIT_SUCCESS);
            break;

        case 'v':
            verbose_level++;
            break;

        default:
            usage ();
            exit (EXIT_FAILURE);
        }
    }

    if (optind >= argc)
    {
        std::cerr << std::endl;
        std::cerr << "Error: you must enter a valid URL to an imagefile"
                  << std::endl;
        usage ();
        exit (EXIT_FAILURE);
    }

    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    // show EWF file information
    // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    auto f = mobius::core::io::new_file_by_url (argv[optind]);
    auto reader = f.new_reader ();

    segment_decoder decoder (reader);
    std::cout << std::endl;
    std::cout << "segment number: " << decoder.get_segment_number ()
              << std::endl;
    std::cout << std::endl;
    std::cout << std::setw (12) << "offset"
              << "\t" << std::setw (12) << "next offset"
              << "\tname\t\t" << std::setw (12) << "size"
              << "\theader Adler32" << std::endl;
    std::cout << ' ' << std::string (78, '=') << std::endl;

    for (const auto &section : decoder)
    {
        if (verbose_level > 0)
            std::cout << std::endl;

        bool is_valid =
            section.get_adler32 () == section.get_calculated_adler32 ();

        std::cout << std::setw (12) << section.get_offset () << '\t'
                  << std::setw (12) << section.get_next_offset () << '\t'
                  << std::setw (12) << std::left << section.get_name ()
                  << std::right << '\t' << std::setw (12) << section.get_size ()
                  << '\t' << to_hexstring (section.get_adler32 ()) << ' '
                  << (is_valid ? "OK" : "ERR") << std::endl;

        if (verbose_level > 0)
            std::cout << std::endl;

        // show details
        if (verbose_level == 0)
            ;

        else if (section.get_name () == "hash")
        {
            auto hash_section = decoder.decode_hash_section (section);
            std::cout << "\t\tMD5 hash            : "
                      << hash_section.get_md5_hash () << std::endl;
            std::cout << "\t\tAdler-32 CRC        : "
                      << to_hexstring (hash_section.get_section_adler32 ())
                      << std::endl;
        }

        else if (section.get_name () == "volume" ||
                 section.get_name () == "disk" || section.get_name () == "data")
        {
            auto volume_section = decoder.decode_volume_section (section);
            std::cout << "\t\tmedia type          : "
                      << volume_section.get_media_type () << std::endl;
            std::cout << "\t\tchunks              : "
                      << volume_section.get_chunk_count () << std::endl;
            std::cout << "\t\tsectors per chunk   : "
                      << volume_section.get_chunk_sectors () << std::endl;
            std::cout << "\t\tsector size         : "
                      << volume_section.get_sector_size () << " bytes"
                      << std::endl;
            std::cout << "\t\tsectors             : "
                      << volume_section.get_sectors () << std::endl;
            std::cout << "\t\tmedia flags         : "
                      << volume_section.get_media_flags () << std::endl;
            std::cout << "\t\tcompression level   : "
                      << volume_section.get_compression_level () << std::endl;
            std::cout << "\t\tGUID                : "
                      << volume_section.get_guid () << std::endl;
            std::cout << "\t\tAdler-32 CRC        : "
                      << to_hexstring (volume_section.get_section_adler32 ())
                      << std::endl;
        }

        else if (section.get_name () == "header" ||
                 section.get_name () == "header2")
        {
            auto header_section = decoder.decode_header_section (section);
            auto text = mobius::core::string::replace (
                header_section.get_text (), "\n", "\n\t\t\t");
            std::cout << "\t\tdrive model         : "
                      << header_section.get_drive_model () << std::endl;
            std::cout << "\t\tdrive serial number : "
                      << header_section.get_drive_serial_number () << std::endl;
            std::cout << "\t\tacquisition user    : "
                      << header_section.get_acquisition_user () << std::endl;
            std::cout << "\t\tacquisition time    : "
                      << header_section.get_acquisition_time () << std::endl;
            std::cout << "\t\tacquisition tool    : "
                      << header_section.get_acquisition_tool () << std::endl;
            std::cout << "\t\tacquisition platform: "
                      << header_section.get_acquisition_platform ()
                      << std::endl;

            if (verbose_level > 1)
            {
                std::cout << "\t\ttext" << std::endl;
                std::cout << "\t\t\t" << text << std::endl;
            }
        }

        else if (section.get_name () == "table" ||
                 section.get_name () == "table2")
        {
            auto table_section = decoder.decode_table_section (section);
            std::cout << "\t\tchunks              : "
                      << table_section.get_chunk_count () << std::endl;
            std::cout << "\t\tbase offset         : "
                      << table_section.get_base_offset () << std::endl;
            std::cout << "\t\tmetadata adler-32   : "
                      << to_hexstring (table_section.get_section_adler32 ())
                      << std::endl;
            std::cout << "\t\toffset list adler-32: "
                      << to_hexstring (table_section.get_table_adler32 ())
                      << std::endl;

            if (verbose_level > 1)
            {
                std::cout << "\t\toffsets" << std::endl;

                for (auto offset : table_section.get_chunk_offset_list ())
                {
                    bool compressed = offset & 0x8000000000000000;
                    offset = offset & 0x7fffffffffffffff;
                    std::cout << "\t\t\t" << to_hex64 (offset)
                              << (compressed ? " (compressed)" : "")
                              << std::endl;
                }
            }
        }
    }
}
