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.
###
