package App::POS::Config;

use strict;
use warnings;
use Config::General;
use FindBin qw( $Bin );
use File::Spec;
use Data::Dumper;
use POSIX qw( strftime );

use base qw( App::Class );

sub file {
  my $self = shift;
  @_ ? $self->{file} = shift : $self->{file};
}

sub load {
  my $self = shift;
  return $self->__cache() if $self->__cache();

  $self->debug("load");

  my $conf = Config::General->new($self->file());
  my %config = $conf->getall();

  if (
    exists $config{AREA} &&
    ! ref $config{AREA}
  ) {
    my $tmp = delete $config{AREA};
    push @{$config{AREA}}, $tmp;
  }

  if (
    exists $config{QUIZ_QUESTIONS} &&
    ! ref $config{QUIZ_QUESTIONS}
  ) {
    my $tmp = delete $config{QUIZ_QUESTIONS};
    push @{$config{QUIZ_QUESTIONS}}, $tmp;
  }

  if (
    exists $config{PRINTING_ZONE_LINK} &&
    ! ref $config{PRINTING_ZONE_LINK}
  ) {
    my $tmp = delete $config{PRINTING_ZONE_LINK};
    push @{$config{PRINTING_ZONE_LINK}}, $tmp;
  }

  if (
    exists $config{QUEUES} &&
    ! ref $config{QUEUES}
  ) {
    my $tmp = delete $config{QUEUES};
    push @{$config{QUEUES}}, $tmp;
  }

  if (
    exists $config{ORDER_SEPARATORS} &&
    ! ref $config{ORDER_SEPARATORS}
  ) {
    my $tmp = delete $config{ORDER_SEPARATORS};
    push @{$config{ORDER_SEPARATORS}}, $tmp;
  }

  if (
    exists $config{PRINT_RECEIPT_PRINTER_VIAS} &&
    ! ref $config{PRINT_RECEIPT_PRINTER_VIAS}
  ) {
    my $tmp = delete $config{PRINT_RECEIPT_PRINTER_VIAS};
    push @{$config{PRINT_RECEIPT_PRINTER_VIAS}}, $tmp;
  }

  if (
    exists $config{AREA_HAPPY_HOUR} &&
    ! ref $config{AREA_HAPPY_HOUR}
  ) {
    my $tmp = delete $config{AREA_HAPPY_HOUR};
    push @{$config{AREA_HAPPY_HOUR}}, $tmp;
  }

  if (
    exists $config{GROUP_REQUESTS_BY_PRINTER_SPECIFIC} &&
    ! ref $config{GROUP_REQUESTS_BY_PRINTER_SPECIFIC}
  ) {
    my $tmp = delete $config{GROUP_REQUESTS_BY_PRINTER_SPECIFIC};
    push @{$config{GROUP_REQUESTS_BY_PRINTER_SPECIFIC}}, $tmp;
  }

  if (
    exists $config{ORDER_PRINTER_ERRORS_CONFIG} &&
    ! ref $config{ORDER_PRINTER_ERRORS_CONFIG}
  ) {
    my $tmp = delete $config{ORDER_PRINTER_ERRORS_CONFIG};
    push @{$config{ORDER_PRINTER_ERRORS_CONFIG}}, $tmp;
  }

  if (
    exists $config{SAFT_SERIES_CODES} &&
    ! ref $config{SAFT_SERIES_CODES}
  ) {
    my $tmp = delete $config{SAFT_SERIES_CODES};
    push @{$config{SAFT_SERIES_CODES}}, $tmp;
  }

  my $pt_certified_software = -e "/opt/pos/common/skins/".$config{LANG}."/sign_server" ? 1 : 0;

  $config{PRINT_BARCODE_IN_BILL} ||= 0;
  $config{PRINT_BARCODE_IN_CHECK} ||= 0;
  #$config{PRINT_BARCODE_IN_REQUEST} ||= 0;
  $config{CLOSE_DRAWER_MANDATORY} ||= 0;

  if (! $pt_certified_software && $config{LANG} ne 'ECR') {
    $config{DOC_TYPE_SELL} ||= 'VD';
    $config{DOC_TYPE_RETURN} ||= 'NCD';
    $config{DOC_TYPE_RETURN_NOTE} ||= 'GD';
    $config{DOC_TYPE_UPFRONT} ||= 'AD';
    $config{DOC_TYPE_VOID} ||= 'VOID';
    $config{DOC_TYPE_CONSUMPTION} ||= 'CP';
    $config{DOC_TYPE_SELL_CUSTOMER} ||= 'FR';
    $config{DOC_TYPE_SELL_SIMPLIFIED} ||= 'FS';
    $config{DOC_TYPE_TABLE_PRINT} ||= 'CM';
    $config{DOC_TYPE_TABLE_PRINT_INTERNAL} ||= 'CM2';
    $config{DOC_TYPE_CUSTOMER_CREDIT} ||= 'FT';
    $config{DOC_TYPE_RECEIPT} ||= 'RG';
    $config{DOC_TYPE_TRANSPORT_NOTE} ||= 'GT';
    $config{DOC_TYPE_DELIVERY_NOTE} ||= 'GR';
    $config{DOC_TYPE_TRANSPORT_DOCUMENT} ||= 'DT';
    $config{DOC_TYPE_DEBIT_NOTE} ||= 'ND';
    $config{DOC_TYPE_CREDIT_NOTE} ||= 'NC';
    $config{DOC_TYPE_EXTERNAL} ||= 'EXT';
    $config{DOC_TYPE_SUPPLIER_FVD} ||= 'FVD';
    $config{DOC_TYPE_SUPPLIER_FFR} ||= 'FFR';
    $config{DOC_TYPE_SUPPLIER_FFT} ||= 'FFT';
    $config{DOC_TYPE_SUPPLIER_FNC} ||= 'FNC';
    $config{DOC_TYPE_SUPPLIER_FND} ||= 'FND';
    $config{DOC_TYPE_SUPPLIER_FNL} ||= 'FNL';
  }
  else {
    $config{DOC_TYPE_SELL} = 'VD';
    $config{DOC_TYPE_RETURN} = 'NCD';
    $config{DOC_TYPE_RETURN_NOTE} = 'GD';
    $config{DOC_TYPE_UPFRONT} = 'AD';
    $config{DOC_TYPE_VOID} = 'VOID';
    $config{DOC_TYPE_CONSUMPTION} = 'CP';
    $config{DOC_TYPE_SELL_CUSTOMER} = 'FR';
    $config{DOC_TYPE_SELL_SIMPLIFIED} = 'FS';
    $config{DOC_TYPE_TABLE_PRINT} = 'CM';
    $config{DOC_TYPE_TABLE_PRINT_INTERNAL} = 'CM2';
    $config{DOC_TYPE_CUSTOMER_CREDIT} = 'FT';
    $config{DOC_TYPE_RECEIPT} = 'RG';
    $config{DOC_TYPE_TRANSPORT_NOTE} = 'GT';
    $config{DOC_TYPE_DELIVERY_NOTE} = 'GR';
    $config{DOC_TYPE_TRANSPORT_DOCUMENT} = 'DT';
    $config{DOC_TYPE_DEBIT_NOTE} = 'ND';
    $config{DOC_TYPE_CREDIT_NOTE} = 'NC';
    $config{DOC_TYPE_EXTERNAL} = 'EXT';
    $config{DOC_TYPE_SUPPLIER_FVD} = 'FVD';
    $config{DOC_TYPE_SUPPLIER_FFR} = 'FFR';
    $config{DOC_TYPE_SUPPLIER_FFT} = 'FFT';
    $config{DOC_TYPE_SUPPLIER_FNC} = 'FNC';
    $config{DOC_TYPE_SUPPLIER_FND} = 'FND';
    $config{DOC_TYPE_SUPPLIER_FNL} = 'FNL';
  }

  # remove spaces from here
  $config{DOC_TYPE_SERIE} =~ s/ //g;

  $config{PRINT_DOC_TYPE_SELL} ||= 1;
  $config{PRINT_DOC_TYPE_RETURN} ||= 1;
  $config{PRINT_DOC_TYPE_RETURN_NOTE} ||= 1;
  $config{PRINT_DOC_TYPE_UPFRONT} ||= 1;
  $config{PRINT_DOC_TYPE_VOID} ||= 1;
  $config{PRINT_DOC_TYPE_CONSUMPTION} ||= 1;
  $config{PRINT_DOC_TYPE_SELL_CUSTOMER} ||= 1;
  $config{PRINT_DOC_TYPE_SELL_SIMPLIFIED} ||= 1;
  $config{PRINT_DOC_TYPE_TABLE_PRINT} ||= 1;
  $config{PRINT_DOC_TYPE_TABLE_PRINT_INTERNAL} ||= 1;
  $config{PRINT_DOC_TYPE_CUSTOMER_CREDIT} ||= 1;
  $config{PRINT_DOC_TYPE_CUSTOMER_CREDIT_CC} ||= 1;
  $config{PRINT_DOC_TYPE_RECEIPT} ||= 1;
  $config{PRINT_DOC_TYPE_TRANSPORT_NOTE} ||= 1;
  $config{PRINT_DOC_TYPE_DELIVERY_NOTE} ||= 1;
  $config{PRINT_DOC_TYPE_DEBIT_NOTE} ||= 1;
  $config{PRINT_DOC_TYPE_CREDIT_NOTE} ||= 1;
  $config{PRINT_DOC_TYPE_EXTERNAL} ||= 1;
  $config{PRINT_CC_REPORTS} ||= 1;
  $config{PRINT_DOC_TYPE_SUPPLIER_FVD} ||= 1;
  $config{PRINT_DOC_TYPE_SUPPLIER_FFR} ||= 1;
  $config{PRINT_DOC_TYPE_SUPPLIER_FFT} ||= 1;
  $config{PRINT_DOC_TYPE_SUPPLIER_FNC} ||= 1;
  $config{PRINT_DOC_TYPE_SUPPLIER_FND} ||= 1;
  $config{PRINT_DOC_TYPE_SUPPLIER_FNL} ||= 1;

  $config{SALES_TO_NETWORK} ||= 0;
  $config{MACHINE_LABEL} ||= "";

  if (! exists $config{DONT_CHANGE_DAY_AFTER_DAY_CLOSE}) {
    $config{DONT_CHANGE_DAY_AFTER_DAY_CLOSE} = 1;
  }

  $self->__cache(\%config);

  return $self->__cache();
}

