# General GUI related functions and structures
use strict;
use warnings;
use Gtk2 -init;

my $builder = Gtk2::Builder->new;
$builder->add_from_file("$Bin/share/remotebox/remotebox.xml");
$builder->connect_signals();

our %gui = (textbufferDescription                => $builder->get_object('textbufferDescription'),
            textbufferSnapshotDescription        => $builder->get_object('textbufferSnapshotDescription'),
            textbufferSnapshotDetailsDescription => $builder->get_object('textbufferSnapshotDetailsDescription'),
            appname                              => $builder->get_object('aboutdialog')->get_program_name(),
            appver                               => $builder->get_object('aboutdialog')->get_version(),
            websn                                => undef,
            pixbufstatuserror16                  => Gtk2::Gdk::Pixbuf->new_from_file("$Bin/share/remotebox/icons/status_error_16px.png"),
            pixbufotheros                        => Gtk2::Gdk::Pixbuf->new_from_file("$Bin/share/remotebox/icons/os/Other.png"));

# Fill %gui so we can reference them easily.
foreach ($builder->get_objects) {
    my $id;
    eval{ $id = $_->Gtk2::Buildable::get_name; }; # DONT USE $_->get_name()
    $gui{$id} = $_ if ($id);
}

# We have to register signals manually, which we want to block at some point because we need the sigids
# as blocking any other way is not supported in perl-gtk (other blocking funcs map to null funcs)
our %signal = (fam          => $gui{comboboxNewOSFam}->signal_connect(changed => \&newgen_osfam, $gui{comboboxNewOSVer}),
               ver          => $gui{comboboxNewOSVer}->signal_connect(changed => \&newgen_osver, $gui{comboboxNewOSFam}),
               famedit      => $gui{comboboxEditGenOSFam}->signal_connect(changed => \&editgen_osfam, $gui{comboboxEditGenOSVer}),
               veredit      => $gui{comboboxEditGenOSVer}->signal_connect(changed => \&editgen_osver, $gui{comboboxEditGenOSFam}),
               audiodrv     => $gui{comboboxEditAudioDriver}->signal_connect(changed => \&editaudio_driver),
               audioctr     => $gui{comboboxEditAudioCtr}->signal_connect(changed => \&editaudio_ctr),
               netadaptype1 => $gui{comboboxEditNetType1}->signal_connect(changed => \&editnet_adapter_type, $gui{checkbuttonEditNetEnable1}),
               netadaptype2 => $gui{comboboxEditNetType2}->signal_connect(changed => \&editnet_adapter_type, $gui{checkbuttonEditNetEnable2}),
               netadaptype3 => $gui{comboboxEditNetType3}->signal_connect(changed => \&editnet_adapter_type, $gui{checkbuttonEditNetEnable3}),
               netadaptype4 => $gui{comboboxEditNetType4}->signal_connect(changed => \&editnet_adapter_type, $gui{checkbuttonEditNetEnable4}),
               netattach1   => $gui{comboboxEditNetAttach1}->signal_connect(changed => \&editnet_attach, $gui{checkbuttonEditNetEnable1}),
               netattach2   => $gui{comboboxEditNetAttach2}->signal_connect(changed => \&editnet_attach, $gui{checkbuttonEditNetEnable2}),
               netattach3   => $gui{comboboxEditNetAttach3}->signal_connect(changed => \&editnet_attach, $gui{checkbuttonEditNetEnable3}),
               netattach4   => $gui{comboboxEditNetAttach4}->signal_connect(changed => \&editnet_attach, $gui{checkbuttonEditNetEnable4}),
               netname1     => $gui{comboboxEditNetName1}->signal_connect(changed => \&editnet_name, $gui{checkbuttonEditNetEnable1}),
               netname2     => $gui{comboboxEditNetName2}->signal_connect(changed => \&editnet_name, $gui{checkbuttonEditNetEnable2}),
               netname3     => $gui{comboboxEditNetName3}->signal_connect(changed => \&editnet_name, $gui{checkbuttonEditNetEnable3}),
               netname4     => $gui{comboboxEditNetName4}->signal_connect(changed => \&editnet_name, $gui{checkbuttonEditNetEnable4}),
               stortype     => $gui{comboboxEditStorCtrType}->signal_connect(changed => \&editstor_ctrtype));

