#!/usr/bin/perl

###############################################################################
# This file is part of Ásbrú Connection Manager
#
# Copyright (C) 2017-2019 Ásbrú Connection Manager team (https://asbru-cm.net)
# Copyright (C) 2010-2016 David Torrejón Vaquerizas
#
# Ásbrú Connection Manager 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 3 of the License, or
# (at your option) any later version.
#
# Ásbrú Connection Manager 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 version 3
# along with Ásbrú Connection Manager.
# If not, see <http://www.gnu.org/licenses/gpl-3.0.html>.
###############################################################################

use utf8;
binmode STDOUT,':utf8';
binmode STDERR,':utf8';

$|++;

# Execute this code before anything else, even loading modules
BEGIN {
    if (grep({ /^-{1,2}(help|h)$/gi } @ARGV)) {
        print "\n";
        print "Usage: $0 [options]\n";
        print "Options:\n";
        print "\t--help : show this message\n";
        print "\t--config-dir=path : absolute '/path' or relative 'path' to ~/.config/\n";
        print "\t--no-backup : do no create alternative config files as a backup (faster shutdown)\n";
        print "\t--start-shell : start a local terminal\n";
        print "\t--password=<pwd> : automatically logon with given password without prompting user\n";
        print "\t--start-uuid=<uuid>[:<cluster] : start connection in cluster (if given)\n";
        print "\t--edit-uuid=<uuid> : edit connection\n";
        print "\t--dump-uuid=<uuid> : dump data for given connection\n";
        print "\t--scripts : open scripts window\n";
        print "\t--start-script=<script> : start given script\n";
        print "\t--preferences : open global preferences dialog\n";
        print "\t--quick-conn : open the Quick Connect dialog on startup\n";
        print "\t--list-uuids : list existing connections/groups and their UUIDs\n";
        print "\t--no-splash : no splash screen on startup\n";
        print "\t--iconified : go to tray once started\n";
        print "\t--readonly : start in read only mode (no config changes allowed)\n";
        print "\n";
        print "See 'man asbru' for additional information.\n";
        print "\n";
        exit 0;
    }

    # Check if user wants to use a different configuration directory
    foreach my $arg (@ARGV) {
        if ($arg =~ /^-{1,2}config-dir=\s*(.+)$/i) {
            my $path = $1;
            # Check if configuration directory is an absolute (starts with /) or relative path (does not starts with /)
            if ($path =~ m|^/|) {
                # Absolute
                $ENV{"ASBRU_CFG"} = $path;
            } else {
                # Relative path to ~./config
                $ENV{"ASBRU_CFG"} = "$ENV{'HOME'}/.config/$path";
            }
        } elsif ($arg !~ /-{1,2}[\w\-]+/i) {
            print STDERR "INFO: bad start up parameter $arg\n";
        }
    }
    if (!$ENV{"ASBRU_CFG"}) {
        ###################################################################
        # DEFAULT CONFIG DIR SHOULD BE DEFINED HERE
        ###################################################################
        $ENV{"ASBRU_CFG"} = "$ENV{'HOME'}/.config/pac";
    }
    # Workaround to unexpected SSH disconnections due to wrong handling of the WINCH event in some Gnome versions
    # See https://www.perlmonks.org/?node_id=11109454
    delete $ENV{'GTK_OVERLAY_SCROLLING'};
}

###################################################################
# START: Import Modules

use FindBin qw ($RealBin $Bin $Script);
use lib "$RealBin/lib", "$RealBin/lib/ex", "$RealBin/lib/edit";
use File::Copy;
use YAML qw (LoadFile);
use PACMain;

# Standard
use strict;
use warnings;

# END: Import Modules
###################################################################

###################################################################
# START: Define GLOBAL variables
our $CFG_FILE_NAME  = "pac.yml";
our $INIT_CFG_FILE  = "$RealBin/res/$CFG_FILE_NAME";
our $CFG_DIR        = $ENV{"ASBRU_CFG"};
our $CFG_DIR_OLD    = "";
our $CFG_FILE       = "$CFG_DIR/$CFG_FILE_NAME";
our $BACKUP_DIR     = "bak";
our $ASBRU;
# END: Define GLOBAL variables
###################################################################

