#!/usr/bin/perl

use strict;
use warnings;
use FindBin qw( $Bin );
use lib "$Bin/../../lib";
use POSIX qw( strftime );
use Data::Dumper;
use File::Path qw( make_path remove_tree );
use File::Copy qw( copy move );
use DBI;
use App::POS::DB::Postgres;
use App::POS::Config;
use App::POS::Utils qw( date_based_on_closing_hour );
use Cwd;

# change when done
my $BACKUP_DESTINATION = shift;
my $DONT_INCLUDE_AREAS = shift;
my $FROM_HARDLOCK = shift;

die "Usage: $0 <dest_dir> [<dont_include_areas>]\n" unless defined $BACKUP_DESTINATION;

make_path($BACKUP_DESTINATION) unless -d $BACKUP_DESTINATION;

# when not running from hardlock backup, backup EVERYTHING
if (! $FROM_HARDLOCK) {
  print STDERR "Dumping database...\n";
  unlink "/opt/pos/db.tgz";
  #system("pg_dump -Fc -U root pos > /opt/pos/db.tgz");
  system("pg_dump -U root pos | gzip -9 > /opt/pos/db.tgz");

  print STDERR "Copying software...\n";
  my $cur_date = &strftime("%Y%m%d_%H%M%S", localtime);

  if (defined $DONT_INCLUDE_AREAS && $DONT_INCLUDE_AREAS) {
    system("cd /opt/pos ; zip -x \"common/data/*.pdf\" -x \"server/data/areas/*/*\" -q -r $BACKUP_DESTINATION/backupv2_$cur_date.zip .");
  }
  else {
    system("cd /opt/pos ; zip -x \"common/data/*.pdf\" -q -r $BACKUP_DESTINATION/backupv2_$cur_date.zip .");
  }

  unlink "/opt/pos/db.tgz";

  print STDERR "Done.\n";

  exit;
}

if (! -e "/opt/pos/.business_date") {
  die "Can't find /opt/pos/.business_date\n";
}

my $CFG = App::POS::Config->new( file => "/opt/pos/etc/config.ini" );
my $CONFIG = $CFG->load_fixed();

my $CLOSE_HOUR = $CONFIG->{END_BUSINESS_HOUR} || "05:00";

if ($CLOSE_HOUR =~ /^\d+:\d+$/) {
  $CLOSE_HOUR = $CLOSE_HOUR.":00";
}

my $time_str = &strftime("%Y-%m-%d %H:%M:%S", localtime);
my $biz_date = date_based_on_closing_hour($time_str, $CLOSE_HOUR);
my @toks = split /\-/, $biz_date;
my $CUR_MASK = $toks[0].$toks[1];

my $DATABASE = "pos";
my $TMP_DIR = "/opt/pos_backup";
#my $TMP_DIR = "/home/mmanso/pos_backup";

my %PARTIAL_TABLES = (
  sales_headers => 1,
  sales_payments => 1,
  sales_hashes_products => 1,
  sales_hashes => 1,
  sales_details => 1,
);

#############################################
## BEGIN
#############################################

my $dsn = 'DBI:Pg:dbname='.$DATABASE.';host=127.0.0.1';
my $user = 'root';
my $passwd = '';

my $db = App::POS::DB::Postgres->new(dbh => DBI->connect($dsn,$user,$passwd,{}));

my @tables = $db->select("SELECT table_name FROM information_schema.tables WHERE table_schema = 'public'");

my %existing_masks = ();

foreach my $table (@tables) {
  foreach my $part (keys %PARTIAL_TABLES) {
    if ($table->{table_name} =~ /^$part/) {
      if (my( $mask ) = $table->{table_name} =~ /(\d\d\d\d\d\d)$/) {
        $existing_masks{ $mask } = 1;
      }
    }
  }
}

# clean tmp backup dir, if exists
remove_tree($TMP_DIR) if -d $TMP_DIR;

# copy files
&copy_files();

# historial
foreach my $k (keys %existing_masks) {
  if ($k ne $CUR_MASK) {
    &backup_particular_mask( $k, \%existing_masks, 1 );
  }
}

# actual
&backup_particular_mask( $CUR_MASK, \%existing_masks, 0 );

# generate final ZIP file
my $cur_dir = getcwd();
my $cur_date = &strftime("%Y%m%d_%H%M%S", localtime);
chdir($TMP_DIR);
system("zip -q -r /opt/backupv2_$cur_date.zip *");

{
  local $SIG{ALRM} = sub { die "alarm\n" };

  eval {
    alarm(900); # 900 seconds
    copy( "/opt/backupv2_$cur_date.zip", "$BACKUP_DESTINATION/backupv2_$cur_date.zip" );
    alarm(0);
  };

  if ($@) {
    print STDERR "### TIMEOUT!!!\n";
  }
}


unlink "/opt/backupv2_$cur_date.zip";
chdir($cur_dir);

# clean tmp backup dir, if exists
remove_tree($TMP_DIR) if -d $TMP_DIR;

#
# Functions
#

sub backup_particular_mask {
  my( $mask, $existing_masks, $is_archive ) = @_;

  my $destination = "";
  my $backup_name = "";

  if ($is_archive) {
    $destination = "$BACKUP_DESTINATION/archive";
    $backup_name = "$mask.dump";

    # ignore already archived months
    return if -e "$destination/$backup_name";

    print STDERR "backup_particular_mask > $mask > $destination/$backup_name\n";
  }
  else {
    $destination = "$TMP_DIR/server/data/database";
    $backup_name = "db.dump";
    print STDERR "backup_particular_mask > $mask\n";
  }

  make_path($destination) unless -d $destination;

  my $exclude_string = &build_exclude_string($existing_masks, $mask);

  print STDERR "  #####> $mask - $destination/$backup_name\n";

  #my $command = "pg_dump -Fc -U root $exclude_string $DATABASE > \"$destination/$backup_name\"";
  my $command = "pg_dump -U root $exclude_string $DATABASE | gzip -9 > \"$destination/$backup_name\"";
  system($command);
}

