package Support::Implementation::MCPSLicenseTemplate;
#-----------------------------------------------------------------
# MCPSLicenseTemplate -- Template to import UK Mechanical Licenses
#
# Change History:
# 7/7/10 - Fixed license arguments to include MCPS ID
# 7/12/10 - Fixed MCPS ID handling in createMCPSLicense
# - Added support for Product Type
# 7/20/10 - Fixed bug (initial periods = 0 wasn't being handled
#   properly); added additional debug code.
# 3/25/11 - Corrected logic to properly detect existing licenses.
# 3/28/11 - Limit product search to physical products only
# 10/11/11 - Made MCPS ID optional per FB14845 (originally made
#   optional in FB13775).  Note that the template still has the
#   column name prefixed with '*'.
#   TODO: Add rsProductID to resolve duplicate product codes
# 1/8/13 - Fixed uninitialized variable.
# 2/20/17 - Prevent multiple MCPS licenses on the same product
#-----------------------------------------------------------------
use strict;
use warnings;

use lib '/app/tools/common/lib';
use lib '/app/tools/rps/lib';

use IO::File;
use Data::Dumper;
use Date::Calc;

use Spreadsheet::ParseExcel;

use Common::Assert;
use Common::UTF8;
use Common::RSApp;
use Common::Consts;
use Common::CurrencyFormat;
use Common::Util qw( clean trimspaces);

use Support::Implementation::ExcelReader;
use Support::Implementation::Excel2007Reader;
use Support::Implementation::TabDelimitedReader;
use Support::Implementation::SearchUtil;
use Support::Implementation::ImplementationUtil qw( report appendString checkMissingColumn printNull );

use RPS::DB::Item::McpsLicense;
use RPS::DB::Item::Product;
use RPS::DB::Item::Payor;

use base 'Support::Implementation::Template';

use constant kQuiet  => 0;
use constant kNormal => 1;
use constant kDebug  => 3;
my $gReportLevel = kNormal;

binmode STDOUT, ":utf8";

#-----------------------------------------------------------------------
# templateHeader maps column names to their default column number.
# The actual column number-header name map is stored in columnMap.
# Note(s):
# 1) Column names are case sensitive
# 2) Spaces are ignored; you must manually remove spaces from the names
#    stored in templateHeader
#
# 6/4/10: gTemplateHeader is obsolete?
#-----------------------------------------------------------------------
my %gTemplateHeader = (
   "Royaltyshare Album ID" => 0,  # A
   "*Album Title"          => 1,  # B
   "*Catalog Number"       => 2,  # C
   "*Product ID"           => 3,  # D
   "*MCPS ID"              => 4,  # E
   "*Payor Name"           => 5,  # F
   "Royaltyshare Payor ID" => 6,  # G
   "TV Advertised"         => 7,  # H
   "DVD Category"          => 8,  # I
);

#--------------------------------------------------------------
# Maps column #'s to a unique key (headername).  The reverse of
# templateHeader, but with actual column #.
#--------------------------------------------------------------
my %gColumnMap = ();


#----------------------------------
# excount keeps track of exceptions
#----------------------------------
my %gExCount;

#--------------------------------------
# gCount keeps track of entities created
#--------------------------------------
my %gCount;

#-------------------------------
# Reference to SearchUtil object
#-------------------------------
my $gSearchObj;

my $gDefaultPayorID;
my %gPayorMap = (); # maps names to ID

my $clientID;
my $execMode;

my $dbo;
my $cdbo;
my $dbh;

sub new {
   my ($class, %args) = @_;
   my $self = bless {}, $class;
   return $self->_init(%args);
}


sub _init {
   my( $self, %args ) = @_;

   report("PublisherTemplate::_init -- args = ". Dumper(\%args));

   $self->SUPER::_init(%args);

   $execMode = $self->exec_mode if ( $self->exec_mode );

   return $self;
}