###################################################################

# START: MAIN program
# Load PACMain now, so it can read the global $CFG_DIR
print "INFO: Ásbrú started ($Script) with PID $$\n";

# Check for Unity's systray-whitelist presence of 'asbru'
my $wl = `gsettings get com.canonical.Unity.Panel systray-whitelist 2>/dev/null`;
chomp $wl;

if (($? eq 0) && (!grep(/'asbru'/, $wl))) {
    print "INFO: Adding 'asbru' to Unity's 'systray-whitelist'\n";
    $wl =~ s/'\s*]/', 'asbru']/;
    `gsettings set com.canonical.Unity.Panel systray-whitelist "$wl"`;
}

# Set up configuration directories

# Create config dir if necessary
if (!-d "$ENV{'HOME'}/.config") {
    mkdir( "$ENV{'HOME'}/.config" );
}

# Code for future configuration migrations when needed
if (($CFG_DIR_OLD) && ($CFG_DIR_OLD ne $CFG_DIR)) {
    config_migration($CFG_DIR_OLD,$CFG_DIR);
}

# Create Asbru config dir if necessary
if (!-d $CFG_DIR) {
    mkdir($CFG_DIR);
}

# Create all subdirectories
foreach my $subdir ('autostart','screenshots','session_logs','scripts','sockets','tmp','bak') {
    if (!-d "$CFG_DIR/$subdir") {
        mkdir("$CFG_DIR/$subdir");
    }
}

# Copy sample files if they do not exist
for (my $n = 1; $n < 5; $n++) {
    if ((!-e "$CFG_DIR/scripts/sample$n.pl") && (-e "$RealBin/res/sample$n.pl")) {
        copy("$RealBin/res/sample$n.pl", "$CFG_DIR/scripts/");
    }
}

getDesktopEnvironment();

# Check if configuration is valid
CheckConfigurationFile();

# Start Àsbrú Connection Manager :)
if (!($ASBRU = PACMain -> new(@ARGV))) {
    print STDERR "WARN: Ásbrú aborted.\n";
    exit 0
}

$ASBRU->start;

print "INFO: Finished $Script (PID:$$)...\n";
exit 0;

# Implemented when necessary
sub config_migration {
    my ($old_dir,$new_dir) = @_;

    return 0;
}

sub getDesktopEnvironment {
    my $c;
    # An attempt to find desktop environment
    foreach my $d ('gnome-shell','cinnamon','lxde','xfce') {
        $c = `pgrep -c '$d'`;
        chop $c;
        #print STDERR "TEST: pgrep -c '$d' = $c\n";
        if ($c) {
            $ENV{'ASBRU_DESKTOP'} = $d;
            last;
        }
    }
    if ($ENV{'XDG_SESSION_DESKTOP'} eq 'xubuntu') {
        $ENV{'ASBRU_DESKTOP'} .= ':xubuntu';
    }
    if (!$ENV{'ASBRU_DESKTOP'}) {
        $ENV{'ASBRU_DESKTOP'} = 'other';
    } elsif ($ENV{'ASBRU_DESKTOP'} eq 'gnome-shell') {
        # Test to see it has the unite extension installed
        if (-e "$ENV{'HOME'}/.local/share/gnome-shell/extensions/unite\@hardpixel.eu") {
            $ENV{'ASBRU_DESKTOP'} .= '-withtray';
        }
    }
    print STDERR "INFO: Desktop environment detected : $ENV{'ASBRU_DESKTOP'}\n";
}

