# The "Controller" tab to control the printer using serial / USB.
# This feature is rarely used. Much more often, the firmware reads the G-codes from a SD card.
# May there be multiple subtabs per each printer connected?

package Slic3r::GUI::Controller;
use strict;
use warnings;
use utf8;

use List::Util qw(any);
use Wx qw(wxTheApp :frame :id :misc :sizer :bitmap :button :icon :dialog);
use Wx::Event qw(EVT_CLOSE EVT_LEFT_DOWN EVT_MENU);
use base qw(Wx::ScrolledWindow Class::Accessor);

__PACKAGE__->mk_accessors(qw(_selected_printer_preset));

our @ConfigOptions = qw(bed_shape serial_port serial_speed);

sub new {
    my ($class, $parent) = @_;
    my $self = $class->SUPER::new($parent, -1, wxDefaultPosition, [600,350]);
    
    $self->SetScrollbars(0, 1, 0, 1);
    $self->{sizer} = my $sizer = Wx::BoxSizer->new(wxVERTICAL);
    
    # warning to show when there are no printers configured
    {
        $self->{text_no_printers} = Wx::StaticText->new($self, -1,
            "No printers were configured for USB/serial control.",
            wxDefaultPosition, wxDefaultSize);
        $self->{sizer}->Add($self->{text_no_printers}, 0, wxTOP | wxLEFT, 30);
    }
    
    # button for adding new printer panels
    {
        my $btn = $self->{btn_add} = Wx::BitmapButton->new($self, -1, Wx::Bitmap->new($Slic3r::var->("add.png"), wxBITMAP_TYPE_PNG),
            wxDefaultPosition, wxDefaultSize, Wx::wxBORDER_NONE);
        $btn->SetToolTipString("Add printer…")
            if $btn->can('SetToolTipString');
        
        EVT_LEFT_DOWN($btn, sub {
            my $menu = Wx::Menu->new;
            my %presets = map { $_->name => $_ } @{wxTheApp->presets->{printer}};
            
            # remove printers that already exist
            my @panels = $self->print_panels;
            delete $presets{$_} for map $_->printer_name, @panels;
            
            foreach my $preset_name (sort keys %presets) {
                my $preset = $presets{$preset_name};
                next if !$preset->dirty_config->serial_port;
                
                my $id = &Wx::NewId();
                $menu->Append($id, $preset_name);
                EVT_MENU($menu, $id, sub {
                    $self->add_printer($preset);
                });
            }
            $self->PopupMenu($menu, $btn->GetPosition);
            $menu->Destroy;
        });
        $self->{sizer}->Add($btn, 0, wxTOP | wxLEFT, 10);
    }
    
    $self->SetSizer($sizer);
    $self->SetMinSize($self->GetSize);
    #$sizer->SetSizeHints($self);
    
    EVT_CLOSE($self, sub {
        my (undef, $event) = @_;
        
        if ($event->CanVeto) {
            foreach my $panel ($self->print_panels) {
                if ($panel->printing) {
                    my $confirm = Wx::MessageDialog->new(
                        $self, "Printer '" . $panel->printer_name . "' is printing.\n\nDo you want to stop printing?",
                        'Unfinished Print', wxYES_NO | wxNO_DEFAULT | wxICON_QUESTION,
                    );
                    if ($confirm->ShowModal == wxID_NO) {
                        $event->Veto;
                        return;
                    }
                }
            }
        }
        foreach my $panel ($self->print_panels) {
            $panel->disconnect;
        }
        
        $event->Skip;
    });
    
    $self->Layout;
    
    return $self;
}

sub OnActivate {
    my ($self) = @_;
    
    # get all available presets
    my %presets = ();
    foreach my $preset (@{wxTheApp->presets->{printer}}) {
        $preset->load_config;
        next if !$preset->dirty_config->serial_port;
        $presets{$preset->name} = $preset;
    }
    
    # decide which ones we want to keep
    my %active = ();
    
    # keep the ones that are currently connected or have jobs in queue
    $active{$_} = 1 for map $_->printer_name,
        grep { $_->is_connected || @{$_->jobs} > 0 }
        $self->print_panels;
    
    if (%presets) {
        # if there are no active panels, use sensible defaults
        if (!%active && keys %presets <= 2) {
            # if only one or two presets exist, load them
            $active{$_} = 1 for keys %presets;
        }
        if (!%active) {
            # enable printers whose port is available
            my %ports = map { $_ => 1 } wxTheApp->scan_serial_ports;
            $active{$_} = 1
                for grep exists $ports{$presets{$_}->dirty_config->serial_port}, keys %presets;
        }
        if (!%active && $self->_selected_printer_preset) {
            # enable currently selected printer if it is configured
            $active{$self->_selected_printer_preset} = 1
                if $presets{$self->_selected_printer_preset};
        }
    }
    
    # apply changes
    for my $panel ($self->print_panels) {
        next if $active{$panel->printer_name};
        
        $self->{sizer}->DetachWindow($panel);
        $panel->Destroy;
    }
    $self->add_printer($presets{$_}) for sort keys %active;
    
    # show/hide the warning about no printers
    $self->{text_no_printers}->Show(!%presets);
    
    # show/hide the Add button
    $self->{btn_add}->Show(keys %presets != keys %active);
    
    $self->Layout;
    
    # we need this in order to trigger the OnSize event of wxScrolledWindow which
    # recalculates the virtual size
    Wx::GetTopLevelParent($self)->SendSizeEvent;
}

sub add_printer {
    my ($self, $preset) = @_;
    
    # check that printer doesn't exist already
    foreach my $panel ($self->print_panels) {
        if ($panel->printer_name eq $preset->name) {
            return $panel;
        }
    }
    
    my $printer_panel = Slic3r::GUI::Controller::PrinterPanel->new($self, $preset);
    $self->{sizer}->Prepend($printer_panel, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, 10);
    $self->Layout;
    
    return $printer_panel;
}

sub print_panels {
    my ($self) = @_;
    return grep $_->isa('Slic3r::GUI::Controller::PrinterPanel'),
        map $_->GetWindow, $self->{sizer}->GetChildren;
}

sub printing {
    my ($self) = @_;
    
    return any { $_->printing } $self->print_panels;
}

sub update_presets {
    my $self = shift;
    my ($group, $presets, $selected, $is_dirty) = @_;
    
    # update configs of currently loaded print panels
    foreach my $panel ($self->print_panels) {
        foreach my $preset (@$presets) {
            if ($panel->printer_name eq $preset->name) {
                my $config = $preset->config(\@ConfigOptions);
                $panel->config->apply($config);
            }
        }
    }
    
    $self->_selected_printer_preset($presets->[$selected]->name);
}

1;