sub parseHeader {
   my $self = shift;
}

#----------------------------------------------
# Read-in all of the template data into memory.
#----------------------------------------------
sub loadMemory {
   my $self = shift;

   $clientID = $self->client_id;
   my $app = Common::RSApp->new(clientID => $clientID);

   $dbo = Common::RSApp::GetClientDB();
   $dbh = $dbo->DBH;
   $cdbo = Common::RSApp::GetCommonDB();

   my $fileName = $self->name;

   $gSearchObj = Support::Implementation::SearchUtil->new(
      clientID => $clientID,
   );

   my %data;
   if( $self->isExcel2003( $fileName ) ) {
      print("MCPSLicenseTemplate::loadMemory -- loading Excel2k3 $fileName into memory\n");

      # Read in the header
      my $reader = Support::Implementation::ExcelReader->new(
         filename => $fileName,
         data => \%data,
         header => \%gTemplateHeader,
         columnmap => \%gColumnMap
      );
      $reader->scanExcelFile;

      # Try and parse it...
      _processData(\%data);
   } else {
      print("ArtistContractTemplate::loadMemory -- loading Excel2k7 $fileName into memory\n");

      #--------------------------
      # Read in the 1st worksheet
      #--------------------------
      my $reader = Support::Implementation::Excel2007Reader->new(
         filename => $fileName,
         data => \%data,
         header => \%gTemplateHeader,
         columnmap => \%gColumnMap,
         tab => 1,
      );
      $reader->scanExcelFile;
      # Try and parse it...
      _processData(\%data);

#   } elsif( $self->isTabDelimited( $fileName ) ) {
#      report("MCPSLicenseTemplate::loadMemory -- processing tab-delimited file");
#
#      my %data;
#      my $reader = Support::Implementation::TabDelimitedReader->new(
#         filename => $fileName,
#         data => \%data,
#         header => \%gTemplateHeader,
#         columnmap => \%gColumnMap
#      );
#
#      $reader->scanTabbedFile;
#      _processData(\%data);
   }
}

