package LIMS::Controller::Roles::Storage;

use POSIX;
use Moose::Role;
use Data::Printer alias => 'p';

has base_location => ( is => 'rw', isa => 'Str' );

# calculates location of rack and tray according to location specification
# re-allocates any empty spaces:
sub get_auto_location {
    my ($self, $base_location) = @_;

    return $base_location unless # will be free-text box for other services:
        $self->cfg('settings')->{'_centre'} eq 'leeds';
    # set base location for _get_plate_location() method:
    $self->base_location($base_location);

    # get list of all locations for $base_location:
    my $o = do {
        my $q = [ storage_location => { like => $base_location . '%' } ];
        $self->model('Base')->get_objects('StorageRack', { query => $q });
    };
    my @locations = map $_->storage_location, @$o; # p \@locations;

    # total no. of plates for this base location:
    my $total = scalar @locations; # p $total; exit;

    my $storage_location; # to be determined:
    # first try to (re)allocate any missing spaces in existing trays:
    PLATE: # for each tray in this rack, check we have 4 plates:
    for my $i ( 0 .. $total - 1 ) { # for each of $total plates:
        next PLATE if $i % 4; # only need to check 1st of 4 identical positions
        # get predicted tray location for plate $i:
        my $tray_location = $self->_get_plate_location($i); # p $tray_location;
        # how many plates do we already have for this tray (4 is full):
        my $n = grep $_ eq $tray_location, @locations; # p $n;
        # allocate this location if tray not full, either because of missing
        # plate in earlier tray, or newest tray and it's not full:
        $storage_location = $tray_location and last PLATE if $n < 4;
    } # p $storage_location;
    # get a new location if not already provided:
    $storage_location ||= $self->_get_plate_location($total); # p $storage_location;
    return $storage_location;
}

sub _get_plate_location {
    my ($self, $n) = @_;

    my $rack = floor($n / 36) + 1; # p $rack;
    my $tray = floor($n % 36 /  4) + 1; # p $tray;

    my $base_location = $self->base_location;
    if ( $base_location eq 'Archive' ) { # replace rack number with letter:
        my $letter = 'A'; # start at A and auto-increment, a ..z, aa, ab, etc
        $letter++ while --$rack; # p $letter;
        $rack = $letter;
    }
    return join '/', $base_location, $rack, $tray;
}

#===============================================================================
sub _rack_dimensions_map {  # dimensions A1-D6, A1-H8, etc
    return {
        24 => { alpha_max => 'D', numbr_max => 6  },
        48 => { alpha_max => 'F', numbr_max => 8  },
        96 => { alpha_max => 'H', numbr_max => 12 },
    };
}

#===============================================================================
sub _dummy_data { # dummy data to spare plate reader ...
    my $self = shift;
=begin # to clear request_storage & storage_racks tables:
    UPDATE request_storage SET rack_id = NULL;
    UPDATE request_storage SET vial_location = NULL;
    UPDATE request_storage SET signed_out = NULL;
    SET FOREIGN_KEY_CHECKS=0;
    TRUNCATE TABLE storage_racks;
    SET FOREIGN_KEY_CHECKS=1;
=cut

#=begin # uncomment to get 24, 48 or 96 vials from request_storage db table:
    {
        my $number_of_vials = 96; # 24, 48 or 96

        my $rack_dimensions = _rack_dimensions_map(); # dimensions A1-D6, A1-H8, etc

        my $q = [ # uncomment one:
            # vial_location => undef # not assigned to a rack
            vial_location => { ne => undef } # is assigned to a rack
        ];

        my $data = do {
            my %args = ( query => $q, limit => $number_of_vials );
            $self->model('Base')->get_objects('RequestStorage', \%args );
        }; # p $_->as_tree for @$data;

        # 24/48/96-well data (24-well = A1-D6, 48-well = A1-F8, 96-well = A1-H12):
        my $alpha_max = $rack_dimensions->{$number_of_vials}{alpha_max};
        my $numbr_max = sprintf '%02d', $rack_dimensions->{$number_of_vials}{numbr_max};

        my @cells = map { $_ . '01' .. $_ . $numbr_max } ( 'A' .. $alpha_max ); # p @cells;
        my %h; @h{@cells} = map $_->vialId, @$data; # p %h;

        my $plateId = DateTime->now->epoch;
        # uncomment to get plateId from storage_racks:
        # $plateId = $self->model('Storage')->get_objects('StorageRack',
        #   { query => [ is_active => 'yes' ], limit => 1 } )->[0]->plateId; # p $plateId;

        return { # same format as xtr-96 output:
            plate_data => \%h,
            well_max   => $rack_dimensions->{$number_of_vials},
            plateId	   => $plateId,
        };
    }
#=cut

=begin to get more data from db:
    select vialId, vial_location
    from request_storage
    where vial_location is not null and signed_out is null
    group by vial_location
    order by vial_location

    select vial_location, vialId
    from request_storage rs
        join storage_racks sr on rs.rack_id = sr.id
    where sr.plateId = 'SA00084423'
    order by vial_location
=cut
	my $plateId = 'SA00084427'; # SA00098711

	my %data = %{ _plate_data() }; # warn Dumper \%data;

    my %h = ( plateId => $plateId );

# uncomment to return 48-well data (replace 96 with 48):
#   ---------------------------------------------------------------------------
#    %data = map { $_ => $data{$_} } grep $_ =~ /[A-F]0[1-8]/, keys %data; # p %data;
#   ---------------------------------------------------------------------------

    $h{plate_data} = \%data;

    { # get number of wells from $data for tt to draw table of correct size:
        my $dimensions_map = _rack_dimensions_map(); # dimensions A1-D6, A1-H8, etc
        my $number_of_wells = keys %data; # warn $number_of_wells;
        $h{well_max} = $dimensions_map->{$number_of_wells};
    } # p %h;

    return \%h; # same format as xtr-96 output
}

