#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------

package Common::Translator;

#
# Common::Translator
#
# This class provides an interface map import files to a
# common format that RoyaltyShare import methods understand.
#
# Usage:
#
#   my $translator = new SubClass::Translator( filename => $file );
#   my $iterator   = $translator->next;  # using an iterator;
#
# OR
#
#   my $translator = new SubClass::Translator( filename => $file );
#   my @records    = $translator->getAll;
#
#

use strict;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::Log;
use Common::Parser;

sub _recordClass         { 'Common::Translator::Record' }
sub _itemClass           { 'Common::Translator::Item' }
sub _versionClasses      { die "Must be overloaded" }
sub _defaultVersionClass { }

# new
#
# my $translator = new Common::Parser( filename => $file )
#
# Constructor.  Filename is a required field.
#
sub new {
    my $class = shift;

    my $self = {};
    bless $self, $class;

    return $self->_init(@_);
}

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

    $self->open( $args{filename} );

    if ( $self->_recordClass ) {
        eval "require " . $self->_recordClass;
        die $@ if ($@);
    }

    if ( $self->_itemClass ) {
        eval "require " . $self->_itemClass;
        die $@ if ($@);
    }

    return $self;
}

sub open {
    my $self = shift;
    my $filename = shift || $self->{_filename};
    $self->{_filename} = $filename;

    assert( $filename,    "File required" );
    assert( -r $filename, "Can not read file: $filename" );

    my $parser = $self->_getParser( filename => $filename );
    $self->{_parser} = $parser;

    unless ($parser) {
        $self->{_error} = "Unknown File Format";
        return undef;
    }

    $self->{_version} = $self->_getVersion($parser);

    $self->{_error} = "Unknown File Version" unless ( $self->{_version} );

    $self->{_error} = $self->{_version}->error()
      if ( $self->{_version} && $self->{_version}->error() );

    return;
}

sub _getParser {
    my $self = shift;
    return Common::Parser->Parse(@_);
}

sub _getVersion {
    my $self   = shift;
    my $parser = shift;
    my $class;
    my $version;
    my @versionClasses = $self->_versionClasses();

    assert($parser);
    assert(@versionClasses);

    foreach $class (@versionClasses) {
        eval "require $class";
        die $@ if ($@);

        $version = $class->new( parser => $parser );
        return $version if ( $version->validate() );
    }

    $class = $self->_defaultVersionClass();
    if ($class) {
        eval "require $class";
        die $@ if ($@);

        my $version = $class->new( parser => $parser );

        return $version;
    }

    return;
}

sub getAll {
    my $self   = shift;
    my $parser = $self->{_parser};
    my $record;

    my @results;
    $parser->rewind;

    # Consume header if needed
    if ( $self->version->hasHeader() ) {
        $self->getHeader();
    }

    return () unless ( $parser && $parser->hasNext() );

    Common::Log::Debug("Reading all records");

    while ( my $row = $parser->next() ) {
        $record = $self->_map($row);
        push( @results, $record ) if ($record);
    }

    return @results;
}

sub getHeader {
    my $self = shift;

    $self->{_parser}->rewind();
    my $row = $self->{_parser}->next;
}

sub _map {
    my $self        = shift;
    my $row         = shift;
    my $recordClass = $self->_recordClass();
    my $line        = $self->{_parser}->currentRow;
    my $record      = $recordClass->new( line => $line );
    my $item;
    my @items;

    return if ( $self->{_parser}->fileType() eq 'xml' );

    return unless ( $row && $row->columnCount );

    for ( my $col = 0 ; $col < $row->columnCount ; $col++ ) {
        $item = $self->_mapItem( $row->getColumn($col), $col );

        for my $i ( ref($item) eq 'ARRAY' ? @$item : ($item) ) {
            $record->addItem($i);
        }
    }

    return $record;
}

sub _mapItem    { die "Must be overloaded" }
sub _initMapper { die "Must be overloaded" }

sub error   { shift->{_error} }
sub version { shift->{_version} }
sub parser  { shift->{_parser} }

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