sub areas_info {
  my $self = shift;
  return $self->{__areas_info} if exists $self->{__areas_info};

  my %areas_info = ();

  my $config = $self->load_fixed();

  $config->{AREA} ||= [];

  my $area_idx = 0;

  foreach my $area (@{ $config->{AREA} }) {
    my @t = split /,/, $area;
    $areas_info{$area_idx++} = \@t;
  }

  $self->{__areas_info} = \%areas_info;
  return $self->{__areas_info};
}

sub printing_zones_by_name {
  my $self = shift;
  return $self->{__zones_by_name} if exists $self->{__zones_by_name};

  my $cfg = $self->load_fixed();
  $cfg->{PrintingZones}->{Zone} ||= [];
  return unless scalar @{ $cfg->{PrintingZones}->{Zone} };

  my $z = $cfg->{PrintingZones}->{Zone};
  my $h = {};

  foreach my $zone (@$z) {
    $h->{$zone->{Name}} = $zone;
  }

  $self->{__zones_by_name} = $h;
  return $h;
}

sub printers_info {
  my $self = shift;
  return $self->{__printers_info} if exists $self->{__printers_info};

  my $i = {};

  my $cfg = $self->load_fixed();

  $cfg->{PrintingZones}->{Zone} ||= [];

  foreach my $zone (@{ $cfg->{PrintingZones}->{Zone} }) {
    $zone->{Printer} ||= [];

    foreach my $printer (@{$zone->{Printer}}) {
      $printer->{FromArea} //= "";
      $printer->{FromStation} //= "";

      if (! exists $i->{ $zone->{Name} }->{ $printer->{Station} }->{ $printer->{Number} }) {
        $i->{ $zone->{Name} }->{ $printer->{Station} }->{ $printer->{Number} } = $printer;
      }

      if (exists $printer->{Template} && defined $printer->{Template} && $printer->{Template} ne '') {
        if (! exists $printer->{FromStation}) {
          $printer->{FromStation} = "";
        }

        if (! exists $printer->{FromArea}) {
          $printer->{FromArea} = "";
        }

        if ($printer->{FromArea} ne '' || $printer->{FromStation} ne '') {
          my $key = $printer->{FromArea}."#".$printer->{FromStation};
          $i->{ $zone->{Name} }->{ $printer->{Station} }->{ $printer->{Number} }->{TemplateFilter}->{$key} = $printer->{Template};
        }
      }
    }
  }

  $self->{__printers_info} = $i;

  return $i;
}