sub _test_data {
    my $data = _plate_data(); # returns hash
    my $dimensions_map  = _rack_dimensions_map(); # dimensions A1-D6, A1-H8, etc
    my $number_of_wells = scalar keys %$data;
    my $well_max = $dimensions_map->{$number_of_wells};
    return {
        plate_data => $data,
        well_max   => $well_max,
        plateId    => 'SA01234567',
    }
}

sub _plate_data {
    my $i = 1000000001; # 10 digit number
    my @cells = map { $_ . '01' .. $_ . '12' } ( 'A' .. 'H' ); # p @cells;
    my %h = map { $_ => $i++ } @cells; # p %h;
    return \%h;
=begin
    return (
        'A01' => '1020850304',
        'A02' => '1020850312',
        'A03' => '1020850320',
        'A04' => '1020850328',
        'A05' => '1020850336',
        'A06' => '1020850344',
        'A07' => '1020850352',
        'A08' => '1020850360',
        'A09' => '1020850368',
        'A10' => '1020850321',
        'A11' => '1020850384',
        'A12' => '1020850392',
        'B01' => '1020850305',
        'B02' => '1020850313',
        'B03' => '1020850348',
        'B04' => '1020850383',
        'B05' => '1020850391',
        'B06' => '1020850345',
        'B07' => '1020850353',
        'B08' => '1020850361',
        'B09' => '1020850369',
        'B10' => '1020850367',
        'B11' => '1020850385',
        'B12' => '1020850393',
        'C01' => '1020850306',
        'C02' => '1020850314',
        'C03' => '1020850322',
        'C04' => '1020850359',
        'C05' => '1020850338',
        'C06' => '1020850346',
        'C07' => '1020850354',
        'C08' => '1020850362',
        'C09' => '1020850370',
        'C10' => '1020850378',
        'C11' => '1020850386',
        'C12' => '1020850394',
        'D01' => '1020850319',
        'D02' => '1020850315',
        'D03' => '1020850323',
        'D04' => '1020850331',
        'D05' => '1020850355',
        'D06' => '1020850347',
        'D07' => '1020850397',
        'D08' => '1020850398',
        'D09' => '1020850371',
        'D10' => '1020850379',
        'D11' => '1020850387',
        'D12' => '1020850395',
        'E01' => '1020850308',
        'E02' => '1020850316',
        'E03' => '1020850324',
        'E04' => '1020850332',
        'E05' => '1020850340',
        'E06' => '1020850311',
        'E07' => '1020850356',
        'E08' => '1020850364',
        'E09' => '1020850372',
        'E10' => '1020850380',
        'E11' => '1020850388',
        'E12' => '1020850382',
        'F01' => '1020850390',
        'F02' => '1020850317',
        'F03' => '1020850325',
        'F04' => '1020850358',
        'F05' => '1020850333',
        'F06' => '1020850357',
        'F07' => '1020850374',
        'F08' => '1020850365',
        'F09' => '1020850373',
        'F10' => '1020850381',
        'F11' => '1020850406',
        'F12' => '1020850414',
        'G01' => '1020850422',
        'G02' => '1020850405',
        'G03' => '1020850413',
        'G04' => '1020850421',
        'G05' => '1020850429',
        'G06' => '1020850437',
        'G07' => '1020850445',
        'G08' => '1020850453',
        'G09' => '1020850461',
        'G10' => '1020850469',
        'G11' => '1020850477',
        'G12' => '1020850485',
        'H01' => '1020850493',
        'H02' => '1020850404',
        'H03' => '1020850412',
        'H04' => '1020850420',
        'H05' => '1020850428',
        'H06' => '1020850436',
        'H07' => '1020850444',
        'H08' => '1020850452',
        'H09' => '1020850460',
        'H10' => '1020850468',
        'H11' => '1020850476',
        'H12' => '1020850484',
    )
=cut
}

1;
