package Support::Implementation::TabDelimitedReader;
#--------------------------------------------------------------------
#  TabDelimitedReader - provides access to tab-delimited spreadsheets
#--------------------------------------------------------------------
use strict;
use warnings;

use IO::File;
use Data::Dumper;

use Spreadsheet::ParseExcel;

use lib '/app/tools/common/lib';
use Common::Assert;
use Common::UTF8;
use Common::Util qw( trimspaces );

#use ExcelReader;

use constant kQuiet  => 0;
use constant kNormal => 1;
use constant kDebug  => 3;
my $gReportLevel = kNormal;

sub new {
   my $class = shift;
   my $self = {@_};
   bless($self, $class);
   $self->_init;
   return $self;
}

sub _init {
   my( $self, %args ) = @_;
   return $self;
}

#------------------------------------------------------------------------
# _scanTabbedFile will build a generic array of hashes; each row from the
# template will be stored as a hash, using the header name as the key.
# The set of rows will be returned as an array.
# Arguments:
#   fileName - the name of the tab-delimited file to be read-in
#   data - the hash which will contain the data
#   templateHeader - the template header hash
#   columnMap - will map each column (by name) to the actual column #
#------------------------------------------------------------------------
sub scanTabbedFile {
   my $self = shift;
   my $fileName = $self->{filename};
   my $data = $self->{data};
   my $templateHeader = $self->{header};
   my $columnMap = $self->{columnmap};

   assert($fileName);
   assert($data);
   assert($templateHeader);

#   my $oExcel = new Spreadsheet::ParseExcel;
#   my $oBook = $oExcel->Parse($fileName);
#   my $numSheets = $oBook->{SheetCount};
#   my $workSheet = $oBook->{Worksheet}[0];

   #--------------
   # Open the file
   #--------------
   my $inputFile = IO::File->new($fileName) ||
      die "Can't open input file '$fileName': $!\n";

   #-------------
   # Get the data
   #-------------
   _readTabInputFile($inputFile, $data, $templateHeader, $columnMap);

   #------------
   # All done...
   #------------
   $inputFile->close();

}#scanTabbedFile