sub doc_type_str_by_id {
  my $self = shift;
  my $id = shift;

  my $h = $self->doc_types_by_id();

  return exists $h->{$id} ? $h->{$id} : "";
}

sub doc_types_by_name {
  my $self = shift;

  my $cfg = $self->load();

  my %i = (
    $cfg->{DOC_TYPE_SELL}                 => 1,
    $cfg->{DOC_TYPE_RETURN}               => 2,
    $cfg->{DOC_TYPE_VOID}                 => 3,
    $cfg->{DOC_TYPE_CONSUMPTION}          => 4,
    $cfg->{DOC_TYPE_SELL_CUSTOMER}        => 5,
    $cfg->{DOC_TYPE_TABLE_PRINT}          => 6,
    $cfg->{DOC_TYPE_CUSTOMER_CREDIT}      => 7,
    $cfg->{DOC_TYPE_RECEIPT}              => 8,
    $cfg->{DOC_TYPE_TABLE_PRINT_INTERNAL} => 9,
    $cfg->{DOC_TYPE_TRANSPORT_NOTE}       => 10,
    $cfg->{DOC_TYPE_DELIVERY_NOTE}        => 11,
    $cfg->{DOC_TYPE_DEBIT_NOTE}           => 12,
    $cfg->{DOC_TYPE_CREDIT_NOTE}          => 13,
    $cfg->{DOC_TYPE_UPFRONT}              => 14,
    $cfg->{DOC_TYPE_RETURN_NOTE}          => 15,
    $cfg->{DOC_TYPE_EXTERNAL}             => 99,
    $cfg->{DOC_TYPE_SELL_SIMPLIFIED}      => 90,

    $cfg->{DOC_TYPE_SUPPLIER_FVD}         => 30,
    $cfg->{DOC_TYPE_SUPPLIER_FFR}         => 31,
    $cfg->{DOC_TYPE_SUPPLIER_FFT}         => 32,
    $cfg->{DOC_TYPE_SUPPLIER_FNC}         => 33,
    $cfg->{DOC_TYPE_SUPPLIER_FND}         => 34,
    $cfg->{DOC_TYPE_SUPPLIER_FNL}         => 35,
  );

  return wantarray ? %i : \%i;
}

