#!/usr/bin/perl

use strict;
use Getopt::Std;
use IO::File;
use Date::Calc;
use Data::Dumper;

use lib '/app/tools/common/lib';
use Common::Util qw(decodeDateMysql trimspaces clean normalize_upc normalize_isrc);
use Common::RSDB;
use Common::RSApp;
use Common::Assert;

use lib '/app/tools/rps/lib';
use RPS::XMLObject;
use RPS::DB::Item::Track;
use RPS::DB::Item::Album;
use RPS::DB::Item::Product;
use RPS::DB::Item::Master;
use RPS::DB::Item::Publisher;
use RPS::DB::Item::TrackLicense;

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

use constant kProgressQuanta => 200;

# if two tracks on the same album have the same title,
# duration is the only distinguishing property. In this
# case the import data might be slightly different from the
# database data so we'll allow a +/- tolerance in matching.
use constant kDurationTolerance => 5;

# we're skipping some publishers (DIGITAL ONLY)
#   - ABKCO MUSIC INC (P11300)
#   - ABKCO MUSIC INC PETE TOWNSHEND CATALOG (P20061)
my @skipPublishers = qw();

# Parse the command-line.
#
my ( $clientID, $inputFile ) = parseCommandLine();

$| = 1;

# Instantiate the application singleton object.
#
my $appSingleton = Common::RSApp->new( clientID => $clientID );

# Scan the input file, and build the data structure of updates.
#
my %updates;
scanInputFileForUpdates( $inputFile, \%updates );

# Commit any necessary updates.
#
processUpdates( \%updates );

#
# --- Subroutines
#

sub parseCommandLine {
    my %opt;
    getopts( 'c:f:p', \%opt );

    my $inputFile = $opt{f};
    my $clientID  = $opt{c};

    unless ( $clientID =~ /^\d+$/ && $clientID > 0 ) {
        die usage("You must specify a valid client ID");
    }

    unless ($inputFile) {
        die usage("You must specify a catalog file to import");
    }

    unless ( -e $inputFile ) {
        die usage("The specified catalog file does not exist");
    }

    return ( $clientID, $inputFile );
}

sub usage {
    my $errstr = shift;
    my $text = ($errstr) ? "ERROR: $errstr\n" : '';
    $text .= "Usage: $0 -c <clientID> -f <inputFile>\n";
    return $text;
}