#-------------------------------------------------------------------
# _processData is where the real work is done.  It takes the generic
# information stored in the supplied array of hashes and decodes it.
# In this case, it assumes that the supplied data contains license
# data.
#-------------------------------------------------------------------
sub _processData {
   my($data) = @_;
   my $rows = $data->{rows};

   #----------------------------------------
   # Reset the exception and entity counters
   #----------------------------------------
   #%gExCount = ();
   #%gCount = ();

   #------------------------
   # Setup payor information
   #------------------------
   my $sql = "SELECT payor_id,name,is_default FROM payor";
   my $sth = $dbo->DoCmd($sql);
   while( my($id,$name,$isDefault) = $sth->fetchrow_array() ) {
      #$gPayorMap{$name} = $id;
      $gPayorMap{$id} = lc $name;
      if ( $isDefault ) {
         $gDefaultPayorID = $id;
      }
   }
   if ( not defined $gDefaultPayorID ) {
      die("No default payor setup for client");
   }

   #---------------------------------
   # Get the client's currency format
   #---------------------------------
   $sql = "SELECT country_code FROM client WHERE client_id=$clientID";
   $sth = $cdbo->DoCmd($sql);
   my($cCode) = $sth->fetchrow_array();
   my $currencyFormat = new Common::CurrencyFormat( countryCode => $cCode );
   my $denomination = $currencyFormat->currencyCode();

   if ( !$denomination ) {
      die("ERROR: Unable to find currency denomination for client($clientID)");
   }

   foreach my $row (@$rows) {

      #======================================================================
      # Read in the column information.
      #
      # IMPORTANT: Except for 'rowid', the hash names _MUST_ match the column
      # names in the template.
      #======================================================================
      my $rowid              = $row->{'rowid'};
      my $rsAlbumID          = $row->{'RoyaltyShare Album ID'};      # A
      my $albumTitle         = $row->{'*Album Title'};               # B
      my $catalogNumber      = $row->{'*Catalog Number'};            # C

      my $productCode        = $row->{'*Product ID'};                # D
      my $productType        = $row->{'*Product Type'};              # D - Must be a valid RS type. - TODO

      my $mcpsID             = $row->{'*MCPS ID'};                   # E
      my $payorName          = $row->{'*Payor Name'};                # F
      my $rsPayorID          = $row->{'Royaltyshare Payor ID'};      # G
      my $tvAdvertised       = $row->{'TV Advertised'};              # H
      my $dvdCategory        = $row->{'DVD Category'};               # I
      my $periodsCompleted   = $row->{'*Initial Periods Completed'}; # J

      my $errorCode;

      # Remove trailing spaces
      $albumTitle    =~ s/\s*$//g if ( $albumTitle );
      $catalogNumber =~ s/\s*$//g if ( $catalogNumber );
      $productCode   =~ s/\s*$//g if ( $productCode );
      $mcpsID        =~ s/\s*$//g if ( $mcpsID );
      $payorName     =~ s/\s*$//g;
      $tvAdvertised  =~ s/\s*$//g if ( $tvAdvertised );

      if ( $dvdCategory ) {
         $dvdCategory =~ s/\s*$//g;
         $dvdCategory = uc $dvdCategory;
      }

      report("#### row($rowid): " . Dumper(\%$row));


      #--------------------------------------------------------------------
      #
      # Validate the album information.
      #
      #--------------------------------------------------------------------
      my $errFlag;
      my $albumID; # This will contain the RS album ID

      if ( !$albumTitle ) {
         appendString( $errorCode, "Missing album title");
         ++$gExCount{missing_album_title};
         $errFlag = 1;
      }

      if ( !$catalogNumber ) {
         appendString( $errorCode, "Missing catalog number");
         ++$gExCount{missing_catalog_number};
         $errFlag = 1;
      }

      if ( $rsAlbumID ) {

         my $o = RPS::DB::Item::Album->Lookup( album_id => $rsAlbumID );
         if ( !$o ) {

            appendString( $errorCode, "Invalid album ID");
            ++$gExCount{invalid_album_id};

         } else {

            if ( $albumTitle and $albumTitle ne $o->title ) {
               appendString( $errorCode, "Album title mismatch");
               ++$gExCount{album_title_mismatch};
               $errFlag = 1;
            }

            if ( $catalogNumber and $catalogNumber ne $o->catalog_number ) {
               appendString( $errorCode, "Catalog number mismatch");
               ++$gExCount{catalog_number_mismatch};
               $errFlag = 1;
            }

            $albumID = $o->album_id if ( !$errFlag );
         }

      } else {

         if ( !$errFlag ) {
            # Find the album using the name and catalog number
            my %args = (
               album_name     => $albumTitle,
               catalog_number => $catalogNumber,
            );

            ($albumID, $errFlag) = $gSearchObj->findAlbum( %args );

            if ( $errFlag ) {
               report("   errFlag($errFlag) ***");
               if ( $errFlag == Support::Implementation::SearchUtil::kAlbumNotFound ) {
                  report("ERROR: findAlbum returned album not found : ". Dumper(\%args) );
                  appendString( $errorCode, "Album not found");
                  ++$gExCount{album_not_found};
               } elsif ( $errFlag == Support::Implementation::SearchUtil::kNonUniqueAlbumName ) {
                  appendString( $errorCode, "Non-unique album name");
                  ++$gExCount{nonunique_album_name};
               } else {
                  die("ERROR: _findAlbum returned unknown errorFlag($errFlag)\n");
               }
            } else {
               #report("   albumID($albumID)");
            }

         }
      }


      #--------------------------------------------------------------------
      # Validate the product information.
      #--------------------------------------------------------------------
      my $productIsDVD; # set if product is DVD
      my $productID; # set if product found
      if ( $albumID ) {
         if ( !$productCode ) {
            report("_processData:DEBUG: Missing product ID");
            appendString( $errorCode, "Missing product ID");
            ++$gExCount{missing_product_id};
         } else {
            #my $o = RPS::DB::Item::Product->Lookup(
            #   product_code => $productCode,
            #   asset_id     => $albumID,
            #);
            #if ( !$o ) {
            #   report("_processData:DEBUG: product '$productCode' not found");
            #   appendString( $errorCode, "Product not found");
            #   ++$gExCount{product_not_found};
            #} else {
            #   my $productTypeID = $o->product_type_id;
            #   $productID = $o->product_id;

            #   report("_processData:DEBUG: product '$productCode' --> productID($productID)");

            #   if ( $productTypeID == RPS::DB::Item::Product::kProductTypeDVD ||
            #        $productTypeID == RPS::DB::Item::Product::kProductTypeDVDCDSet )
            #   {
            #      $productIsDVD = 1;
            #   }
            #}

            #-------------------------------------------
            # Limit the search to physical products ONLY
            #-------------------------------------------
            my $sql = "SELECT product_id, product_type_id FROM product WHERE product_code=? AND asset_id=$albumID "
               . "AND product_type_id NOT IN (3,4)";
            my $sth = $dbh->prepare($sql);
            $sth->execute($productCode);
            if ( $sth->rows == 1 )
            {
               my( $_productID, $_productTypeID ) = $sth->fetchrow_array();
               $productID = $_productID;

               if ( $_productTypeID == RPS::DB::Item::Product::kProductTypeDVD ||
                    $_productTypeID == RPS::DB::Item::Product::kProductTypeDVDCDSet )
               {
                  $productIsDVD = 1;
               }
            }
            elsif( $sth->rows > 1 )
            {
               appendString( $errorCode, "Multiple products found");
               ++$gExCount{multiple_products_found};

            }
            else
            {
               report("_processData:DEBUG: product '$productCode' not found");
               appendString( $errorCode, "Product not found");
               ++$gExCount{product_not_found};
            }

         }
      } else {
         report("_processData:DEBUG: albumID not set -- skipping product lookup");
      }


#      report("   DEBUG: albumID($albumID) productID($productID) "
#         . "isDVD(" . printNull($productIsDVD) . ")");

      #-------------------
      # Validate the payor
      #-------------------
      if ( $rsPayorID ) {

         if ( exists $gPayorMap{$rsPayorID} ) {

            if ( $payorName && ( lc $payorName ne $gPayorMap{$rsPayorID} ) ) {
               report("  PAYOR_VALIDATE: Payor name mismatch: '$payorName' doesn't match "
                  . "'" . $gPayorMap{$rsPayorID} . "'");
               appendString( $errorCode, "Payor name mismatch");
               ++$gExCount{payor_name_mismatch};
            }

         } else {
            report("  PAYOR_VALIDATE: PayorID not found");
            appendString( $errorCode, "PayorID not found");
            ++$gExCount{payorid_not_found};
         }

      } else {


         if ( $payorName ) {

            my $o = RPS::DB::Item::Payor->Lookup(
               name => $payorName
            );
            if ( !$o ) {
               report("  PAYOR_VALIDATE: Invalid payor name($payorName)");
               appendString( $errorCode, "Invalid payor name");
               ++$gExCount{invalid_payor_name};
            } else {
               $rsPayorID = $o->payor_id;
            }
         } else {
            report("  PAYOR_VALIDATE: Missing payor information");
            appendString( $errorCode, "Missing payor");
            ++$gExCount{missing_payor};
         }
      }

      #--------------------------------------------------------------------
      # Validate the retention category
      #--------------------------------------------------------------------
      my $retentionCategory;

      if ( $tvAdvertised ) {
         if( ($tvAdvertised =~ m/^y/i ) ) {
            $retentionCategory = 'T'; # TV Advertised
         } elsif( ($tvAdvertised =~ m/^n/i ) ) {
            $retentionCategory = 'S'; # Standard
         } else {
            report( "ERROR:row($rowid): Unknown retention category '$tvAdvertised'");
            appendString( $errorCode, "Unknown retention category");
            ++$gExCount{unknown_retention_category};
         }
      } else {
         $retentionCategory = 'S'; # Standard
      }


      #--------------------------------------------------------------------
      # Validate the dvd category
      #--------------------------------------------------------------------
      if ( $dvdCategory ) {
         
         if ( $productIsDVD ) {
            if ( ! _isValidDVDCategory( $dvdCategory ) ) {
               appendString( $errorCode, "Invalid DVD category");
               ++$gExCount{invalid_dvd_category};
            }
         } else {
            appendString( $errorCode, "DVD category specified but no DVD product");
            ++$gExCount{dvd_category_specified_without_dvd_product};
         }
      } else {
         if ( $productIsDVD ) {
            appendString( $errorCode, "Missing DVD Category");
            ++$gExCount{missing_dvd_category};
         }
      }

      #--------------------------------------------------------------------
      # Validate the periods completed
      #--------------------------------------------------------------------
      if ( not defined $periodsCompleted ) {
         appendString( $errorCode, "Missing periods completed");
         ++$gExCount{missing_periods_completed};
      } else {
         if ( $periodsCompleted !~ m/\d*/ ) {
            appendString( $errorCode, "Periods completed must be integer");
            ++$gExCount{invalid_periods_completed_format};
         } else {
            if ( $periodsCompleted < 0 or $periodsCompleted > 5 ) {
               appendString( $errorCode, "Periods must be 0 to 5");
               ++$gExCount{invalid_period_value};
            }
         }

      }

      #--------------------------------------------------------------------
      # Are there any other licenses for the same product but through a
      # different payor?
      #--------------------------------------------------------------------
      my $sql = "SELECT mcps_license_id,payor_id FROM mcps_license WHERE product_id=? AND payor_id !=?";
      my $sth = $dbh->prepare($sql);
      $sth->execute($productID, $rsPayorID);
      if ( $sth->rows >= 1 )
      {
          appendString( $errorCode, "Already licensed through another payor");
          ++$gExCount{already_licensed_other_payor};
          while( my($id,$payorID) = $sth->fetchrow_array() ) {
              print "  D: productID $productID is already licensed through payor $payorID with mcps_license_id $id\n";
          }
      }

      #=======================================
      # If there are any errors, we're done...
      #=======================================
      if ( $errorCode ) {
         report("_processData:DEBUG: error(s): $errorCode");
         $row->{'error-code'} = $errorCode;
         next;
      }


      #------------------------------------
      # Otherwise create the license!!!
      #------------------------------------
      my %args = (
         product_id         => $productID,
         payor_id           => $rsPayorID,
         retention_category => $retentionCategory,
         periods_completed  => $periodsCompleted,
         mcps_id            => $mcpsID,
      );

      # optional arguments
      $args{dvd_category} = $dvdCategory if ( $dvdCategory );

      my($licenseID, $err) = _createMCPSLicense( %args );

      if ( $err ) {
         $row->{'error-code'} = $err;
      } else  {
         $row->{'rs-license-id'} = $licenseID;
      }
   }

   #------------------------------------------------------------
   # Dump out the errors
   # TODO: Need to properly propagate the errors back to the user
   #------------------------------------------------------------
   _showExceptions( $rows );

   #-----------------------
   # Show import statistics
   #-----------------------
   report("##### S U M M A R Y #####");
   my $totalExceptions = 0;
   my $totalRows = (scalar @$rows);

   foreach my $c (keys %gExCount) {
      my $v = $gExCount{$c};
      printf("%30s %6d\n", $c, $v);
      $totalExceptions += $v;
   }
   printf("%30s %s\n", " ", "-------" );
   printf("%30s %6d\n", "Total Exceptions", $totalExceptions );
   printf("%30s %6d\n", "Total Rows", $totalRows );
   report(" ");
   
   foreach my $c (keys %gCount) {
      my $v = $gCount{$c};
      printf("%30s %d\n", $c, $v);
   }

}#_processData