# Sensivity groups for enabling/disabling different parts of the gui
our %sensgrp = (connect     => [$gui{menuitemNew},
                                $gui{menuitemVMM},
                                $gui{menuitemServerInfo},
                                $gui{menuitemVBPrefs},
                                $gui{toolbuttonNew},
                                $gui{toolbuttonRefresh}],
                running     => [$gui{menuitemStop},
                                $gui{menuitemPause},
                                $gui{menuitemReset},
                                $gui{menuitemRemoteDisplay},
                                $gui{menuitemKeyboard},
                                $gui{toolbuttonStop},
                                $gui{toolbuttonReset},
                                $gui{toolbuttonRemoteDisplay},
                                $gui{buttonShowDetails},
                                $gui{buttonRefreshSnapshots},
                                $gui{buttonTakeSnapshot}],
                saved       => [$gui{menuitemStart},
                                $gui{toolbuttonStart},
                                $gui{buttonShowDetails},
                                $gui{buttonRefreshSnapshots},
                                $gui{buttonTakeSnapshot}],
                poweroff    => [$gui{menuitemStart},
                                $gui{menuitemSettings},
                                $gui{menuitemDelete},
                                $gui{toolbuttonStart},
                                $gui{toolbuttonSettings},
                                $gui{buttonSaveDescription},
                                $gui{buttonShowDetails},
                                $gui{buttonRefreshSnapshots},
                                $gui{buttonTakeSnapshot}],
                paused      => [$gui{menuitemResume},
                                $gui{menuitemRemoteDisplay},
                                $gui{toolbuttonRemoteDisplay},
                                $gui{buttonShowDetails}],
                unselected  => [$gui{menuitemStart},
                                $gui{menuitemStop},
                                $gui{menuitemPause},
                                $gui{menuitemResume},
                                $gui{menuitemSettings},
                                $gui{menuitemReset},
                                $gui{menuitemDelete},
                                $gui{menuitemRemoteDisplay},
                                $gui{menuitemKeyboard},
                                $gui{toolbuttonStart},
                                $gui{toolbuttonStop},
                                $gui{toolbuttonSettings},
                                $gui{toolbuttonRemoteDisplay},
                                $gui{toolbuttonReset},
                                $gui{buttonShowDetails},
                                $gui{buttonRefreshSnapshots},
                                $gui{buttonSaveDescription},
                                $gui{buttonTakeSnapshot}],
                stor_unsel  => [$gui{buttonEditStorAddAttach},
                                $gui{buttonEditStorRemoveAttach},
                                $gui{buttonEditStorRemoveCtr},
                                $gui{comboboxEditStorCtrType},
                                $gui{checkbuttonEditStorCache}],
                ctr_sel_on  => [$gui{buttonEditStorAddAttach},
                                $gui{buttonEditStorRemoveCtr},
                                $gui{comboboxEditStorCtrType},
                                $gui{checkbuttonEditStorCache}],
                ctr_sel_off => [$gui{buttonEditStorAddAttach},
                                $gui{buttonEditStorRemoveCtr},
                                $gui{comboboxEditStorCtrType},
                                $gui{checkbuttonEditStorCache}],
                usb_sel     => [$gui{buttonEditUSBEdit},
                                $gui{buttonEditUSBRemove},
                                $gui{buttonEditUSBUp},
                                $gui{buttonEditUSBDown}],
                snap        => [$gui{buttonRestoreSnapshot},
                                $gui{buttonDeleteSnapshot},
                                $gui{buttonDetailsSnapshot}],
                vbprefsnet  => [$gui{buttonVBPrefsDelNet},
                                $gui{buttonVBPrefsEditNet}]);

our (%hostspec);

#Glib->install_exception_handler (\&gui_ex_handler);

# Simple GUI subs
sub sensitive_on() { $_->set_sensitive(1) foreach (@_);}
sub sensitive_off() { $_->set_sensitive(0) foreach (@_);}
sub hide_on() { $_->hide foreach (@_); }
sub hide_off() { $_->show foreach (@_); }

# Set the transient window's (ie parent window) sensitivity on
sub transwin_sens_on() {
    my ($window) = @_;
    my $transientwin = $window->get_transient_for();
    $transientwin->set_sensitive(1) if ($transientwin);
}

# Set the transient window's (ie parent window) sensitivity off
sub transwin_sens_off() {
    my ($window) = @_;
    my $transientwin = $window->get_transient_for();
    $transientwin->set_sensitive(0) if ($transientwin);
}

# Ghosts window and optionally sets pointer
sub busy_window() {
    my ($window, $sens, $pointer) = @_;
    $window->set_sensitive($sens);

    if ($pointer) { eval { $window->window->set_cursor(Gtk2::Gdk::Cursor->new($pointer)); }; }
    else { eval{ $window->window->set_cursor(undef); }; }

    Gtk2->main_iteration() while Gtk2->events_pending();
}

