#!/usr/bin/perl

use 5.24.0;
use strict;
use warnings;
use Carp qw/croak/;
use Cwd qw(cwd);
use File::Path;
use File::Copy;
use File::Basename;
use Getopt::Long	qw(:config bundling);
use FindBin '$RealBin';


#--------------------------------------------------------------------
#  DATA
#--------------------------------------------------------------------

my $printers = {
    'copy4b' => {
        'model'    => 'TA5007ci',
        'location' => 'Building B, 4th floor',
    },
    'copy3a' => {
        'model'    => 'TA4505ci',
        'location' => 'Building A, 3th floor kitchen',
    },
    'lj5c' => {
        'model'    => 'hplj600',
        'location' => 'Building C, 5th floor, next to copy',
    },
    'copy5b' => {
        'model'    => 'TA5007ci',
        'location' => 'Building B, 5th floor',
    },
    'lj4c' => {
        'model'    => 'hplj600',
        'location' => 'Building C, 4th floor, next to copy',
    },
    'lj4p' => {
        'model'    => 'hpljp4015x',
        'location' => 'Building B, 4th floor, next to copy',
    },
    'lj4a' => {
        'model'    => 'hplj600',
        'location' => 'Building A, 4th floor kitchen',
    },
    'copy4a' => {
        'model'    => 'TA4505ci',
        'location' => 'Building A, 4th floor kitchen',
    },
    'copy2a' => {
        'model'    => 'TA4505ci',
        'location' => 'Building A, 2nd floor kitchen',
    },
    'lj3a' => {
        'model'    => 'hplj600',
        'location' => 'Building A, 3th floor kitchen',
    },
    'lj2a' => {
        'model'    => 'hplj600',
        'location' => 'Building A, 2nd floor kitchen',
    },
    'copy5c' => {
        'model'    => 'TA4505ci',
        'location' => 'Building C, 5th floor',
    },
    'lj3b' => {
        'model'    => 'hpljp4515x',
        'location' => 'Building B, 3th floor, kitchen',
    },
    'lj-a302' => {
        'model'    => 'hplj600',
        'location' => 'Building A, 3th floor A302 LaSArIS',
    },
    'copy4c' => {
        'model'    => 'TA4505ci',
        'location' => 'Building C, 4th floor',
    },
    'lj2b' => {
        'model'    => 'hpljp3015',
        'location' => 'Building B, 2nd floor, kitchen',
    }
};

my $models = {
    'TA5007ci' => {
        'filters' => [
            '/usr/lib/cups/filter/kyofilter_pre_E',
            '/usr/lib/cups/filter/kyofilter_E',
        ],
    },
    'TA4505ci' => {
        'filters' => ['/usr/libexec/cups/filter/kyofilter_B',],
        'options' => ['Option19=One', 'Option21=True', 'Option17=DF730',],
    },
    'hplj600' => {
        'options' => ['HPOption_Duplexer=True',],
    }
};

#--------------------------------------------------------------------
#  Option processing
#--------------------------------------------------------------------

my $only_update = '';
my $only_usage = '';
my $only_cron = '';

GetOptions(
    'update'     => \$only_update,
    'cron'       => \$only_cron,
    'help|h|?'   => \$only_usage,
) or die;

if ($only_usage || (@ARGV != 1 && !$only_cron)) {
   usage();
}

check_env();

if ($only_cron) {
    check_cron();
    exit;
}

#--------------------------------------------------------------------
#  Helper functions
#--------------------------------------------------------------------

sub get_description {
    my ($printer_name) = @_;
    return "$printer_name (FI MUNI)";
}

sub prompt_username {
    print "[PROMPT] Enter username: ";
    chomp(my $username = <STDIN>);
    return $username;
}

sub prompt_password {
    print "[PROMPT] Enter password: ";
    system("stty -echo");
    chomp(my $password1 = <STDIN>);
    system("stty echo");
    print "\n[PROMPT] Repeat password: ";
    system("stty -echo");
    chomp(my $password2 = <STDIN>);
    system("stty echo");
    print "\n";

    if ($password1 ne $password2) {
        die "[ERROR] Passwords do not match!";
    }
    return $password1;
}

sub get_url {
    my ($printer_name) = @_;
    my $user           = prompt_username();
    my $password       = prompt_password();

    return "smb://NTFI\\${user}:${password}\@print.fi.muni.cz/$printer_name";
}

sub install_printer {
    my ($printer_name) = @_;
    my $url = get_url($printer_name);

    my $command = qq(lpadmin \\
    -p "${printer_name}.fi.muni" \\
    -D "${printer_name} (FI MUNI)" \\
    -L "$printers->{$printer_name}->{'location'}" \\
    -E \\
    -v "${url}" \\
    -P "ppds/${printer_name}.ppd" \\
    -o media-default=iso_a4_210x297mm \\
    -o sides-default=two-sided-long-edge \\
    -o auth-info-required=username,password);

    for my $option (
        @{$models->{$printers->{$printer_name}->{'model'}}->{'options'}})
    {
        $command .= " -o $option ";
    }

    system($command);

    return;
}

