package Orchard::ETL::Extract::CSV;

use strict;
use warnings;

use Digest::MD5;
use Encode qw(encode_utf8);

use lib '/var/app/orchard/collab/jgetter/perllib';

use base 'Class::Accessor';

__PACKAGE__->mk_accessors(qw/csv data fh header named_cols col_names total_lines lineno/);

use Text::CSV;
use FileHandle;
use Orchard::NamedColumns;

sub new {
    my $class = shift;
    my $file  = shift;
    my $line  = shift;
    my $head  = shift;
    my $sep   = shift;
    my $self  = {
        'fh'  => FileHandle->new( $file, 'r' ),
        'csv' => Text::CSV->new( { 'binary' => 1, 'eol' => $/ } ),
    };
    bless $self, $class;
    if ( defined $sep ) {
        if ( $sep eq 'tab' ) {
            $self->csv->sep_char("\t");
			$self->csv->quote_char(undef);
			$self->csv->escape_char(undef);
        } else {
            $self->csv->sep_char($sep);
        }
    }
    if ( defined $head ) {
        if ( defined $line ) {
            my $i = 0;
            while ( $self->csv->getline( $self->fh ) ) {
                next unless ++$i eq $line;
                $self->{'lineno'} = $i;
                last;
            }
        }
        $self->header($head);
    }
    $self->__init__( defined $line ? $line : 1 );
    $self;
}

sub __init__ {
    my $self  = shift;
    my $start = shift;
    unless ( defined $self->header and ref $self->header eq 'ARRAY' ) {
        if ( $start == 0 ) {
            my $line;
            for my $i ( 'a' ... 'z' ) {
                push @{$line}, 'col_' . $i;
            }
            $self->{'lineno'} = 0;
            $self->header($line);
        } else {
            my $i = 0;
            while ( my $line = $self->csv->getline( $self->fh ) ) {
                next unless ++$i eq $start;
                map { $_ =~ s/\n/ /g } @{$line};
                $self->{'lineno'} = $i;
                $self->header($line);
                last;
            }
        }
    }
    $self->named_cols( Orchard::NamedColumns::letter_cols( $self->header ) );
    $self->col_names( Orchard::NamedColumns::col_letters( $self->header ) );
}

sub extract {
    my $self = shift;
    my $data;
    while ( my $row = $self->getrow ) {
        push @{$data}, $row;
    }
    $self->data($data);
    $data;
}

sub getrow {
    my $self = shift;

    my $row = $self->csv->getline( $self->fh );
    return undef unless defined $row;

    ## stop at the limit of lines requested
    return undef if defined $self->total_lines and ( $self->lineno == $self->total_lines );

    my $header = $self->header;

    my $href;
    for ( my $j = 0 ; $j < scalar @{$header} ; $j++ ) {
        $href->{ $header->[$j] } = $row->[$j];
    }
    $self->{'lineno'}++;
    $href->{'lineno'} = $self->lineno;

    $href;

}

sub getcol {
    my $self = shift;
    my $row  = shift;
    my $col  = shift;
    my $key  = $self->named_cols->{ uc($col) };
    ( defined $key and defined $row->{$key} and $row->{$key} ne '' ) ? $row->{$key} : undef;
}

sub getkey {
    my $self = shift;
    my $row  = shift;
    my $key  = shift;
    ( defined $row->{$key} and $row->{$key} ne '' ) ? $row->{$key} : undef;
}

sub getletter {
    my $self  = shift;
    my $text  = shift;
    my ($col) = map { $self->col_names->{$_} } grep { $_ =~ m/$text/i } keys %{ $self->col_names };
    $col;
}

sub getstr {
    my $self = shift;
    my $row  = shift;
    my @cols;
    foreach my $col ( ( 'A' ... 'Z', 'AA' ... 'ZZ' ) ) {
        next unless exists $self->named_cols->{$col};
        my $col = defined $self->getcol( $row, $col ) ? $self->getcol( $row, $col ) : '';
        push @cols, $col;
    }
    $self->csv->combine(@cols);
    $self->csv->string;

}

sub getmd5 {
    my $self = shift;
    my $row  = shift;
    my $md5  = Digest::MD5->new;
    foreach my $col ( ( 'A' ... 'Z', 'AA' ... 'ZZ' ) ) {
        next unless exists $self->named_cols->{$col};
        my $col = defined $self->getcol( $row, $col ) ? $self->getcol( $row, $col ) : '';
        $md5->add( encode_utf8($col) );
    }
    $md5->hexdigest;
}

1;