sub CheckConfigurationFile {
    use POSIX qw(strftime);

    my $nfreeze = "$CFG_DIR/pac.nfreeze";
    my $now = strftime "%Y%m%d%H%M%S", localtime;
    my $BACKUP_CFG_FILE  = "$CFG_DIR/$BACKUP_DIR/$CFG_FILE_NAME";
    my $MAX_BACKUP_FILES = 10;
    my $cfg;

    if (!-e $CFG_FILE) {
        # No configuration file, do not check anyting, it's probably the first time we start Ásbrú
        return;
    }
    eval {
        $cfg = YAML::LoadFile($CFG_FILE);
    };
    if ($@ || !$cfg) {
        my $OK = 0;
        print "INFO: Config file lost or corrupted, trying to recover a previous configuration file...\n";
        # Remove freeze file
        unlink $nfreeze;
        # Find the last good configuration
        for (my $n=0; $n < $MAX_BACKUP_FILES; $n++) {
            if (!-e "$BACKUP_CFG_FILE.$n") {
                next;
            }
            print "INFO: Testing recovery file $CFG_FILE_NAME.$n...\n";
            eval {
                $cfg = YAML::LoadFile("$BACKUP_CFG_FILE.$n");
            };
            if (!$@ && $cfg) {
                print "INFO: Found valid file $CFG_FILE_NAME.$n, recovering...\n";
                system "mv -f $CFG_FILE $BACKUP_CFG_FILE.$now.bad";
                system "cp -fp $BACKUP_CFG_FILE.$n $CFG_FILE";
                $OK = 1;
                last;
            } else {
                system "mv -f $BACKUP_CFG_FILE.$n $BACKUP_CFG_FILE.$n.$now.bad";
            }
        }
        if (!$OK) {
            die "ERROR: You have no valid configuration file to load!\nERROR: Please inspect $CFG_FILE manually.";
        }
    } elsif ((-e $nfreeze) && (-s $nfreeze == 0)) {
        # nfreeze corrupted remove so it can be recreated
        print "INFO: file.nfreeze lost on last session, recreating\n";
        unlink $nfreeze;
    }

    # Backup file already exists, roll previous backups
    if (-e "$BACKUP_CFG_FILE.0") {
        for (my $n=$MAX_BACKUP_FILES-1; $n > 0; $n--) {
            my $p = $n-1; # previous
            if (-e "$BACKUP_CFG_FILE.$p") {
                system "cp -fp $BACKUP_CFG_FILE.$p $BACKUP_CFG_FILE.$n";
            }
        }
    }
    # Save current known configuration
    system "cp -fp $CFG_FILE $BACKUP_CFG_FILE.0";
}

END {
    $ENV{"ASBRU_CFG"} = '';
}

# END: MAIN program
###################################################################

__END__

=encoding utf8

=head1 NAME

asbru-cm

=head1 SYNOPSYS

asbru-cm [options]

B<Options>

    --help                          : show this message
    --config-dir=path               : absolute '/path' or relative 'path' to ~/.config/
    --no-backup                     : do no create alternative config files as a backup (faster shutdown)
    --start-shell                   : start a local terminal
    --password=<pwd>                : automatically logon with given password without prompting user
    --start-uuid=<uuid>[:<cluster]  : start connection in cluster (if given)
    --edit-uuid=<uuid>              : edit connection
    --dump-uuid=<uuid>              : dump data for given connection
    --scripts                       : open scripts window
    --start-script=<script>         : start given script
    --preferences                   : open global preferences dialog
    --quick-conn                    : open the Quick Connect dialog on startup
    --list-uuids                    : list existing connections/groups and their UUIDs
    --no-splash                     : no splash screen on startup
    --iconified                     : go to tray once started
    --readonly                      : start in read only mode (no config changes allowed)

=head1 DESCRIPTION

=head2 Global Variables

I<$ENV{"ASBRU_CFG"}>    Setup your default configuration directory here.

You may run different versions of Ásbru and each have different configuration settings and connections.

=head2 Functions

C<sub config_migration> (no parameters) (no return values)

Function normally empty unless there is a migration of the configuration files.

C<sub getDesktopEnvironment> (no parameters)

Attempts to set $ENV{'ASBRU_DESKTOP'} to the current Desktop Environment name

Used to deal with limitations on certain desktop environments.

For example: gnome-shell at the time of this documentation has no Tray capability in Gtk3

C<sub CheckConfigurationFile> (no parameters)

Check that the configuration file is valid.  If the valid is invalid, try to recover a valid
configuration file from a previous version.

If configuration is valid, make a copy of the file into the backup directory.