sub update_ppd {
	my ($printer_name) = @_;

	my $command = qq(lpadmin \\
	-p "${printer_name}.fi.muni" \\
	-P "ppds/${printer_name}.ppd" );

	system($command);

	return;
}

sub copy_filter {
    my ($path) = @_;

    my ($filter, $destination, $suffix) = fileparse($path);

    # my $destination = "/usr/lib/cups/filter/";

    if (!-d $destination) {
        my $dirs = eval { mkpath($destination) };
        die "[ERROR] Failed to create $destination $@\n" unless $dirs;
    }

    copy("filters/" . $filter, $destination)
      or die "[ERROR] Failed to copy $filter: $!\n";
    say "[OK] Copied $filter.";

    chmod 0755, $destination . $filter;

    return;
}

sub print_printers {
    say "Available printers:";
    printf "%-14s | %-12s| %s\n", "Printer name", "Model", "Location";
    say "-" x 70;
    for (sort keys %$printers) {
        printf "%-14s | %-12s| %s\n", $_, $printers->{$_}->{'model'},
          $printers->{$_}->{'location'};
    }
    return;
}

sub usage {
    say
      "Script for installing faculty printers on Linux via Samba print server.";
    say "Requires local CUPS server running and Samba client installed.";
    say "usage: $0 (--cron|[--update] <printer-name>)";
    say "Must be run as root.";
    say "Triumph-Adler 50007ci needs Python 2.7 as a dependency";
    say "and will install 2 additional python modules automatically.";
    say "Option --update only updates installed printer.";
    say "Option --cron only prompts for cron installation.";
    print "\n";
    print_printers();

    exit 1;
}

sub check_python {
    my $regex  = qr/^Python 2\.7*/;
    my $output = qx/python -V 2>&1/;

    if ($? == 0 && $output =~ $regex) {
        say "[OK] Python 2.7 is present.";
        return "python ";
    }

    say "[ERROR] Python 2.7 is not present";

    my $s_output = qx/python2 -V 2>&1/;
    if ($? == 0 && $s_output =~ $regex) {
        say "[OK] Python 2.7 is present as python2";
        return "python2 ";
    }

    die "[ERROR] Python 2.7 Not Found and is needed for TA5007ci\n";
}

sub python_package {
    my ($package_name, $python) = @_;

    say "[INFO] Installing $package_name...";
    my $status = chdir "$package_name";
    if ($status == 0) {
        die "[ERROR] folder $package_name is not present!";
    }
    my $output = system("${python} setup.py install");
    if ($? != 0) {
        die "[ERROR] $package_name exited with different exit code than 0!";
    }
    $status = chdir '..';
    if ($status == 0) {
        die "[ERROR] Could not return to the working directory!";
    }
    return;
}

sub pre_TA5007ci {
    my $python = check_python;
    python_package("PyPDF2-1.26.0", $python);
    python_package("reportlab",     $python);
    return;
}

sub check_cron {
    if (! -e "/etc/cron.weekly/fi_printers_cron") {
        print "[PROMPT] Do you want to enable automatic updates via cron? [Y/n] ";

        chomp(my $answer = <STDIN>);
        if ($answer eq 'Y' || $answer eq 'y' || $answer eq '') {
            print "[INFO] Installing cron.\n";

            copy("$RealBin/cron.pl", "/etc/cron.weekly/fi_printers_cron")
                or die "[ERROR] Could not copy cron script\n";
            chmod 0700, "/etc/cron.weekly/fi_printers_cron";
            say "[INFO] Cron installed";
        }
    } else {
        say "[INFO] Cron is already installed.";
    }
}

sub check_env {
    # Check for smbclient
    my $output = `which smbclient &>/dev/null`;
    if ($? != 0) {
        die "[ERROR] Samba client seems not to be installed. (smbclinet)";
    }

    # Check if we don't have Root
    die "[ERROR] Must be run with root privileges\n" if $> != 0;
}
#--------------------------------------------------------------------
#  Start of the script
#--------------------------------------------------------------------

my ($printer_name) = @ARGV;

if (not(exists($printers->{$printer_name}))) {
    say "[ERROR] Printer $printer_name is not supported.";
    print_printers;
    exit 1;
}


my $model = $printers->{$printer_name}->{'model'};
say "[INFO] Selected model is $model";

#TA5007ci requires python2 reportlab and PyPDF2
if ($model eq "TA5007ci") {
    pre_TA5007ci;
}

for my $filter (@{($models->{$model} // {})->{filters} // []}) {
    copy_filter($filter);
}
if ($only_update) {
	update_ppd("$printer_name");
	say "[INFO] Update of $printer_name is completed.";
}
else {
	install_printer("$printer_name");
	say "[INFO] Installation of $printer_name is completed.";
    check_cron();
}