sub _readTabInputFile {
   my($file, $data, $templateHeader, $columnMap) = @_;
   assert($file);
   assert($data);
   assert($templateHeader);
   assert($columnMap);

   #-----------------------------------
   # The first row _must_ be the header
   #-----------------------------------

   #----------------------------------------------------------------
   # As columns are found, they'll be removed from 'columnsNotFound'
   #----------------------------------------------------------------
   my %columnsNotFound = %$templateHeader;

   my $firstRow = <$file>;
   chomp $firstRow;
   my @header = split("\t", $firstRow);
   @header = trimquotes(@header);

   my $col=0;
   my $foundColumns=0;
#   my $maxCol = $workSheet->{MaxCol}; # absolute number of columns
#   for(my $col = $workSheet->{MinCol};
#       defined $maxCol && $col <= $maxCol;
#       $col++) 
   foreach my $cellValue (@header) {
      #my $cellObj = $workSheet->{Cells}[0][$col];
      #my $cellValue = $cellObj->Value; # TODO - trimSpaces?
      #my $origCellValue = $cellObj->Value; # header name direct from template
      my $origCellValue = $cellValue;

      $cellValue =~ s/ //g;  # remember, no spaces in header names...

      if ( '' eq $cellValue ) { # shouldn't happen..
         assert("undefined cellValue (col=$col)");
      }

      report("Looking for column($cellValue)");
      if ( defined $templateHeader->{"$cellValue"} ) {
         my $expectedColumn = $templateHeader->{"$cellValue"};
         $foundColumns++;
         delete $columnsNotFound{"$cellValue"};
         if ( $expectedColumn != $col ) {
            report("   WARNING: column '$origCellValue', expected $expectedColumn, got $col");
         } else {
            report("   OK: column '$origCellValue' is in column $col");
         }

die("empty cellValue while processing header (col=$col)") if ( '' eq $cellValue );

         #------------------------------------------------------------------
         # Store the actual column # associated with the column header.
         # We'll use this information when it comes time to parse the 'data'
         # hash.
         #------------------------------------------------------------------
         report("FYI: storing '$cellValue' in col($col)");
         $columnMap->{$col} = $cellValue;

      } else {
         report("   ERROR: Did not find column '$origCellValue' (col=$col) "
            . "in templateHeader");
      }

      $col++;
   }#column loop (header)

   if ( $foundColumns < (keys %{$templateHeader}) ) {
      report("This template is missing the following columns:");
      foreach my $k (keys %columnsNotFound) {
         my $v = $columnsNotFound{$k};
         report("DEBUG: key($k)   v($v)");
      }
   }

   #----------------------
   # Process the data rows
   #----------------------
#   for( my $row = $workSheet->{MinRow} + 1;
#        $row <= $workSheet->{MaxRow};
#        $row++ )
   my $row=0;
   my $blankRowCount=0;
   while( my $record = <$file> ) {
      chomp $record;
      $row++;

      my $dataRow = {};
      $dataRow->{rowid} = $row+1; # rowid is the row# that the user sees

      my @fields = split("\t", $record);
      @fields = trimquotes(@fields);
      @fields = escapequotes(@fields);

      #----------------------------------------------------------------------
      # In some files (esp. those that originated in Excel), there may be
      # "extra" blank lines that don't contain any data but whose row number
      # is less than the maximum number of rows.  We check each row to see if
      # contains any data or if it's just a bunch of empty cells.  If the
      # latter then we don't store anything.
      #----------------------------------------------------------------------
      my $rowIsBlank=1;
      foreach my $cellValue (@fields) {
         undef $rowIsBlank if ( "" ne $cellValue );
      }
      if( $rowIsBlank ) {
         $blankRowCount++;
         next;
      }

      #------------------------------------------
      # Store each data row in the 'dataRow' hash
      #------------------------------------------
      $col = 0;
      foreach my $rawCellValue (@fields) {
         my $cellValue;
         if ( defined $rawCellValue ) {

            $cellValue = $rawCellValue;

            #--------------------------------------
            # Some fields may need special handling
            #--------------------------------------

            # NOTE: we don't do any special processing with the tab-limited
            # fields.

            my $excelRow = $row + 1;
            my $excelCol = $col + 1;

            $cellValue = Common::UTF8::Encode($cellValue);

         } else {
            #die("no cellObj at ($row,$col) ?");
         }

         my $key = $columnMap->{$col};

         #----------------------------------------------------------------
         # If the key isn't defined, then the template likely has a column
         # that we don't care about (e.g., an extra column).
         #----------------------------------------------------------------
         next if ( ! $key );

#         if ( ! $key ) {
#            report("null key at row($row) col($col)?");
#            foreach my $k (sort {$a <=> $b} keys %$columnMap) {
#               my $v = $columnMap->{$k};
#               report("k($k) v($v)");
#            }
#         }

         $dataRow->{$key} = $cellValue;

         $col++;
      }# column loop

      #---------------
      # Store the data
      #---------------
      push @{$data->{rows}}, $dataRow;

   }# row loop

}#_readTabInputFile

sub trimquotes {
   my @string = trimspaces(@_);

   for (@string) {
      if (m/^\".*\"$/) {
         s/^\"//;
         s/\"$//;
      }
   }

   return wantarray ? @string : $string[0];
}

sub escapequotes {
   my @string = @_;

   for (@string) {
      s/\"\"/"/g;
   }

   return wantarray ? @string : $string[0];
}

sub report {
   my($text, $level) = @_;
   $level = kNormal unless $level;
   if ( $level <= $gReportLevel ) {
      print $text . "\n";
   }
}

1;
