package Board;
use strict;
1;

sub new {
    my($class, $board) = @_;
    die if $board =~ /[^ .ijlostz]/os;
    my $self = {
        map => [ map { [ split(//) ] } split(' ', $board) ],
    };
    my $width = scalar @{$self->{map}->[0]};
    foreach my $row (@{$self->{map}}) {
        die unless scalar @$row == $width;
    }
    bless $self, $class;
    $self->init();
    return $self;
}

sub clone {
    my($class, $board, $piece, $piecex, $piecey, $pieceangle) = @_;
    my $self = {
        map => [ ],
    };
    my $boardWidth = $board->width;
    my $boardHeight = $board->height;
    my $pieceWidth = $piece->width($pieceangle);
    my $pieceHeight = $piece->height($pieceangle);
    my $score = 0;
    for (my $y = 0; $y < $boardHeight; $y += 1) {
        if ($y < $piecey or $y >= $piecey + $pieceHeight) {
            push(@{$self->{map}}, $board->{map}->[$y]);
        } else {
            my $row = [];
            my $blanks = 0;
            for (my $x = 0; $x < $boardWidth; $x += 1) {
                my $cell = $board->{map}->[$y]->[$x];
                if ($cell eq '.' and
                    $x >= $piecex and $x < $piecex+$pieceWidth and
                    $y >= $piecey and $y < $piecey+$pieceHeight) {
                    $cell = $piece->cell($pieceangle, $x - $piecex, $y - $piecey);
                }
                if ($cell eq '.') {
                    $blanks += 1;
                }
                push(@$row, $cell);
            }
            if ($blanks > 0) {
                push(@{$self->{map}}, $row);
            } else {
                $score += 1;
            }
        }
    }
    $self->{score} = $score;
    bless $self, $class;
    $self->init();
    return $self;
}

sub init {
    my($self) = @_;
    $self->{width} = scalar(@{$self->{map}->[0]});
    $self->{height} = scalar(@{$self->{map}});
    $self->{blankCounts} = {};
}

sub removeFirstColumn {
    my($self) = @_;
    my $boardHeight = $self->{height};
    for (my $y = 0; $y < $boardHeight; $y += 1) {
        pop(@{$self->{map}->[$y]});
    }
    $self->init();
}

sub width {
    return $_[0]->{width};
}

sub height {
    return $_[0]->{height};
}

sub score {
    return $_[0]->{score};
}

sub depth {
    my($self) = @_;
    if (not exists $self->{depth}) {
        # find y position of lowest line with only '.'s
        my $depth = -1;
        row: foreach my $row (@{$self->{map}}) {
            foreach my $cell (@$row) {
                if ($cell ne '.') {
                    last row;
                }
            }
            $depth += 1;
        }
        $self->{depth} = $depth;
    }
    return $self->{depth};
}

sub floor {
    my($self) = @_;
    if (not exists $self->{floor}) {
        # find y position of lowest line with an uncovered '.'
        my $floor = -1;
        my $view = [@{$self->{map}->[0]}];
        row: foreach my $row (@{$self->{map}}) {
            for (my $x = 0; $x < scalar(@$row); $x += 1) {
                if ($row->[$x] ne '.') {
                    $view->[$x] = $row->[$x];
                }
            }
            for my $cell (@$view) {
                if ($cell eq '.') {
                    $floor += 1;
                    next row;
                }
            }
            last row;
        }
        $self->{floor} = $floor;
    }
    return $self->{floor};
}

sub getDeltas {
    my($self) = @_;
    my @result;
    for my $x (0..$self->{width}-1) {
        push(@result, $self->height);
    }
    for my $y (0..$self->{height}-1) {
        for my $x (0..$self->{width}-1) {
            if ($result[$x] > $y) {
                if ($self->{map}->[$y]->[$x] ne '.') {
                    $result[$x] = $y;
                }
            }
        }
    }
    my $last = $result[0];
    foreach (my $x = 1; $x < scalar(@result); $x +=1) {
        ($result[$x], $last) = ($last - $result[$x], $result[$x]);
    }
    return @result;
}

sub blankCount {
    my($self, $y) = @_;
    if (not exists $self->{blankCounts}->{$y}) {
        my $count = 0;
        for (my $x = 0; $x < $self->width; $x += 1) {
            if ($self->cell($x, $y) eq '.') {
                $count += 1;
            }
        }
        $self->{blankCounts}->{$y} = $count;
    }
    return $self->{blankCounts}->{$y};
}

sub dump {
    my($self) = @_;
    my $result = '';
    for (my $y = 0; $y < $self->height; $y += 1) {
        for (my $x = 0; $x < $self->width; $x += 1) {
            $result .= $self->cell($x, $y);
        }
        $result .= "\n";
    }
    return $result;
}

sub cell {
    return $_[0]->{map}->[$_[2]]->[$_[1]];
}
