# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# 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/>.
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
import threading

from gi.repository import GLib
from gi.repository import Gdk
from gi.repository import Gtk


# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
# @brief Dialog: CopyDialog
# =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
class CopyDialog(object):

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Init widget
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __init__(self, reader, writer, title='Copying data...'):
        self.__reader = reader
        self.__writer = writer

        # dialog
        self.__dialog = Gtk.Dialog(title=title, modal=False)

        self.__cancel_button = self.__dialog.add_button(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL)
        self.__ok_button = self.__dialog.add_button(Gtk.STOCK_OK, Gtk.ResponseType.OK)
        self.__ok_button.set_sensitive(False)
        self.__dialog.set_position(Gtk.WindowPosition.CENTER)
        self.__dialog.set_type_hint(Gdk.WindowTypeHint.DIALOG)
        self.__dialog.set_border_width(5)
        self.__dialog.set_default_size(400, 200)

        # grid
        grid = Gtk.Grid.new()
        grid.set_row_spacing(5)
        grid.set_column_spacing(5)
        grid.show()
        self.__dialog.vbox.pack_start(grid, True, True, 0)

        label = Gtk.Label.new()
        label.set_markup('<b>Total bytes:</b>')
        label.set_xalign(1.0)
        label.show()
        grid.attach(label, 0, 0, 1, 1)

        self.__total_bytes_label = Gtk.Label('%15d' % reader.size)
        self.__total_bytes_label.set_xalign(1.0)
        self.__total_bytes_label.show()
        grid.attach(self.__total_bytes_label, 1, 0, 1, 1)

        label = Gtk.Label.new()
        label.set_markup('<b>Bytes copied:</b>')
        label.set_xalign(1.0)
        label.show()
        grid.attach(label, 0, 1, 1, 1)

        self.__bytes_copied_label = Gtk.Label('%15d' % 0)
        self.__bytes_copied_label.set_xalign(1.0)
        self.__bytes_copied_label.show()
        grid.attach(self.__bytes_copied_label, 1, 1, 1, 1)

        label = Gtk.Label.new()
        label.set_markup('<b>Bytes to go:</b>')
        label.set_xalign(1.0)
        label.show()
        grid.attach(label, 0, 2, 1, 1)

        self.__bytes_to_go_label = Gtk.Label('%15d' % reader.size)
        self.__bytes_to_go_label.set_xalign(1.0)
        self.__bytes_to_go_label.show()
        grid.attach(self.__bytes_to_go_label, 1, 2, 1, 1)

        # progress bar
        self.__progress_bar = Gtk.ProgressBar()
        self.__progress_bar.set_hexpand(True)
        self.__progress_bar.show()
        grid.attach(self.__progress_bar, 0, 4, 3, 1)

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Run dialog
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def run(self):
        self.__thread = threading.Thread(target=self.__export_thread, args=(self.__reader, self.__writer))
        self.__stop_event = threading.Event()
        self.__thread.start()

        rc = self.__dialog.run()

        if rc == Gtk.ResponseType.CANCEL:
            self.__stop_event.set()

        self.__dialog.destroy()

        return rc

    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    # @brief Export thread
    # =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
    def __export_thread(self, reader, writer):
        BLOCK_SIZE = 1048576
        pct = 0.0
        size = 0
        total_size = reader.size

        # read sectors from 'reader' and write them into 'writer'
        data = reader.read(BLOCK_SIZE)

        while not self.__stop_event.is_set() and data:
            writer.write(data)
            size += len(data)
            newpct = float(size) / total_size

            GLib.idle_add(self.__bytes_copied_label.set_text, '%15d' % size)
            GLib.idle_add(self.__bytes_to_go_label.set_text, '%15d' % (total_size - size))

            if newpct - pct > 0.01:
                pct = newpct
                GLib.idle_add(self.__progress_bar.set_fraction, pct)
                GLib.idle_add(self.__progress_bar.set_text, '%.2f %%' % (pct * 100.0))

            data = reader.read(BLOCK_SIZE)

        # finish
        writer.flush()
        GLib.idle_add(self.__progress_bar.set_fraction, 1.0)
        GLib.idle_add(self.__ok_button.set_sensitive, True)
        GLib.idle_add(self.__cancel_button.set_sensitive, False)