#--------------------------------------------------------------
# _isValidDVDCategory: Check if the specified category is valid
# Returns: 1 if category is valid, undef otherwise.
#--------------------------------------------------------------
sub _isValidDVDCategory {
   my($c) = @_;
   my $retval;
   if ( (uc $c eq "A" ) or
        (uc $c eq "B" ) or
        (uc $c eq "C" ) or
        (uc $c eq "AVP" ) ) {
      $retval = 1;
   }
   return $retval;
}

#
# _createTransaction
#
sub _createMCPSLicense {
   my( %args ) = @_;
   
   my $productID          = $args{product_id};
   my $payorID            = $args{payor_id};
   my $retentionCategory  = $args{retention_category};
   my $periodsCompleted   = $args{periods_completed};
   my $mcpsID             = $args{mcps_id};

   my $dvdCategory        = $args{dvd_category};

   # Note: should we create a payee account if it doesn't exist?
   # If so, what min payment do we use?
   my $minPayment = 0; # only for new payee accounts

   assert($productID);
   assert($payorID);
   assert($retentionCategory);
   #assert($periodsCompleted);
   #assert($mcpsID);

   my $mcpsLicenseID; # set if we create a license
   my $err; # set if we can't create a license

   #------------------------
   # MCPS Arguments
   #------------------------

   # 3/25/11 - Based on the current schema, the productID and payorID
   #  must be unique.  We search for existing licenses and if none
   #  are found, create the license.
   my %a = (
      product_id                => $productID,
      payor_id                  => $payorID,
      #retention_category        => $retentionCategory,
      #initial_periods_completed => $periodsCompleted,
      #mcps_id                   => $mcpsID,
   );
   #$a{dvd_category} = $dvdCategory if ( $dvdCategory );

   my $o = RPS::DB::Item::McpsLicense->Lookup( %a );
   if ( $o ) {

      my $id = $o->mcps_license_id;
      report("EXISTS: mcps_license $id");
      $err = "License exists";
      ++$gExCount{license_exists};

   } else {

      # Add in the rest of the license data
      #
      $a{retention_category}        = $retentionCategory;
      $a{initial_periods_completed} = $periodsCompleted;
      $a{mcps_id}                   = $mcpsID if ( $mcpsID );
      $a{dvd_category} = $dvdCategory if ( $dvdCategory );

      if ( $execMode ) {
         $o = RPS::DB::Item::McpsLicense->Create( %a );
         $o->save();
         $mcpsLicenseID = $o->mcps_license_id;
         report("   Created mcps_license $mcpsLicenseID : ". Dumper(\%a));
         ++$gCount{mcps_license};
      } else {
         report("   Non-exec mode, skipped mcps_license : ". Dumper(\%a));
      }
   }

   return($mcpsLicenseID, $err);

} #_createMCPSLicense