sub doc_types_by_id {
  my $self = shift;

  my $cfg = $self->load();

  my %i = (
    1  => $cfg->{DOC_TYPE_SELL},
    2  => $cfg->{DOC_TYPE_RETURN},
    3  => $cfg->{DOC_TYPE_VOID},
    4  => $cfg->{DOC_TYPE_CONSUMPTION},
    5  => $cfg->{DOC_TYPE_SELL_CUSTOMER},
    6  => $cfg->{DOC_TYPE_TABLE_PRINT},
    7  => $cfg->{DOC_TYPE_CUSTOMER_CREDIT},
    8  => $cfg->{DOC_TYPE_RECEIPT},
    9  => $cfg->{DOC_TYPE_TABLE_PRINT_INTERNAL},
    10 => $cfg->{DOC_TYPE_TRANSPORT_NOTE},
    11 => $cfg->{DOC_TYPE_DELIVERY_NOTE},
    12 => $cfg->{DOC_TYPE_DEBIT_NOTE},
    13 => $cfg->{DOC_TYPE_CREDIT_NOTE},
    14 => $cfg->{DOC_TYPE_UPFRONT},
    15 => $cfg->{DOC_TYPE_RETURN_NOTE},
    99 => $cfg->{DOC_TYPE_EXTERNAL},
    90 => $cfg->{DOC_TYPE_SELL_SIMPLIFIED},

    30 => $cfg->{DOC_TYPE_SUPPLIER_FVD},
    31 => $cfg->{DOC_TYPE_SUPPLIER_FFR},
    32 => $cfg->{DOC_TYPE_SUPPLIER_FFT},
    33 => $cfg->{DOC_TYPE_SUPPLIER_FNC},
    34 => $cfg->{DOC_TYPE_SUPPLIER_FND},
    35 => $cfg->{DOC_TYPE_SUPPLIER_FNL},
  );

  return wantarray ? %i : \%i;
}

