#!/usr/bin/perl

use strict;
use warnings;
use FindBin qw( $Bin );
use lib "$Bin/../../lib";
use Data::Dumper;
use App::POS::Config;
use App::POS::Machine::Server;
use App::POS::Machine::Station;
use App::POS::Hardware::Handlers qw( read_barcode read_scale files_monitor server_monitor );
use Proc::Launcher::Manager;
use File::Copy qw( move );
use File::Spec;

die "Needs to be run by root...\n" unless $ENV{USER} eq 'root';

my $kill = shift;

if (defined $kill) {
  print STDERR "Will kill it...\n";
  system("ps xa | grep \"perl /opt/pos/common/bin/devices_handler.pl\" | grep -v grep | awk '{print \$1;}' | xargs kill -TERM 2> /dev/null");
}

my @l = split /\n/, `ps xa | grep "perl /opt/pos/common/bin/devices_handler.pl" | grep -v grep`;

if (scalar @l > 1) {
  #print STDERR Dumper(\@l);
  die "Another instance of $0 is running already, aborting...\n";
}


my $monitor = Proc::Launcher::Manager->new( app_name  => 'pos' );

$SIG{CHLD} = 'IGNORE';
$SIG{INT}  = \&stop_program;
$SIG{HUP}  = \&stop_program;
$SIG{TERM} = \&stop_program;

# Handlers for devices that GENERATE data for pos

my %handlers = (
  DallasReaders    => 'read_dallas_reader',
  CardReaders      => 'read_magnetic_card',
  BarcodeReaders   => 'read_barcode',
  Scales           => 'read_scale',
  Printers         => 'printers_monitor_group',
  Queues           => 'queue_manager',
);

# touch the config, to prevent future dates problem
system("touch /opt/pos/etc/config.ini");

while (1) {
  eval {
    &run_main();
  };

  if ($@) {
    print STDERR "ERROR: $@\n";
    print STDERR "Will restart...\n";

    #ps xa | grep "Proc::Launcher" | grep -v grep | awk '{print $1}' | xargs kill -9 2> /dev/null
    my $out = `ps xa | grep "Proc::Launcher" | grep -v grep`;

    if ($out) {
      my @l = split /\n/, $out;
      for (@l) {
        s/^\s+//;
        my( $pid ) = $_ =~ /^(\d+)/;
        system("kill -9 $pid 2> /dev/null");
      }
    }

    undef $monitor;
    $monitor = Proc::Launcher::Manager->new( app_name  => 'pos' );
    sleep 5;
  }
}

sub run_main {
  if (! -d "/tmp/logs") {
    system("mkdir -p /tmp/logs");
  }

  # logs cleanup
  system("rm -rf /root/logs/*");

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

  my $machine = undef;

  if (exists $info->{STATION_NUMBER} && $info->{STATION_NUMBER} > 0) {
    $machine = App::POS::Machine::Station->new( config => $cfg );  
  }
  else {
    $machine = App::POS::Machine::Server->new( config => $cfg );  
  }

  if (! -d $machine->dir_tmp()) {
    system("mkdir -p ".$machine->dir_tmp());
  }

  my %handlers_group = ();

  foreach my $handler_name (keys %handlers) {
    if (exists $info->{Hardware} && exists $info->{Hardware}->{$handler_name}) {
      my $idx = 0;
      foreach my $device ( @{ $info->{Hardware}->{$handler_name}->{Device} } ) {
        my $num = $idx;

        $device->{Inactive} ||= 0;

        if (! $device->{Inactive}) {
          push @{ $handlers_group{ $handler_name } }, {
            num => $num,
            device => $device,
          };
        }
        else {
          print STDERR "$handler_name - Device $num (".$device->{Description}.") is inactive, ignoring...\n";
        }

        $idx++;
      }
    }
  }

  # Call the grouped processes
  foreach my $group (keys %handlers_group) {
    my $devices = $handlers_group{$group};
    my $function_name = $handlers{$group};

    my $start_method = "App::POS::Hardware::Handlers::$function_name";

    # it's here to allow the function call using the vbl
    no strict 'refs';

    $monitor->register(
      daemon_name  => $group,
      start_method => sub {
        &{"$start_method"}($group, $machine, $devices);
      },
    );
  }

  # Register a process to handle files from pos to devices
  $monitor->register(
    daemon_name  => 'FilesMonitor',
    start_method => sub {
      &files_monitor('FilesMonitor', $machine);
      #&files_monitor_new('FilesMonitor', $machine);
    },
  );

  #$monitor->register(
  #  daemon_name  => 'FilesChangesDaemon',
  #  start_method => sub {
  #    &files_changes_daemon('FilesChangesDaemon', $machine);
  #  },
  #);

  # Register a process to allow the station to monitor the server
  if (! $machine->is_server()) {
    $monitor->register(
      daemon_name  => 'ServerMonitor',
      start_method => sub {
        &server_monitor('ServerMonitor', $machine);
      },
    );
  }

  $monitor->start();

  while (1) {
    my $restart_file = $machine->server_dir_tmp()."/restart_hardware_handler#".$info->{STATION_NUMBER}.".txt";

    if (-e $restart_file) {
      print STDERR "Will restart...\n";
      unlink($restart_file);
      $monitor->force_stop();
      $monitor->start();
    }

    sleep 5;
  }
}

sub stop_program {
  print STDERR "##### stop_program()\n";
  $monitor->force_stop();
  exit;
}