sub scanInputFileForUpdates {
    my ( $inputFilePath, $updates ) = @_;
    assert($inputFilePath);
    assert($updates);

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

    readTabInputFile( $inputFile, $updates );

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

sub readTabInputFile {
    my ( $file, $updates ) = @_;
    assert($file);
    assert($updates);

    report("\nReading input file...");

    my $recordCount = 0;
    my $skipCount   = 0;

    # Process the file line by line
    #
    while ( my $record = <$file> ) {
        my @fields;
        my $licenseType;
        my $artistName;
        my $albumTitle;
        my $catalogNumber;
        my $upc;
        my $isrc;
        my $productConfig;
        my $songCode;
        my $duration;
        my $songTitle;
        my $dateSent;
        my $licenseDate;
        my $dateSigned;
        my $topPublisherNumber;
        my $topPublisherName;
        my $publisherNumber;
        my $publisherName;
        my $publisherShare;
        my $licenseNumber;
        my $rate;
        my $balanceFwd;

        $recordCount++;

        if ( 0 == $recordCount % kProgressQuanta ) {
            report("  record $recordCount ... ");
        }

        # Split the line up into fields.
        #
        chomp $record;
        @fields = split( "\t", $record );
        @fields = trimquotes(@fields);
        @fields = escapequotes(@fields);

        # Note - clientAlbumProductID is coming from the 'albumID' field,
        # and clientTrackProductID is coming from the 'trackID' field.
        # I'm not positive that this mapping is correct, or even
        # meaningful, but I do believe that the original values _are_ just
        # client-specified meta-data... so, it may make sense.
        # In any event, we need _something_ to put in the
        # 'product_code' column in the RPS product table, so this is
        # as good as anything...
        #
        (
            undef,               $licenseType,      undef,            undef,          $artistName,     $albumTitle,
            $catalogNumber,      $upc,              $isrc,            $productConfig, undef,           $duration,
            $songTitle,          undef,             $songCode,        $dateSent,      $licenseDate,    $dateSigned,
            $topPublisherNumber, $topPublisherName, $publisherNumber, $publisherName, $publisherShare, undef,
            $licenseNumber,      undef,             $rate,            undef,          undef,           $balanceFwd,
            undef,               undef
        ) = undefspaces(@fields);

        $isrc = normalize_isrc($isrc);

        # trim leading zeros
        $upc = _myNormalizeUPC($upc);
        my $altUPC = _myNormalizeUPC($catalogNumber);
        $publisherShare =~ s/%//;

        # we aren't saving licenses with share of 0.00%.
        #
        if ( abs($publisherShare) && ( $catalogNumber || $altUPC || $upc || $isrc ) && $songCode && $publisherNumber ) {
            my $newLicense = {};
            $newLicense->{trackLicenseID}     = undef;
            $newLicense->{lineNum}            = $recordCount + 1;             # the original excel file started on row 2, adjust here
            $newLicense->{songCode}           = $songCode;
            $newLicense->{licenseType}        = $licenseType;
            $newLicense->{artistName}         = $artistName;
            $newLicense->{albumTitle}         = $albumTitle;
            $newLicense->{catalogNumber}      = $catalogNumber;
            $newLicense->{altUPC}             = $altUPC;
            $newLicense->{upc}                = $upc;
            $newLicense->{isrc}               = $isrc;
            $newLicense->{productConfig}      = $productConfig;
            $newLicense->{duration}           = $duration;
            $newLicense->{songTitle}          = $songTitle;
            $newLicense->{dateSent}           = _convertDate($dateSent);
            $newLicense->{licenseDate}        = _convertDate($licenseDate);
            $newLicense->{dateSigned}         = _convertDate($dateSigned);
            $newLicense->{topPublisherNumber} = $topPublisherNumber;
            $newLicense->{publisherNumber}    = $publisherNumber;
            $newLicense->{publisherShare}     = $publisherShare;
            $newLicense->{licenseNumber}      = $licenseNumber;
            $newLicense->{rate}               = $rate;
            $balanceFwd =~ s/,//g;
            $newLicense->{advance_amount} = $balanceFwd;

            push @{ $updates->{licenses} }, $newLicense;

            # -jff- I forget why this line is here
            #
            # report(join('\t', sort keys %$newLicense)) if($recordCount == 2);
        } else {
            report(
                "!Warning (line " . $recordCount . "): Incomplete data for song=$songTitle, code=$songCode, publisher#=$publisherNumber" );
            $skipCount++;
        }

        # regular publisher
        #
        if ( $publisherNumber && $publisherName ) {
            if ( !exists( $updates->{publishers}->{$publisherNumber} ) ) {

                # $updates->{publishers}->{$publisherNumber}->{publisherID} = "";
                # $updates->{publishers}->{$publisherNumber}->{isAdmin} = "";
                # $updates->{publishers}->{$publisherNumber}->{adminNumber} = "";
                $updates->{publishers}->{$publisherNumber}->{publisher_name} = $publisherName;
            }
        } else {
            report( "!Warning (line " . $recordCount . "): Incomplete publisher", kDebug );
        }

        # top (admin) publisher
        #
        if ( $topPublisherNumber && $topPublisherName ) {
            if ( !exists( $updates->{publishers}->{$topPublisherNumber} ) ) {
                $updates->{publishers}->{$topPublisherNumber}->{publisher_name} = $topPublisherName;
            }

            if ( $publisherName && ( $publisherName ne $topPublisherName ) ) {

                # mark this publisher as an admin
                #
                $updates->{publishers}->{$topPublisherNumber}->{isAdmin} = 1;

                # set the regular publisher's admin as this publisher
                #
                $updates->{publishers}->{$publisherNumber}->{adminNumber} = $topPublisherNumber;
            }
        } else {
            report( "!Warning (line " . $recordCount . "): Incomplete top publisher", kDebug );
        }

    }
    report("  Records processed : $recordCount");
    report("  Records skipped   : $skipCount");
}

sub processUpdates {
    my ($updates) = @_;

    my $publishers = $updates->{publishers};

    # Create publisher entries
    #
    report("\nCreating Publishers...");
    my $count = 0;

    # we need to create an entry for Harry Fox first
    #
    # my $hfa = RPS::DB::Item::Publisher->Create(publisher_name => "HARRY FOX AGENCY",
    # is_agency => 1,
    # is_admin => 0);
    # $hfa->save();
    # my $hfaID = $hfa->publisher_id;

    # HFA has publisher_id=1, we don't need to create it again. -jff-
    #
    my $hfaID = 1;

    my $pubCreateCount = 0;
    my $pubMatchCount  = 0;
    foreach my $publisherNumber ( sort { $publishers->{$b}->{isAdmin} <=> $publishers->{$a}->{isAdmin} } keys %$publishers ) {
        $count++;
        if ( 0 == $count % kProgressQuanta ) {
            report("  publisher $count ...");
        }

        my $publisher = $publishers->{$publisherNumber};

        # have we already created/matched this publisher?
        #
        if ( !$publisher->{publisherID} ) {

            # Does this publisher already exist?
            #
            my $coll = RPS::DB::Item::Publisher->Match( $publisher->{publisher_name} );
            if ( defined $coll && $coll->hasNext() ) {
                my $dbItem = $coll->next();
                $publisher->{publisherID} = $dbItem->publisher_id();
                $pubMatchCount++;

                # is this a new admin?
                #
                if ( $publisher->{isAdmin} == 1 && !$dbItem->is_admin() ) {
                    $dbItem->is_admin(1);
                    $dbItem->save();
                    report( "!Update (Pub# $publisherNumber): publisher set to Admin, publisher = " . $publisher->{publisher_name} );
                }

                next;
            }

            my %pubParams = (
                publisher_name => $publisher->{publisher_name},
                is_agency      => 0,
                is_admin       => 0,
                agent_id       => $hfaID,
            );

            if ( $publisher->{adminNumber} ) {

                # does this publisher have an admin?
                #
                $pubParams{admin_id} = $publishers->{ $publisher->{adminNumber} }->{publisherID};
            } elsif ( $publisher->{isAdmin} == 1 ) {

                # is this an admin?
                #
                $pubParams{is_admin} = 1;
            }

            # report("Creating publisher: ".Dumper(\%pubParams), kDebug);

            my $dbItem = RPS::DB::Item::Publisher->Create(%pubParams);
            $dbItem->save();
            $publisher->{publisherID} = $dbItem->publisher_id;
            $pubCreateCount++;
        }

    }    # END - publishers loop

    report("  Publishers created : $pubCreateCount");
    report("  Publishers matched : $pubMatchCount");

    # Create license entries
    #
    my $tracks      = {};
    my $licenses    = $updates->{licenses};
    my %seenLicense = ();

    report("\nCreating Licenses...");

    my $matchCount = 0;
    $count = 0;
    foreach my $license (@$licenses) {
        $count++;
        if ( 0 == $count % kProgressQuanta ) {
            report("  license $count ...");
        }

        # SKIP these publishers:
        #
        my $publisherNumber    = $license->{publisherNumber};
        my $topPublisherNumber = $license->{topPublisherNumber};
        if ( grep( /^$publisherNumber$/, @skipPublishers ) || grep( /^$topPublisherNumber$/, @skipPublishers ) ) {
            report( "!Warning (line " . $license->{lineNum} . "): Skipping publisher=$publisherNumber (admin: $topPublisherNumber)" );
            next;
        }

        # did we already create this license?
        #
        if ( !$license->{trackLicenseID} ) {
            #
            # We need to identify this track before we can assign
            # a license to it.
            #
            my $trackID;
            my $trackKey = join( '|',
                $license->{albumTitle}, $license->{catalogNumber}, $license->{upc}, $license->{songCode},
                $license->{songTitle},  $license->{isrc},          $license->{duration}, );

            if ( !exists( $tracks->{$trackKey} ) ) {
                $trackID = _deriveTrackIDFromLicense($license);
            } else {
                $trackID = $tracks->{$trackKey};
                report( "TrackID already found for trackKey = $trackKey", kDebug );
            }

            if ($trackID) {
                report( "Song matched!", kDebug );

                # store this for future reference
                #
                $tracks->{$trackKey} = $trackID;

                my %licenseParams = (
                    track_id          => $trackID,
                    date_sent         => $license->{dateSent},
                    date_received     => $license->{licenseDate},
                    date_issued       => $license->{dateSigned},
                    issuer_license_id => $license->{licenseNumber},
                    type              => 1,
                    rate_basis        => 1,
                    free_goods        => 15,                          # Sanctuary physical license specific
                                                                      # percentage_of_sales => 100,
                );

                # all the balances are negative
                #
                if ( $license->{advance_amount} < 0 ) {
                    $licenseParams{advance_amount} = $license->{advance_amount} * -1;
                }

                my $rateType = _deriveRateType( $license->{rate} );
                if ($rateType) {
                    $licenseParams{rate_type} = $rateType;
                } else {
                    report( "!Warning (line "
                          . $license->{lineNum}
                          . "): unknown rate basis ("
                          . $license->{rate}
                          . ") for song = "
                          . $license->{songTitle} );
                    next;
                }

                if ( $rateType == RPS::DB::Item::TrackLicense::kRateTypePenny() ) {

                    # just assign the penny rate
                    #
                    # $licenseParams{penny_rate} = $license->{rate};
                    $licenseParams{penny_rate}          = sprintf( "%.4f", $license->{rate} );
                    $licenseParams{rate_basis}          = undef;
                    $licenseParams{percentage_of_sales} = 100;
                } else {

                    # otherwise, figure out the percent rate
                    #
                    my $percentRate;
                    $percentRate = _deriveRate( $license->{rate} );
                    if ($percentRate) {
                        $licenseParams{percentage_of_sales} = $percentRate;
                    } else {
                        report( "!Warning (line "
                              . $license->{lineNum}
                              . "): unknown percent of rate ("
                              . $license->{rate}
                              . ") for song = "
                              . $license->{songTitle} );
                        next;
                    }
                }

                my $productTypeID = _deriveProductType( $license->{productConfig} );
                if ($productTypeID) {
                    $licenseParams{product_type_id} = $productTypeID;
                } else {
                    report( "!Warning (line "
                          . $license->{lineNum}
                          . "): unknown license type ("
                          . $license->{productConfig}
                          . ") for song = "
                          . $license->{songTitle} );
                    next;
                }

                my $share;
                if ( $license->{publisherShare} ) {
                    $licenseParams{share} = $license->{publisherShare};
                } else {
                    report( "!Warning (line " . $license->{lineNum} . "): missing share for song = " . $license->{songTitle} );
                    next;
                }

                my $publisherID;
                my $pubNum = $license->{publisherNumber};
                if ( $pubNum && $publishers->{$pubNum} && $publishers->{$pubNum}->{publisherID} ) {
                    $licenseParams{publisher_id} = $publishers->{$pubNum}->{publisherID};
                } else {
                    report( "!Warning (line " . $license->{lineNum} . "): missing publisher for song = " . $license->{songTitle} );
                    next;
                }

             # let's make sure we didn't already import this license
             #
             # my $licenseKey = join('-', $trackID, $licenseParams{publisher_id}, $licenseParams{share}, $licenseParams{issuer_license_id});
                my $licenseKey = join( '-', $trackID, $licenseParams{publisher_id}, $licenseParams{product_type_id} );
                if ( !$seenLicense{$licenseKey} ) {
                    $seenLicense{$licenseKey} = 1;

                    # check for duplicate in the system
                    #
                    my $coll = RPS::DB::Item::TrackLicense->GetByTrackPublisherProductType(
                        $trackID,
                        $licenseParams{publisher_id},
                        $licenseParams{product_type_id}
                    );
                    if ( defined $coll && $coll->size > 0 ) {
                        report( "!Warning (line "
                              . $license->{lineNum}
                              . "): License already exists: track_id=$trackID, song = "
                              . $license->{songTitle} );
                        next;
                    }

                    # report("Creating track_license with: ".Dumper(\%licenseParams), kDebug);

                    my $newLicense = RPS::DB::Item::TrackLicense->Create(%licenseParams);
                    $newLicense->save();
                    $license->{trackLicenseID} = $newLicense->track_license_id;
                    $matchCount++;
                } else {
                    report( "!Warning (line " . $license->{lineNum} . "): License already created: song = " . $license->{songTitle} );
                }
            } else {
                report( "!Warning (line " . $license->{lineNum} . "): Song not found: song = " . $license->{songTitle} );
            }
        } else {
            report( "!Warning (line " . $license->{lineNum} . "): I guess we already processed this license?" );
        }

    }    # END - licenses loop

    report("  Total licenses processed : $count");
    report("  Licenses created         : $matchCount");
}

sub _deriveRateType {
    my $rate = shift;

    # full  = 1
    # min   = 2
    # penny = 3
    #
    my $rateType;
    if ( $rate =~ /(MIN(IMUM)?|WITHOUT REGARD TO PLAYING TIME)/i ) {
        $rateType = RPS::DB::Item::TrackLicense::kRateTypeMinimum;
    } elsif ( $rate =~ /\d\.\d+/ ) {
        $rateType = RPS::DB::Item::TrackLicense::kRateTypePenny;
    } else {
        $rateType = RPS::DB::Item::TrackLicense::kRateTypeFull;
    }

    return $rateType;
}

sub _deriveRate {
    my $rateType = shift;

    my $rate;

    if ( $rateType =~ /(\d\d)%/ ) {
        $rate = $1;
    } else {
        $rate = 100;
    }

    return $rate;
}

sub _deriveProductType {
    my $config = shift;

    my $id;

    if ( $config =~ /(DIGITAL|DOWNLOAD)/i ) {

        # digital
        #
        $id = 3;
    } elsif ( $config =~ /COMPACT DISC/i ) {

        # physical - CD
        #
        $id = 2;
    }

    return $id;
}

sub _deriveTrackIDFromLicense {
    my $license = shift;

    my $trackID;

    # The Sanctuary matching rule is this:
    # 1. We must be able to match the catalog number or UPC to a release in our catalog.
    #
    if ( $license->{catalogNumber} || $license->{upc} ) {
        $trackID = _getTrackIDFromCatalogNumber($license);
    }

    return $trackID;
}

sub lookupPublisher {
    my ($name) = @_;

    my $publisherID;

    # Does this publisher already exist?
    #
    my $coll = RPS::DB::Item::Publisher->Match($name);
    if ( defined $coll && $coll->size == 1 ) {
        my $dbItem = $coll->next();
        $publisherID = $dbItem->publisher_id;
    }

    return $publisherID;
}

sub _normalizeCatalogNumber {
    my $catalogNumber = shift;

    $catalogNumber =~ s/\D//g;
    if ( length($catalogNumber) >= 10 ) {
        if ( length($catalogNumber) == 10 ) {
            if ( $catalogNumber !~ /2$/ ) {
                $catalogNumber = $catalogNumber . "2";
            } elsif ( $catalogNumber !~ /^0/ ) {
                $catalogNumber = "0" . $catalogNumber;
            }
        }

        $catalogNumber = substr( $catalogNumber, 5, 10 );
        if ( length($catalogNumber) == 5 ) {
            $catalogNumber .= "-2";
        } else {
            $catalogNumber =~ s/2$/-2/;
        }
    } elsif ( length($catalogNumber) == 5 ) {
        $catalogNumber .= "-2";
    }

    return $catalogNumber;
}

sub _getTrackIDFromCatalogNumber {
    my ($license) = @_;

    my $trackID;
    my $matchedTracks;

    # the order here is important, we always want to use the catalogNumber if we can.
    #
    my $catalogNumber = _normalizeCatalogNumber( $license->{catalogNumber} ) || _normalizeCatalogNumber( $license->{upc} );
    report( "Looking up CatalogNumber = $catalogNumber", kDebug );
    if ($catalogNumber) {
        my $coll = RPS::DB::Item::Album->GetByCatalogNumber($catalogNumber);

        if ( $coll->hasNext() ) {
            my $albumItem = $coll->next();

            $matchedTracks = MatchTracksFromAlbumID( $albumItem->album_id, $license );
            $trackID = FindTrackFromMatchedTracks( $matchedTracks, $license );
        } else {
            report( "!Warning: CatalogNumber not found", kDebug );
        }
    }

    return $trackID;
}

sub _getTrackIDFromUPC {
    my ($license) = @_;

    my $trackID;
    my $upc = $license->{upc} || $license->{altUPC};

    if ($upc) {
        report( "Looking up UPC = $upc", kDebug );
        my $productColl = RPS::DB::Item::Product->GetByUPC($upc);
        if ( $productColl->hasNext() ) {
            my $prodItem = $productColl->next();
            my $matchedTracks = MatchTracksFromAlbumID( $prodItem->album_id, $license );
            $trackID = FindTrackFromMatchedTracks( $matchedTracks, $license );
        } else {
            report( "!Warning: UPC not found", kDebug );
        }
    }

    return $trackID;
}

sub FindTrackFromMatchedTracks {
    my ( $matchedTracks, $license ) = @_;

    my $trackID;

    if ( @$matchedTracks == 1 ) {
        $trackID = $matchedTracks->[0]->track_id;
        report( "!Notice (line " . $license->{lineNum} . "): MATCH found! trackID = " . $trackID, kDebug );
    } elsif ( @$matchedTracks > 1 && $license->{duration} =~ /\d?\d?:\d\d/ ) {
        report( "Checking duration...", kDebug );
        foreach my $track (@$matchedTracks) {

            # let's compare duration
            #
            my $licDuration = RPS::XMLObject::_MMSSToSeconds( $license->{duration} );
            my $masterItem = RPS::DB::Item::Master->Lookup( master_id => $track->master_id );

            if ( $licDuration == $masterItem->duration || abs( $licDuration - $masterItem->duration ) <= kDurationTolerance ) {
                $trackID = $track->track_id;
            }
        }

        if ( !$trackID ) {
            report( "!Warning (line " . $license->{lineNum} . "): duplicate song, no match on duration = " . $license->{songTitle},
                kDebug );
        }
    } else {
        report( "!Warning (line " . $license->{lineNum} . "): duplicate song, no match on duration = " . $license->{songTitle}, kDebug );
    }

    return $trackID;
}

sub MatchTracksFromAlbumID {
    my ( $albumID, $license ) = @_;

    my @matchedTracks = ();

    # normalize the song title we are trying to match
    #
    my $licenseTrack = lc( $license->{songTitle} );
    $licenseTrack =~ s/\s+//g;
    my $licenseTrackClean = clean($licenseTrack);

    my $trackColl = RPS::DB::Item::Track->GetTracksByAlbumID($albumID);
    while ( $trackColl->hasNext() ) {
        my $trackItem = $trackColl->next();

        if ( $trackItem->title ) {

            # normalize the song title from the database
            #
            my $prodTrack = lc( $trackItem->title );
            $prodTrack =~ s/\s+//g;
            my $prodTrackClean = clean($prodTrack);
            $prodTrackClean =~ s/_//g;

            report( "!Notice (line " . $license->{lineNum} . "): comparing ('" . $prodTrack . "' cmp '" . $licenseTrack . "')", kDebug );

            ##########################
            ########################## We are restricting matches to ONLY EXACT matches
            ##########################

            # match a short title
            #			if (length($prodTrack) < 3)
            #			{
            if ( $licenseTrack eq $prodTrack || $licenseTrackClean eq $prodTrackClean ) {
                report( "!Notice (line " . $license->{lineNum} . "): MATCH ('" . $prodTrack . "' cmp '" . $licenseTrack . "')", kDebug );
                push @matchedTracks, $trackItem;

                # $trackID = $trackItem->track_id;
            }

            #			}
            #			# does the dbTrack contain the title? or the other way around?
            #			# This works because we've already narrowed it down to the
            #			# correct album.
            #			elsif ($licenseTrack eq $prodTrack ||
            #				   index($licenseTrack, $prodTrack) > -1 ||
            #				   index($prodTrack, $licenseTrack) > -1 ||
            #				   $licenseTrackClean eq $prodTrackClean ||
            #				   index($licenseTrackClean, $prodTrackClean) > -1 ||
            #				   index($prodTrackClean, $licenseTrackClean) > -1
            #				   )
            #			{
            #				report("!Notice (line ".$license->{lineNum}."): MATCH ('".$prodTrack."' cmp '".$licenseTrack."')", kDebug);
            #				push @matchedTracks, $trackItem;
            #				# $trackID = $trackItem->track_id;
            #			}
        }
    }

    return \@matchedTracks;
}

sub escapequotes {
    my @string = @_;

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

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

sub trimquotes {
    my @string = trimspaces(@_);

    for (@string) {
        if (m/^\".*\"$/) {
            s/^\"//;
            s/\"$//;
        }
    }

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

sub undefspaces {
    my @string = trimspaces(@_);

    for (@string) {
        if ( $_ eq '' ) {
            $_ = undef;
        }
    }

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

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

    $level = kNormal unless $level;

    if ( $level <= $gReportLevel ) {
        print $text . "\n";
    }
}

sub _convertDate {
    my $date = shift;
    my $newValue;

    my ( $year, $month, $day );
    if (   ( ( $year, $month, $day ) = Date::Calc::Decode_Date_US($date) )
        || ( ( $year, $month, $day ) = Date::Calc::Decode_Date_EU($date) )
        || ( ( $year, $month, $day ) = decodeDateMysql($date) ) ) {
        $newValue = sprintf( "%4d-%02d-%02d", $year, $month, $day );
    }

    return $newValue;
}

sub _myNormalizeUPC {
    my $upc = shift;

    if ( length($upc) > 10 ) {
        $upc =~ s/^0+//;
    }

    if ( length($upc) == 10 ) {

        # assume it needs the "format" digit on the end (2)
        $upc = $upc . "2";
    }

    return normalize_upc($upc);
}