sub print_destination_by_docid {
  my $self = shift;

  my $cfg = $self->load();

  my %i = (
    1  => $cfg->{PRINT_DOC_TYPE_SELL},
    2  => $cfg->{PRINT_DOC_TYPE_RETURN},
    3  => $cfg->{PRINT_DOC_TYPE_VOID},
    4  => $cfg->{PRINT_DOC_TYPE_CONSUMPTION},
    5  => $cfg->{PRINT_DOC_TYPE_SELL_CUSTOMER},
    6  => $cfg->{PRINT_DOC_TYPE_TABLE_PRINT},
    7  => $cfg->{PRINT_DOC_TYPE_CUSTOMER_CREDIT},
    71 => $cfg->{PRINT_DOC_TYPE_CUSTOMER_CREDIT_CC},
    8  => $cfg->{PRINT_DOC_TYPE_RECEIPT},
    9  => $cfg->{PRINT_DOC_TYPE_TABLE_PRINT_INTERNAL},
    10 => $cfg->{PRINT_DOC_TYPE_TRANSPORT_NOTE},
    11 => $cfg->{PRINT_DOC_TYPE_DELIVERY_NOTE},
    12 => $cfg->{PRINT_DOC_TYPE_DEBIT_NOTE},
    13 => $cfg->{PRINT_DOC_TYPE_CREDIT_NOTE},
    14 => $cfg->{PRINT_DOC_TYPE_UPFRONT},
    15 => $cfg->{PRINT_DOC_TYPE_RETURN_NOTE},
    99 => $cfg->{PRINT_DOC_TYPE_EXTERNAL},
    90 => $cfg->{PRINT_DOC_TYPE_SELL_SIMPLIFIED},
    30 => $cfg->{PRINT_DOC_TYPE_SUPPLIER_FVD},
    31 => $cfg->{PRINT_DOC_TYPE_SUPPLIER_FFR},
    32 => $cfg->{PRINT_DOC_TYPE_SUPPLIER_FFT},
    33 => $cfg->{PRINT_DOC_TYPE_SUPPLIER_FNC},
    34 => $cfg->{PRINT_DOC_TYPE_SUPPLIER_FND},
    35 => $cfg->{PRINT_DOC_TYPE_SUPPLIER_FNL},
  );

  return wantarray ? %i : \%i;
}