#--------------------------------------------------------------------------
# _showExceptions was originally intended to _just_ show the template lines
# that exceptioned out.  It's been modified to output both exception and
# non-exception lines.  Exception message(s) will be placed in the error
# code column.  If a line is imported successfully, then the resulting
# payeeID will be stored in the import status column ("payeeID(###)"),
# otherwise this column will contain the string "__FAILED__".
#--------------------------------------------------------------------------
sub _showExceptions {
   my ( $rows ) = @_;

   #---------------------------
   # Build the exception header
   #---------------------------
   my @header;
   for( my $i = 0; $i < (keys %gColumnMap); $i++ ) {
      # Get the key at the specified column
      my $v = $gColumnMap{$i};

      #----------------------------------------------------------
      # Do _not_ push the "Error Code" or "import-status" columns
      # if they were in the original template.  We'll re-create
      # them on the fly.
      #----------------------------------------------------------
      next if ( ("Error Code" eq $v) || ("import-status") eq $v );

      push @header, $v;
   }

   report("STATUS:\t".join("\t", @header, "Error Code", "import-status"));

   #--------------------------------------
   # Now dump out the rows that had errors
   #--------------------------------------
   foreach my $row (@$rows) {
      my $rowid = $row->{rowid};
report("dumping row($rowid)");

      my $errorCode = $row->{'error-code'};
      my $licenseID = $row->{'rs-license-id'};

      #----------------------------------------
      # Output the row data in the proper order
      #----------------------------------------
      my @obuf;
      for( my $i = 0; $i < (keys %gColumnMap); $i++ ) {
         # Get the key at the specified column
         my $v = $gColumnMap{$i};
         my $val = ($row->{$v}) ? $row->{$v} : '';

         #----------------------------------------------------------
         # Do _not_ push the "Error Code" or "import-status" columns
         # if they were in the original template.
         #----------------------------------------------------------
         next if ( ("Error Code" eq $v) || ("import-status") eq $v );

         push @obuf, $val;
      }

      $licenseID = "NULL" if ( !$licenseID );

      my $importStatus;
      if ( $errorCode ) {
         $importStatus = "__FAIL__";
      } else {
         $importStatus = "license($licenseID)";
      }
      #my $importStatus = ($errorCode) ? "__FAIL__" : "payee($payeeID)";

      my $ecString = ($errorCode) ? $errorCode : '';

      my $zzbuf = join("\t", "STATUS:", @obuf, $ecString, $importStatus);

      report($zzbuf);
   }
}#_showExceptions