sub copy_files {
  make_path("$TMP_DIR/etc");
  make_path("$TMP_DIR/etc/machine");
  make_path("$TMP_DIR/server/data/database");
  make_path("$TMP_DIR/common/data");
  make_path("$TMP_DIR/common/data/custom_scripts");
  make_path("$TMP_DIR/common/print_templates");
  make_path("$TMP_DIR/common/bin");
  make_path("$TMP_DIR/lib");
  make_path("$TMP_DIR/common/addons/graphical_display");
  make_path("$TMP_DIR/common/skins/".$CONFIG->{LANG});

  &copy( "/opt/pos/common/skins/".$CONFIG->{LANG}."/main.png", "$TMP_DIR/common/skins/".$CONFIG->{LANG} );
  &copy( "/opt/pos/common/skins/".$CONFIG->{LANG}."/login.png", "$TMP_DIR/common/skins/".$CONFIG->{LANG} );
  &copy( "/opt/pos/common/skins/".$CONFIG->{LANG}."/checks.png", "$TMP_DIR/common/skins/".$CONFIG->{LANG} );

  system("cp -r /opt/pos/common/addons/graphical_display/* \"$TMP_DIR/common/addons/graphical_display\"");

  system("cp -r /opt/pos/common/data/custom_scripts/* \"$TMP_DIR/common/data/custom_scripts\"")
    if -d "/opt/pos/common/data/custom_scripts";

  system("cp -r /opt/pos/lib/* \"$TMP_DIR/lib\"");

  &copy( "/opt/pos/common/bin/pos", "$TMP_DIR/common/bin/pos" );
  &copy( "/opt/pos/version", "$TMP_DIR/version" );

  &copy( "/root/weckOFFICE/etc/config.xml", "$TMP_DIR/etc" )
    if -e "/root/weckOFFICE/etc/config.xml";

  &copy( "/opt/pos/common/bin/external_ticket_scale.custom", "$TMP_DIR/common/bin" )
    if -e "/opt/pos/common/bin/external_ticket_scale.custom";

  &copy( "/etc/X11/xorg.conf", "$TMP_DIR/etc/machine" )
    if -e "/etc/X11/xorg.conf";

  &copy( "/etc/default/grub", "$TMP_DIR/etc/machine" )
    if -e "/etc/default/grub";

  &copy( "/etc/rc.local", "$TMP_DIR/etc/machine" )
    if -e "/etc/rc.local";

  &copy( "/root/.fluxbox/startup", "$TMP_DIR/etc/machine" )
    if -e "/root/.fluxbox/startup";

  &copy( "/opt/pos/hardlock", "$TMP_DIR" )
    if -e "/opt/pos/hardlock";

  &copy( "/opt/pos/hardlock2", "$TMP_DIR" )
    if -e "/opt/pos/hardlock2";

  &copy( "/opt/pos/lic_validation.ini", "$TMP_DIR" )
    if -e "/opt/pos/lic_validation.ini";

  &copy( "/opt/bin/machine_calibrate", "$TMP_DIR/etc/machine" )
    if -e "/opt/bin/machine_calibrate";

  &copy( "/opt/pos/hardlock", "$TMP_DIR/hardlock" )
    if -e "/opt/pos/hardlock";

  system("cp -r /opt/pos/*.png \"$TMP_DIR\" 2> /dev/null");
  system("cp -r /opt/pos/common/print_templates/* \"$TMP_DIR/common/print_templates\"");
  system("cp -r /opt/pos/common/data/* \"$TMP_DIR/common/data\"");
  system("cp -r /opt/pos/server/data/* \"$TMP_DIR/server/data\"");
  system("rm -rf \"$TMP_DIR/common/data/sales_*.zip\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/backup_*.zip\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/backupv2_*.zip\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/custom_scales\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/DB_*.xls\" 2> /dev/null");
  system("cp -r /opt/pos/etc/* \"$TMP_DIR/etc\" 2> /dev/null");
  unlink "$TMP_DIR/etc/network_autodiscovery.txt";

  # don't save images main library, to keep backup lighter
  system("rm -rf \"$TMP_DIR/server/data/images\" 2> /dev/null");

  system("rm -rf \"$TMP_DIR/server/data/print_checks_archive\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/emails_send\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/emails_to_send\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/reports_to_network\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/saft_to_network\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/saft_to_network_daily\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/saft_to_network_monthly\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/saft_to_hdd\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/reports_to_network/daily/send_control\" 2> /dev/null");
  system("rm -rf \"$TMP_DIR/common/data/execution_control\" 2> /dev/null");

  if (defined $DONT_INCLUDE_AREAS && $DONT_INCLUDE_AREAS) {
    system("rm -rf \"$TMP_DIR/server/data/areas\" 2> /dev/null");
  }
}

sub build_exclude_string {
  my( $masks, $the_one_to_use ) = @_;

  my $str = "";

  foreach my $mask (keys %$masks) {
    next if $mask eq $the_one_to_use;

    foreach my $part (keys %PARTIAL_TABLES) {
      $str .= "--exclude-table=".$part."_".$mask." \\\n";
    }
  }

  $str =~ s/\s\\\n$//;

  return $str;
}