sub print_template_by_docid {
  my $self = shift;

  my $cfg = $self->load();

  my %i = (
    1  => 'print_invoice',
    2  => 'print_return',
    3  => '',
    4  => 'print_consumption',
    5  => 'print_invoice',
    6  => 'print_bill',
    7  => 'print_invoice',
    8  => 'print_receipt',
    9  => 'print_bill_internal',
    10 => 'print_transport_note',
    11 => 'print_delivery_note',
    12 => 'print_debit_note',
    13 => 'print_credit_note',
    14 => 'print_upfront',
    15 => 'print_return_note',
    99 => 'print_external',

    30 => 'print_invoice',
    31 => 'print_invoice',
    32 => 'print_invoice',
    33 => 'print_invoice',
    34 => 'print_invoice',
    35 => 'print_receipt',
  );

  return wantarray ? %i : \%i;
}

sub print_destination {
  my $self = shift;
  my $doc_type = shift;

  my $print_destination_by_docid = $self->print_destination_by_docid();

  return exists $print_destination_by_docid->{$doc_type} ? $print_destination_by_docid->{$doc_type} : 1;
}

sub document_template {
  my $self = shift;
  my( $dir, $doc_type, $overrided_destination ) = @_;

  my $templates = $self->print_template_by_docid();
  my $print_destination_by_docid = $self->print_destination_by_docid();

  my $print_destination = exists $print_destination_by_docid->{$doc_type} ? $print_destination_by_docid->{$doc_type} : 1;
  my $template_file = exists $templates->{$doc_type} ? $templates->{$doc_type} : 'print_invoice';

  # if an invalid template is being used, use the print_invoice as default
  if (! -e $dir.'/common/print_templates/'.$template_file.".tmpl") {
    $template_file = 'print_invoice';
  }

  # this is an optional parameter that can be used to override the print destination.
  # ex, the doc would be printed to an external printer and we want it to go to the ticket one
  if (defined $overrided_destination) {
    $print_destination = $overrided_destination;
  }

  # 1 = to receipt printer
  # 2 = to external printer
  if ($print_destination == 2) {
    $template_file .= ".external";
  }

  my $cfg = $self->load();
  my $skin = $cfg->{LANG};

  my $tmpl_file = "";

  if (-e $dir.'/common/print_templates/'.$cfg->{LANG}."/$template_file.tmpl") {
    $tmpl_file = $cfg->{LANG}."/$template_file.tmpl"
  } else {
    $tmpl_file = $template_file.'.tmpl';
  }

  return $tmpl_file;
}