#----------------------------------------------------------------------
# _normalizeDate convert the template date into a MySQL-compatible date
#----------------------------------------------------------------------
sub _normalizeDate {
   my($dt) = @_;
   my($month,$day,$year) = split("/",$dt);
   return join("-", $year, $month, $day);
}

#---------------------------------------------------
# _isValidDateFmt - return true if the supplied date
# is in the format YYYY-MM-DD, false otherwise
#---------------------------------------------------
sub _isValidDateFmt {
   my($dstr) = @_;
   my($yr,$mo,$dy) = split("-",$dstr);
   my $st = 1; # valid unless we detect otherwise

   $st = 0 if ( !$yr || !$mo || !$dy );
   $st = 0 if ( $mo && ( $mo !~ /^\d+$/ ));
   $st = 0 if ( $yr && ( $yr !~ /^\d+$/ ));
   $st = 0 if ( $dy && ( $dy !~ /^\d+$/ ));
   $st = 0 if ( length($yr) != 4 );
   return $st;
}

#sub _appendString {
#   my($str,$v) = @_;
#
#   if ( $str ) {
#      my $cur = $str;
#      my $newstring = "$cur; $v";
#      $_[0] = $newstring;
#   } else {
#      $_[0] = $v;
#   }
#   return;
#}# _appendString

sub _reportError {
   my ($name, $obj) = @_;

   report("   _reportError: checking '$name' for errors");
   if ($obj && $obj->_hasError()) {
      #my ($e, $msg) = $obj->getError();
      #print "$name has an error: $e : $msg\n";

      my %xmlParams = $obj->getXMLParams();
      my $msg = defined $xmlParams{emsg} ? $xmlParams{emsg} : "MSG_NOT_AVAILABLE";
      my $e = $xmlParams{e};
      print "$name has an error:: $e :: $msg\n";
      return 1;
   }
   return undef;
}

#sub report {
#   my($text, $level) = @_;
#   $level = kNormal unless $level;
#   if ( $level <= $gReportLevel ) {
#      print $text . "\n";
#   }
#}

1;
