package Importer;

use strict;
use warnings;
use IO::File;

use lib '/app/tools/common/lib';
use Common::Util qw(trimspaces trimquotes);
use Common::Assert;

# reporting flags and variable
#
use constant kQuiet  => 0;
use constant kNormal => 1;
use constant kDebug  => 3;

use constant kProgressQuanta => 200;

use constant kTabDelimiter => "\t";

sub new {
    my ( $class, %args ) = @_;

    my $self = bless {}, $class;

    return $self->_init(%args);
}

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

    $self->delimiter( $args{delimiter} || kTabDelimiter );
    $self->reportLevel( $args{report}  || kNormal );
    $self->{fieldConfig} = $self->_columns();
    $self->{fieldCount}  = scalar @{ $self->{fieldConfig} };

    return $self;
}

# -------------------------------------------------------------
# Subroutines
# -------------------------------------------------------------

sub importFile {
    my ( $self, $inputFilePath ) = @_;
    assert($inputFilePath);

    # Open the input file for reading.
    #
    my $inputFile = IO::File->new($inputFilePath) || die "Can't open input file '$inputFilePath': $!\n";

    $self->report("\nImporting file ($inputFilePath)...");

    # skip first line (header row) and start line num at one
    <$inputFile>;
    my $lineNum    = 1;
    my $saveCount  = 0;
    my $matchCount = 0;
    my $dupCount   = 0;
    my $skipCount  = 0;
    my $delim      = $self->delimiter();

    # Process the file line by line
    #
    while ( my $record = <$inputFile> ) {
        $lineNum++;

        if ( 0 == $lineNum % kProgressQuanta ) {
            $self->report("  record $lineNum ... ");
        }

        # Split the line up into fields.
        #
        chomp $record;
        my @data = split( $delim, $record );
        @data = trimquotes(@data);
        @data = _escapequotes(@data);

        # sanity check
        #
        if ( $#data != $self->{fieldCount} ) {

            #warn "import column count ($self->{fieldCount}) doesn't match data column count (".$#data.") on line $lineNum\n";
        }

        my @fieldValues = ();

        for ( my $i = 0 ; $i <= $#data ; $i++ ) {
            my $value = $data[$i];

            # do we need to pre-process this data in some way?
            #
            my $preProcess;
            if ( exists $self->{fieldConfig}->[$i]->{preProcess} ) {
                my $handler = $self->{fieldConfig}->[$i]->{preProcess};
                $value = $self->$handler($value);
            }
            push( @fieldValues, $value );
        }

        # now that all fields are loaded, check if any adjustment is required
        if ( $self->can('post_parse_row') ) {
            $self->post_parse_row( \@fieldValues );
        }

        # is the data complete?
        #
        if ( $self->validate( \@fieldValues ) ) {

            # have we already created/matched this item?
            #
            if ( !$self->found( \@fieldValues ) ) {

                # does this item already exist in the system?
                #
                if ( !$self->lookup( \@fieldValues ) ) {

                    #$self->report("line $lineNum: saved");
                    $self->save( \@fieldValues );
                    $saveCount++;
                } else {

                    # already exists
                    $self->report("line $lineNum: exists");
                    $matchCount++;
                }
            } else {
                $self->report("line $lineNum: duplicate");
                $dupCount++;
            }
        } else {
            $self->report("line $lineNum: skipped");
            $skipCount++;
        }
    }
    my $total = $saveCount + $dupCount + $matchCount + $skipCount;
    $self->report("  Records imported  : $saveCount");
    $self->report("  Records duped     : $dupCount");
    $self->report("  Records matched   : $matchCount");
    $self->report("  Records skipped   : $skipCount");
    $self->report("  ----------------------------");
    $self->report("  Total records     : $total");

    # Done with the input file, close it.
    #
    $inputFile->close();
}

sub found {
    my ( $self, $fieldValues, $itemID ) = @_;

    my $key = $self->_getKey($fieldValues);

    if ($itemID) {
        $self->{_exists}->{$key} = $itemID;
    }

    return $self->{_exists}->{$key};
}

sub validate {
    my ( $self, $fieldValues ) = @_;

    my $valid = 1;

    for ( my $i = 0 ; $i <= $self->{fieldCount} ; $i++ ) {
        if ( $self->{fieldConfig}->[$i]->{required} && !length $fieldValues->[$i] ) {
            $valid = 0;
        }
    }

    return $valid;
}

sub save {
    my ( $self, %args ) = @_;

    my $fieldValues = $args{dataRow};
    my $class       = $args{class};
    my $id          = $args{id};

    my %params = ();
    for ( my $i = 0 ; $i <= $self->{fieldCount} ; $i++ ) {
        next if ( !keys %{ $self->{fieldConfig}->[$i] } or $self->{fieldConfig}->[$i]->{no_save} );

        my $fieldsAref = $self->{fieldConfig}->[$i]->{saveAs} || [ $self->{fieldConfig}->[$i]->{fieldName} ];

        foreach my $dbFieldName (@$fieldsAref) {
            $params{$dbFieldName} = $fieldValues->[$i];
        }
    }

    my $dbItem = $class->Create(%params);
    $dbItem->save();
    $self->found( $fieldValues, $dbItem->$id );
}

sub delimiter {
    my ( $self, $delim ) = @_;

    if ($delim) {
        $self->{_delimiter} = $delim;
    }

    return $self->{_delimiter};
}

sub reportLevel {
    my ( $self, $level ) = @_;

    if ($level) {
        $self->{_reportLevel} = $level;
    }

    return $self->{_reportLevel};
}

sub _getLookups {
    my ( $self, $fieldValues ) = @_;

    my @values;
    for ( my $i = 0 ; $i <= $self->{fieldCount} ; $i++ ) {
        if ( $self->{fieldConfig}->[$i]->{lookup} ) {
            push @values, $fieldValues->[$i];
        }
    }

    return \@values;
}

sub _getKey {
    my ( $self, $fieldValues ) = @_;

    my $key;
    my @vals = ();
    for ( my $i = 0 ; $i <= $self->{fieldCount} ; $i++ ) {
        if ( $self->{fieldConfig}->[$i]->{lookup} ) {
            push @vals, $fieldValues->[$i];
        }
    }

    if (@vals) {
        $key = join( '||', @vals );
    }

    return $key;
}

# ------------------------------------------------------------
# Utilities
# ------------------------------------------------------------

sub _escapequotes {
    my @string = @_;

    for (@string) {
        s/\"\"/"/g;
    }

    return wantarray ? @string : $string[0];
}

sub report {
    my ( $self, $text, $level ) = @_;

    $level = kNormal unless $level;

    if ( $level <= $self->reportLevel() ) {
        print $text . "\n";
    }
}

###
1;    # Play nicely.
###