sub load_fixed {
  my $self = shift;

  my $h = $self->load();

  if (
    exists $h->{AREA} &&
    ! ref $h->{AREA}
  ) {
    my $tmp = delete $h->{AREA};
    push @{$h->{AREA}}, $tmp;
  }

  if (
    exists $h->{PRINTING_ZONE_LINK} &&
    ! ref $h->{PRINTING_ZONE_LINK}
  ) {
    my $tmp = delete $h->{PRINTING_ZONE_LINK};
    push @{$h->{PRINTING_ZONE_LINK}}, $tmp;
  }

  if (
    exists $h->{AREA_CHECK_VALIDATION} &&
    ! ref $h->{AREA_CHECK_VALIDATION}
  ) {
    my $tmp = delete $h->{AREA_CHECK_VALIDATION};
    push @{$h->{AREA_CHECK_VALIDATION}}, $tmp;
  }

  if (
    exists $h->{AREA_HAPPY_HOUR} &&
    ! ref $h->{AREA_HAPPY_HOUR}
  ) {
    my $tmp = delete $h->{AREA_HAPPY_HOUR};
    push @{$h->{AREA_HAPPY_HOUR}}, $tmp;
  }

  if (
    exists $h->{QUEUES} &&
    ! ref $h->{QUEUES}
  ) {
    my $tmp = delete $h->{QUEUES};
    push @{$h->{QUEUES}}, $tmp;
  }

  if (
    exists $h->{ORDER_SEPARATORS} &&
    ! ref $h->{ORDER_SEPARATORS}
  ) {
    my $tmp = delete $h->{ORDER_SEPARATORS};
    push @{$h->{ORDER_SEPARATORS}}, $tmp;
  }

=pod
  if (
    exists $h->{POINTS_PRODUCTS} &&
    ! ref $h->{POINTS_PRODUCTS}
  ) {
    my $tmp = delete $h->{POINTS_PRODUCTS};
    push @{$h->{POINTS_PRODUCTS}}, $tmp;
  }
=cut

  if (
    exists $h->{STATION_CASHIERS} &&
    ! ref $h->{STATION_CASHIERS}
  ) {
    my $tmp = delete $h->{STATION_CASHIERS};
    push @{$h->{STATION_CASHIERS}}, $tmp;
  }

  if (
    exists $h->{CASHIERS_BY_STATION} &&
    ! ref $h->{CASHIERS_BY_STATION}
  ) {
    my $tmp = delete $h->{CASHIERS_BY_STATION};
    push @{$h->{CASHIERS_BY_STATION}}, $tmp;
  }

  if (
    exists $h->{REDIRECT_TO_NETWORK_PRINTERS} &&
    ! ref $h->{REDIRECT_TO_NETWORK_PRINTERS}
  ) {
    my $tmp = delete $h->{REDIRECT_TO_NETWORK_PRINTERS};
    push @{$h->{REDIRECT_TO_NETWORK_PRINTERS}}, $tmp;
  }

  # convert the array of options into an hash by station num
  if (exists $h->{REDIRECT_TO_NETWORK_PRINTERS}) {
    my $ref = {};

    foreach my $redirect (@{$h->{REDIRECT_TO_NETWORK_PRINTERS}}) {
      my @a = split /,/, $redirect;

      if (scalar @a == 2) {
        $ref->{$a[0]} = $a[1];
      }
    }

    $h->{REDIRECT_TO_NETWORK_PRINTERS_BY_STATION} = $ref;
  }

  if (
    exists $h->{PRINT_RECEIPT_PRINTER_VIAS} &&
    ! ref $h->{PRINT_RECEIPT_PRINTER_VIAS}
  ) {
    my $tmp = delete $h->{PRINT_RECEIPT_PRINTER_VIAS};
    push @{$h->{PRINT_RECEIPT_PRINTER_VIAS}}, $tmp;
  }

  if (
    exists $h->{EXTERNAL_DOC} &&
    ! ref $h->{EXTERNAL_DOC}
  ) {
    my $tmp = delete $h->{EXTERNAL_DOC};
    push @{$h->{EXTERNAL_DOC}}, $tmp;
  }

  if ($h->{EXTERNAL_DOC} && scalar @{$h->{EXTERNAL_DOC}}) {
    foreach my $doc (@{$h->{EXTERNAL_DOC}}) {
      my @a = split /,/, $doc;
      $h->{EXTERNAL_DOC_BY_CODE}->{$a[0]} = $a[1];
    }
  }

  if (
    exists $h->{AREA_HAPPY_HOUR} &&
    ! ref $h->{AREA_HAPPY_HOUR}
  ) {
    my $tmp = delete $h->{AREA_HAPPY_HOUR};
    push @{$h->{AREA_HAPPY_HOUR}}, $tmp;
  }

  if (
    exists $h->{PrintingZones} &&
    exists $h->{PrintingZones}->{Zone} &&
    ref $h->{PrintingZones}->{Zone} eq 'HASH'
  ) {
    my $tmp = delete $h->{PrintingZones}->{Zone};
    push @{$h->{PrintingZones}->{Zone}}, $tmp;
  }

  $h->{PrintingZones}->{Zone} ||= [];

  foreach my $zone (@{ $h->{PrintingZones}->{Zone} }) {
    if (exists $zone->{Printer}) {
      if (ref($zone->{Printer}) eq 'HASH') {
        my $tmp = delete $zone->{Printer};
        push @{$zone->{Printer}}, $tmp;
      }
    }
  }

  if (exists $h->{Hardware}) {
    # scales
    if (
      exists $h->{Hardware}->{Scales} &&
      $h->{Hardware}->{Scales}->{Device} &&
      ref $h->{Hardware}->{Scales}->{Device} eq 'HASH'
    ) {
      my $tmp = delete $h->{Hardware}->{Scales}->{Device};
      push @{$h->{Hardware}->{Scales}->{Device}}, $tmp;
    }

    # customer displays
    if (
      exists $h->{Hardware}->{CustomerDisplays} &&
      $h->{Hardware}->{CustomerDisplays}->{Device} &&
      ref $h->{Hardware}->{CustomerDisplays}->{Device} eq 'HASH'
    ) {
      my $tmp = delete $h->{Hardware}->{CustomerDisplays}->{Device};
      push @{$h->{Hardware}->{CustomerDisplays}->{Device}}, $tmp;
    }

    # cashiers
    if (
      exists $h->{Cashiers} &&
      $h->{Cashiers}->{Cashier} &&
      ref $h->{Cashiers}->{Cashier} eq 'HASH'
    ) {
      my $tmp = delete $h->{Cashiers}->{Cashier};
      push @{$h->{Cashiers}->{Cashier}}, $tmp;
    }

    # barcode readers
    if (
      exists $h->{Hardware}->{BarcodeReaders} &&
      $h->{Hardware}->{BarcodeReaders}->{Device} &&
      ref $h->{Hardware}->{BarcodeReaders}->{Device} eq 'HASH'
    ) {
      my $tmp = delete $h->{Hardware}->{BarcodeReaders}->{Device};
      push @{$h->{Hardware}->{BarcodeReaders}->{Device}}, $tmp;
    }

    # dallas readers
    if (
      exists $h->{Hardware}->{DallasReaders} &&
      $h->{Hardware}->{DallasReaders}->{Device} &&
      ref $h->{Hardware}->{DallasReaders}->{Device} eq 'HASH'
    ) {
      my $tmp = delete $h->{Hardware}->{DallasReaders}->{Device};
      push @{$h->{Hardware}->{DallasReaders}->{Device}}, $tmp;
    }

    # queues
    if (
      exists $h->{Hardware}->{Queues} &&
      $h->{Hardware}->{Queues}->{Device} &&
      ref $h->{Hardware}->{Queues}->{Device} eq 'HASH'
    ) {
      my $tmp = delete $h->{Hardware}->{Queues}->{Device};
      push @{$h->{Hardware}->{Queues}->{Device}}, $tmp;
    }

    # card readers - devices
    if (
      exists $h->{Hardware}->{CardReaders} &&
      $h->{Hardware}->{CardReaders}->{Device} &&
      ref $h->{Hardware}->{CardReaders}->{Device} eq 'HASH'
    ) {
      my $tmp = delete $h->{Hardware}->{CardReaders}->{Device};
      push @{$h->{Hardware}->{CardReaders}->{Device}}, $tmp;
    }

    # cash drawers
    if (
      exists $h->{Hardware}->{CashDrawers} &&
      $h->{Hardware}->{CashDrawers}->{Device} &&
      ref $h->{Hardware}->{CashDrawers}->{Device} eq 'HASH'
    ) {
      my $tmp = delete $h->{Hardware}->{CashDrawers}->{Device};
      push @{$h->{Hardware}->{CashDrawers}->{Device}}, $tmp;
    }

    # printers
    if (
      exists $h->{Hardware}->{Printers} &&
      $h->{Hardware}->{Printers}->{Device} &&
      ref $h->{Hardware}->{Printers}->{Device} eq 'HASH'
    ) {
      my $tmp = delete $h->{Hardware}->{Printers}->{Device};
      push @{$h->{Hardware}->{Printers}->{Device}}, $tmp;
    }
  }

  return $h;
}

sub devices_by_type {
  my $self = shift;
  my $type = shift;

  # invalidate config cache
  undef $self->{__cache};

  my $info = $self->load_fixed();
  my @devices = ();

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

      push @devices, {
        num => $num,
        device => $device,
      };

      $idx++;
    }
  }

  return \@devices;
}

sub __fix {
}

sub __cache {
  my $self = shift;
  @_ ? $self->{__cache} = shift : $self->{__cache};
}


1;