sub show_remotefilechooser_window() {
    $gui{dialogRemoteFileChooser}->set_title("Choose file on $endpoint...");
    my $IAppliance = IVirtualBox_createAppliance($gui{websn});
    $gui{IVFSExplorer} = IAppliance_createVFSExplorer($IAppliance, "file://$hostspec{machinedir}");
    &fill_list_remotefiles($hostspec{machinedir});
    my $response = $gui{dialogRemoteFileChooser}->run();
    $gui{dialogRemoteFileChooser}->hide();
    IManagedObjectRef_release($gui{IVFSExplorer});
    IManagedObjectRef_release($IAppliance);

    if ($response eq 'ok') {
        my %file = &getsel_list_remotefiles();
        my $location = $gui{entryRemoteFileChooserLocation}->get_text();
        $location = "$location/$file{FileName}" if ($file{FileName});
        &vmm_add($location);
    }
}

sub refresh_remotefilechooser() { &fill_list_remotefiles($gui{entryRemoteFileChooserLocation}->get_text()); }

sub cdup_remotefilechooser() {
    IVFSExplorer_cdUp($gui{IVFSExplorer});
    &fill_list_remotefiles(IVFSExplorer_getPath($gui{IVFSExplorer}));
}

sub show_progress_window() {
    my ($IProgress, $msg, $cancel, $transwin) = @_;
    my $resultcode = 0;
    my $timer = 0;
    $gui{labelProgress}->set_text($msg);
    $gui{dialogProgress}->set_title($msg);
    $gui{dialogProgress}->set_transient_for($transwin) if ($transwin);
    $gui{progressbar}->set_fraction(0);

    if ($cancel) { &hide_off($gui{buttonProgressCancel}); }
    else { &hide_on($gui{buttonProgressCancel}); }

    $timer = Glib::Timeout->add(1000,
        sub {
            my $percent = IProgress_getPercent($IProgress);

            if (IProgress_getCompleted($IProgress) eq 'true') {
                Glib::Source->remove($timer);
                $timer = 0;
                $gui{progressbar}->set_fraction(1.00);
                Gtk2->main_iteration() while Gtk2->events_pending();
                $resultcode = IProgress_getResultCode($IProgress);
                $gui{dialogProgress}->response('ok');
                return 0;
            }
            else {
                $gui{progressbar}->set_fraction($percent * 0.01);
                return 1;
            }
        });

    $gui{dialogProgress}->run();
    $gui{dialogProgress}->hide();
    Glib::Source->remove($timer) if ($timer);
    return $resultcode;
}

# Works around a 'quirk/bug' in VB where the IProgress is destroyed as the
# session is automatically closed meaning we can't use normal methods for
# monitoring operations
sub show_progress_window2() {
    my ($IProgress, $msg, $transwin, $IMachine, $finalstate) = @_;
    my $timer = 0;
    $gui{dialogProgress2}->set_title($msg);
    $gui{dialogProgress2}->set_transient_for($transwin) if ($transwin);
    $gui{progressbar2}->set_text($msg);

    $timer = Glib::Timeout->add(500,
        sub {
            if (IMachine_getState($IMachine) eq $finalstate) {
                Glib::Source->remove($timer);
                $timer = 0;
                $gui{dialogProgress2}->response('ok');
                return 0;
            }
            else {
                $gui{progressbar2}->pulse();
                return 1;
            }
        });

    $gui{dialogProgress2}->run();
    $gui{dialogProgress2}->hide();
    Glib::Source->remove($timer) if ($timer);
}

sub validate_name() {
    my ($entry, $char, $len, $pos) = @_;
    $char =~ s/[\?\/\;\*\\\<\>\|\.]//; # Strip these chars
    return $char, $pos;
}

sub validate_hex() {
    my ($entry, $char, $len, $pos) = @_;
    $char =~ s/[^A-F0-9a-f]//; # Strip everything but these chars
    return $char, $pos;
}

sub validate_port() {
    my ($entry, $char, $len, $pos) = @_;
    $char =~ s/[^0-9,-]//; # Strip everything but these chars
    return $char, $pos;
}

sub validate_number() {
    my ($entry, $char, $len, $pos) = @_;
    $char =~ s/[^0-9]//; # Strip everything but these chars
    return $char, $pos;
}

sub validate_ip() {
    my ($entry, $char, $len, $pos) = @_;
    $char =~ s/[^0-9,.]//; # Strip everything but these chars
    return $char, $pos;
}

sub validate_ipv6() {
    my ($entry, $char, $len, $pos) = @_;
    $char =~ s/[^A-F0-9a-f,:]//; # Strip everything but these chars
    return $char, $pos;
}

sub gui_ex_handler {
    &show_err_msg('guiexception', $gui{messagedialogError}, "This appears to be a bug in RemoteBox. Unfortunately RemoteBox will be terminated when the dialog closes:\n\n @_");
    &quit_remotebox();
    0; # Remove after running once, we've probably quit before here anyway
}

1;
