package Common::File::Tied;
use strict;
use IO::File;

use lib '/app/tools/common/lib';

# This class will implement a simple interface that ties a file to an array.
# Accessing the array will return a line from the file.
#
# What we'll do is keep track of the file offsets for each line.  So if the user
# asks lines we've seen, we can quickly seek to that location.
#
sub TIEARRAY {
    my ( $class, $file ) = @_;

    my $self = bless {}, $class;
    return $self->_init($file);
}

sub FETCHSIZE {
    my ($self) = @_;

    return scalar @{ $self->{offsets} };
}

sub _init {
    my ( $self, $file ) = @_;

    $self->{file} = $file;

    $self->_initOffsets();

    return $self;
}

sub _initOffsets {
    my ($self) = @_;

    Common::Log::Print("Common::File::Tied::_initOffsets");

    # JPK - We can't lazily instantiate the offsets, as much as I'd like to, because
    # we wouldn't be able to product a valid value for the FETCHSIZE call.
    # So we will quickly scan the file, note the offsets for each line, and store that.
    # For very large files, this offset array will grow to a multi-megabyte size.
    #
    $self->{offsets} = [];

    # The first offset might NOT be '0'.
    # We want to skip over any byte-order markers.
    #
    $self->{offsets}->[0] = $self->{file}->headerSize();

    my $resetOffset = tell( $self->{file}->filehandle );
    seek( $self->{file}->filehandle, 0, 0 );
    my $line;
    my $lastOffset;

    #    while ($line = $self->{file}->readLine())
    while ( $line = $self->_readNextLineFromFile() ) {
        $lastOffset = tell( $self->{file}->filehandle );
        push @{ $self->{offsets} }, $lastOffset;
    }
    seek( $self->{file}->filehandle, $resetOffset, 0 );

    Common::Log::Print("Common::File::Tied::_initOffsets DONE");
}

sub FETCH {
    my ( $self, $index ) = @_;

    return $self->_readLine($index);
}

# Implement with a private method so we can subclass sensibly.
# !!! We might want to cache the last line read, to make accessing the
#     same line over and over again more efficient.
#
sub _readLine {
    my ( $self, $index ) = @_;

    if ( $index > $self->_lastIndex() ) {
        return undef;
    } else {
        my $offset = $self->{offsets}->[$index];
        seek( $self->{file}->filehandle, $offset, 0 );

        #        return $self->{file}->readLine();
        return $self->_readNextLineFromFile();
    }
}

# Breaking this out into a seperate method to allow us to change how we determine
# where lines end.    This is mostly to allow the 'Parsed' class to handle quoted \n characters.
#
sub _readNextLineFromFile {
    my ($self) = @_;

    return $self->{file}->readLine();
}

sub _lastIndex {
    my ($self) = @_;
    my $lastIndex = scalar @{ $self->{offsets} } - 1;
    return $lastIndex;
}

sub STORE {
    die "ERROR - Read-only";
}

sub DESTROY {
    my ($self) = @_;
    if ( $self->{fh} ) {
        close $self->{fh};
    }
}

1;
