#!/usr/bin/perl
#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
# $Id$
#------------------------------------------------------------

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

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

use lib '/app/tools/rps/lib';
use RPS::DB::Item::Artist;
use RPS::DB::Item::ArtistContractTermReserve;
use RPS::DB::Item::BonusTrack;
use RPS::DB::Item::LicenseReserve;
use RPS::DB::Item::MechanicalStatementItem;
use RPS::DB::Item::Label;
use RPS::DB::Item::Track;
use RPS::DB::Item::Album;
use RPS::DB::Item::Product;
use RPS::DB::Item::ProductTrack;
use RPS::DB::Item::Master;
use RPS::DB::Item::Song;
use RPS::DB::Item::Composer;

use constant kFileFormatXML => 999;
my @gHeaders = ( [
        "album-name",         "album-artist",   "label-name",       "catalog-no",     "cd-product-id",  "cd-upc",
        "digital-product-id", "digital-upc",    "vinyl-product-id", "vinyl-upc",      "dvd-product-id", "dvd-upc",
        "release-date",       "album-custom-1", "album-custom-2",   "album-custom-3", "track-name",     "disc-no",
        "disc-name",          "track-no",       "track-minutes",    "track-seconds",  "track-artist",   "isrc",
        "genre",              "track-custom-1", "track-custom-2",   "track-custom-3"
    ],
);

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

use constant kProgressQuanta => 100;

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

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

# Scan the catalog file, and build the data structure of updates.
#
my %updates;
scanCatalogFileForUpdates( $catalogFile, \%updates );

#
# --- Subroutines
#

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

    my $catalogFile = $opt{f};
    my $clientID    = $opt{c};
    my $commit      = $opt{e};

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

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

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

    return ( $clientID, $catalogFile, $commit );
}

sub usage {
    my $errstr = shift;
    my $text = ($errstr) ? "ERROR: $errstr\n" : '';
    $text .= "Usage: $0 -c <clientID> -f <catalogFile> [-e]\n       -e executes/commits an update\n";
    return $text;
}

sub scanCatalogFileForUpdates {
    my ( $catalogFilePath, $updates ) = @_;
    assert($catalogFilePath);
    assert($updates);

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

    # Examine the first line - This should contain the field headers.
    # And will tell us whether we're looking at a tab-seperated file, or XML.
    #
    my $format = determineFileType($catalogFile);

    if ( kFileFormatXML == $format ) {
        readXMLCatalogFile( $catalogFile, $updates );
    } else {
        print "FORMAT = $format\n";

        # !!! Probably want to seed this read method with the field definitions.
        #
        readTabSeperatedCatalogFile( $catalogFile, $format );
    }

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

sub determineFileType {
    my ($file) = @_;
    assert($file);

    # Read the first line from the file.
    #
    my $line = <$file>;
    die "ERROR - empty catalog file!\n" unless $line;
    chomp $line;

    my @fields = split( "\t", $line );
    @fields = trimquotes(@fields);
    @fields = escapequotes(@fields);

    report("Examining file header...");

    # First, see if this file contains XML - should be easy to tell.
    #
    if ( $line =~ m/\^<catalog\>/ ) {
        report("  File is XML");
        return kFileFormatXML;
    }

    # Try to match the fields to one of our known header configurations.
    #
    my $match;
    my $formats = scalar @gHeaders;

    my $format = 0;
    while ( $format < $formats && !$match ) {
        my $fields = @fields;
        $match = 1;
        my $i;
        for ( $i = 0 ; $i < $fields ; $i++ ) {
            if ( !defined $gHeaders[$format]->[$i] ) {
                $match = 0;
                last;
            }

            if ( $fields[$i] !~ m/$gHeaders[$format]->[$i]/i ) {
                $match = 0;
                last;
            }
        }
        if ( !$match ) {
            $format++;
        }
    }
    if ( !$match ) {
        die "Unrecognized file format. Header:\n$line\n";
    }

    report("  File is tab-delimited");
    report( "  format = $format", kDebug );

    return $format;
}

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

    die "--- NOT YET IMPLEMENTED---\n";
}

sub readTabSeperatedCatalogFile {
    my ( $file, $format ) = @_;
    assert($file);

    exit unless ( $format == 0 );

    report("Processing catalog file");

    my %inserted_albums;
    my $last_album;
    my ( %album_info, %track_info );
    my %err_on_album;

    my $recordCount = 0;

    # Process the file line by line
    while ( my $record = <$file> ) {
        my @fields;
        $recordCount++;

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

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

        # Parse the fields, based on the expected field order.
        my (
            $albumName,    $albumArtist,    $labelName, $catalogID,    $productIdCD,  $upcCD,        $productIdDigital,
            $upcDigital,   $productIdVinyl, $upcVinyl,  $productIdDVD, $upcDVD,       $releaseDate,  $albumCustom1,
            $albumCustom2, $albumCustom3,   $trackName, $discNumber,   $discName,     $trackNumber,  $trackMinutes,
            $trackSeconds, $trackArtist,    $isrc,      $genre,        $trackCustom1, $trackCustom2, $trackCustom3
        ) = undefspaces(@fields);

        unless ( defined $catalogID ) {
            report("missing catalogID: @fields");
            next;
        }

        ## check if $catalogID already in system
        my $isAlbumInSystem = RPS::DB::Item::Album->Lookup( catalog_number => $catalogID );
        if ($isAlbumInSystem) {
            report("album $catalogID is already in system");
            $err_on_album{$catalogID} = 1;
            next;
        }

        unless ( defined $albumName && defined $albumArtist && defined $labelName ) {
            report("missing album name, artist, or label on $catalogID");
            $err_on_album{$catalogID} = 1;
            next;
        }
        unless ( defined $trackName && defined $trackArtist ) {
            report("missing track name or artist on $catalogID");
            $err_on_album{$catalogID} = 1;
            next;
        }
        unless ( $trackNumber =~ m/^\d+$/ && $discNumber =~ m/^\d+$/ ) {
            report("missing or invalid track/disc numbers for $catalogID");
            $err_on_album{$catalogID} = 1;
            next;
        }
        unless ( $trackMinutes =~ m/^\d*$/ && $trackSeconds =~ m/^\d*$/ ) {
            report("invalid min/sec values for $catalogID");
            $err_on_album{$catalogID} = 1;
            next;
        }

        if ( $upcCD && !$productIdCD ) {
            report("incomplete CD specs for $catalogID");
            $err_on_album{$catalogID} = 1;
            next;
        }
        if ( $upcDigital && !$productIdDigital ) {
            report("incomplete Digital specs for $catalogID");
            $err_on_album{$catalogID} = 1;
            next;
        }
        if ( $upcVinyl && !$productIdVinyl ) {
            report("incomplete Vinyl specs for $catalogID");
            $err_on_album{$catalogID} = 1;
            next;
        }
        if ( $upcDVD && !$productIdDVD ) {
            report("incomplete DVD specs for $catalogID");
            $err_on_album{$catalogID} = 1;
            next;
        }

        unless ( $productIdCD || $productIdDigital || $productIdVinyl || $productIdDVD ) {
            report("no products specified for $catalogID");
            $err_on_album{$catalogID} = 1;
            next;
        }

        if ( $upcCD ne "" ) {
            $upcCD = "0" . $upcCD if ( length $upcCD == 11 );
            if ( $upcCD ne normalize_upc($upcCD) ) {
                report("bad upc check digit on $catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( length $upcCD != 12 ) {
                report("upc of wrong length on $catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            }
        }
        if ( $upcDigital ne "" ) {
            $upcDigital = "0" . $upcDigital if ( length $upcDigital == 11 );
            if ( $upcDigital ne normalize_upc($upcDigital) ) {
                report("bad upc check digit on $catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( length $upcDigital != 12 ) {
                report("upc of wrong length on $catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            }
        }
        if ( $upcVinyl ne "" ) {
            $upcVinyl = "0" . $upcVinyl if ( length $upcVinyl == 11 );
            if ( $upcVinyl ne normalize_upc($upcVinyl) ) {
                report("bad upc check digit on $catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( length $upcVinyl != 12 ) {
                report("upc of wrong length on $catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            }
        }
        if ( $upcDVD ne "" ) {
            $upcDVD = "0" . $upcDVD if ( length $upcDVD == 11 );
            if ( $upcDVD ne normalize_upc($upcDVD) ) {
                report("bad upc check digit on $catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( length $upcDVD != 12 ) {
                report("upc of wrong length on $catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            }
        }

        my $duration = ( $trackMinutes * 60 ) + $trackSeconds;

        $releaseDate = normalize_date($releaseDate);

        if ( $releaseDate ne '' && $releaseDate !~ m/^\d\d\d\d-\d\d-\d\d/ ) {
            report("badly formatted release date on $catalogID");
            $err_on_album{$catalogID} = 1;
            next;
        }

        if ( defined $last_album && $last_album ne $catalogID ) {
            if ( exists $inserted_albums{$catalogID} ) {
                report("skipping duplicated or out-of-order album $catalogID");
                next;
            }

            ## process/insert %album_info and %track_info (for last album)
            if ( $err_on_album{$last_album} == 0 ) {
                if ($commit) {
                    processAlbum( $last_album, \%album_info, \%track_info );
                    report("album $last_album inserted");
                }
            }
            %album_info                   = ();
            %track_info                   = ();
            $inserted_albums{$last_album} = 1;

        }
        $last_album = $catalogID;

        if ( !exists( $inserted_albums{$catalogID} ) ) {
            $album_info{albumName}        = $albumName;
            $album_info{albumArtist}      = $albumArtist;
            $album_info{labelName}        = $labelName;
            $album_info{catalogID}        = $catalogID;
            $album_info{releaseDate}      = $releaseDate;
            $album_info{albumCustom1}     = $albumCustom1;
            $album_info{albumCustom2}     = $albumCustom2;
            $album_info{albumCustom3}     = $albumCustom3;
            $album_info{productIdCD}      = $productIdCD;
            $album_info{productIdDigital} = $productIdDigital;
            $album_info{productIdVinyl}   = $productIdVinyl;
            $album_info{productIdDVD}     = $productIdDVD;
            $album_info{upcCD}            = $upcCD;
            $album_info{upcDigital}       = $upcDigital;
            $album_info{upcVinyl}         = $upcVinyl;
            $album_info{upcDVD}           = $upcDVD;
        } else {
            if ( $album_info{$catalogID}->{albumName} ne $albumName ) {
                report("Album name varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$catalogID}->{albumArtist} ne $albumArtist ) {
                report("Artist name varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$catalogID}->{labelName} ne $labelName ) {
                report("Label name varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$catalogID}->{releaseDate} ne $releaseDate ) {
                report("Release date varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$productIdCD}->{releaseDate} ne $productIdCD ) {
                report("CD product ID varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$productIdDigital}->{releaseDate} ne $productIdDigital ) {
                report("Digital product ID varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$productIdVinyl}->{releaseDate} ne $productIdVinyl ) {
                report("Vinyl product ID varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$productIdDVD}->{releaseDate} ne $productIdDVD ) {
                report("DVD product ID varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$upcCD}->{releaseDate} ne $upcCD ) {
                report("CD UPC varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$upcDigital}->{releaseDate} ne $upcDigital ) {
                report("Digital UPC varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$upcVinyl}->{releaseDate} ne $upcVinyl ) {
                report("Vinyl UPC varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            } elsif ( $album_info{$upcDVD}->{releaseDate} ne $upcDVD ) {
                report("DVD UPC varies: album ID=$catalogID");
                $err_on_album{$catalogID} = 1;
                next;
            }
        }

        if ( !exists( $track_info{$discNumber}->{$trackNumber}->{trackName} ) ) {
            $track_info{$discNumber}->{$trackNumber}->{trackName}    = $trackName;
            $track_info{$discNumber}->{$trackNumber}->{duration}     = $duration;
            $track_info{$discNumber}->{$trackNumber}->{trackArtist}  = $trackArtist;
            $track_info{$discNumber}->{$trackNumber}->{isrc}         = $isrc;
            $track_info{$discNumber}->{$trackNumber}->{genreName}    = $genre;
            $track_info{$discNumber}->{$trackNumber}->{trackCustom1} = $trackCustom1;
            $track_info{$discNumber}->{$trackNumber}->{trackCustom2} = $trackCustom2;
            $track_info{$discNumber}->{$trackNumber}->{trackCustom3} = $trackCustom3;
        } else {
            report("Duplicate track: album ID=$catalogID, track ${discNumber}.${trackNumber}");

            #print "$catalogID ${discNumber}.${trackNumber} ".($track_info{$discNumber}->{$trackNumber}->{trackName})."\n";
            $err_on_album{$catalogID} = 1;
            next;
        }
    }
    ## process/insert %album_info and %track_info (for last album)
    if ( $err_on_album{$last_album} == 0 ) {
        if ($commit) {
            processAlbum( $last_album, \%album_info, \%track_info );
            report("album $last_album inserted");
        }
    }
    ##
    report("\n\n  $recordCount records processed\n\n");
    foreach my $id ( keys %err_on_album ) {
        report("album $id skipped due to errors") if ( $err_on_album{$id} != 0 );
    }

    unless ($commit) {
        report("\ncorrect any problems (if necessary) and re-run with the -e option to commit");
    }
}

sub processAlbum {
    my ( $catalogID, $album, $tracks ) = @_;

    my %artistNameIDHash;

    # report("ALBUM DATA $albumID: " . Dumper($albums->{$albumID}));

    # Before we create the album entry, we first have to
    # - create the label (if necessary)
    # - create the artist (if necessary)
    #

    # Does this label exist already?
    #
    my $labelName = $album->{labelName};
    my $labelDBItem = RPS::DB::Item::Label->Lookup( label_name => $labelName );
    if ( !$labelDBItem ) {
        $labelDBItem = RPS::DB::Item::Label->Create(
            label_name       => $labelName,
            label_name_clean => clean_name_catalog($labelName),
        );
        $labelDBItem->save();
    }
    my $labelID = $labelDBItem->label_id;

    # Does this artist exist already?
    #
    my $artistName = $album->{albumArtist};
    my $artistDBItem = RPS::DB::Item::Artist->Lookup( name => $artistName );
    if ( !$artistDBItem ) {
        $artistDBItem = RPS::DB::Item::Artist->Create(
            name       => $artistName,
            name_clean => clean_name_catalog($artistName),
        );
        $artistDBItem->save();
    }
    my $artistID = $artistDBItem->artist_id;

    my $newAlbum = RPS::DB::Item::Album->Create(
        catalog_number => $album->{catalogID},
        title          => $album->{albumName},
        title_clean    => clean_name_catalog( $album->{albumName} ),
        artist_id      => $artistID,
        label_id       => $labelID,
        custom_1       => $album->{albumCustom1},
        custom_2       => $album->{albumCustom2},
        custom_3       => $album->{albumCustom3},
        status         => 1,                                           # 1=active

    );
    $newAlbum->save();

    my $albumID = $newAlbum->album_id;

    # Now create a corresponding digital product (for the album).

    my ( $albumDigitalProductID, $albumCDProductID, $albumVinylProductID, $albumDVDProductID );
    if ( $album->{productIdDigital} ) {
        my $newDigProduct = RPS::DB::Item::Product->Create(

            #product_id => $album->{albumProductID},
            asset_id          => $albumID,
            upc_ean           => $album->{upcDigital},
            release_date      => $album->{releaseDate},
            product_type_id   => RPS::DB::Item::Product::kProductTypeDigital,    # 3 == digital
            product_status_id => 1,                                              # 1 == active
                 # product_price_id => 1,     # 1 == full (the default) # don't set this at all -jff-
            product_code => $album->{productIdDigital},
        );
        $newDigProduct->save();
        $albumDigitalProductID = $newDigProduct->product_id;
    }

    if ( $album->{productIdCD} ) {
        my $newPhysProduct = RPS::DB::Item::Product->Create(

            #product_id => $album->{physicalCDProductID},
            asset_id          => $albumID,
            upc_ean           => $album->{upcCD},
            release_date      => $album->{releaseDate},
            product_type_id   => RPS::DB::Item::Product::kProductTypeCD,    # 2 == cd
            product_status_id => 1,                                         # 1 == active
                 # product_price_id => 1,     # 1 == full (the default) # don't set this -jff-
            product_code => $album->{productIdCD},
        );
        $newPhysProduct->save();
        $albumCDProductID = $newPhysProduct->product_id;
    }

    if ( $album->{productIdVinyl} ) {
        my $newPhysProduct = RPS::DB::Item::Product->Create(

            #product_id => $album->{physicalLPProductID},
            asset_id          => $albumID,
            upc_ean           => $album->{upcVinyl},
            release_date      => $album->{releaseDate},
            product_type_id   => RPS::DB::Item::Product::kProductTypeLP,
            product_status_id => 1,                                        # 1 == active
                 # product_price_id => 1,     # 1 == full (the default) # don't set this -jff-
            product_code => $album->{productIdVinyl},
        );
        $newPhysProduct->save();
        $albumVinylProductID = $newPhysProduct->product_id;
    }

    if ( $album->{productIdDVD} ) {
        my $newPhysProduct = RPS::DB::Item::Product->Create(

            #product_id => $album->{physicalDVDProductID},
            asset_id          => $albumID,
            upc_ean           => $album->{upcDVD},
            release_date      => $album->{releaseDate},
            product_type_id   => RPS::DB::Item::Product::kProductTypeDVD,
            product_status_id => 1,                                         # 1 == active
                 # product_price_id => 1,     # 1 == full (the default) # don't set this -jff-
            product_code => $album->{productIdDVD},
        );
        $newPhysProduct->save();
        $albumDVDProductID = $newPhysProduct->product_id;
    }

    # Create/update track entries
    #
    foreach my $disc_no ( keys %$tracks ) {
        foreach my $track_no ( keys %{ $$tracks{$disc_no} } ) {

            my $trackArtistName = $tracks->{$disc_no}->{$track_no}->{trackArtist};

            # Does this artist exist already?
            #
            my $artistDBItem = RPS::DB::Item::Artist->Lookup( name => $trackArtistName );
            if ( !$artistDBItem ) {
                $artistDBItem = RPS::DB::Item::Artist->Create(
                    name       => $trackArtistName,
                    name_clean => clean_name_catalog($trackArtistName),
                );
                $artistDBItem->save();
            }
            my $trackArtistID = $artistDBItem->artist_id;

            # We need to create a Master for this track, because that's where track length is stored.
            # I guess that means that we want to create a Song for this track, too.
            # We'll just do our best, and see what happens.
            #

            my $newSong = RPS::DB::Item::Song->Create( title => $tracks->{$disc_no}->{$track_no}->{trackName} );
            $newSong->save();
            my $trackSongID = $newSong->song_id;

            my $newMaster = RPS::DB::Item::Master->Create(
                song_id   => $trackSongID,
                title     => $tracks->{$disc_no}->{$track_no}->{trackName},
                artist_id => $trackArtistID,
                isrc      => $tracks->{$disc_no}->{$track_no}{isrc},
                duration  => $tracks->{$disc_no}->{$track_no}{duration},
            );
            $newMaster->save();
            my $trackMasterID = $newMaster->master_id;

            my $newTrack = RPS::DB::Item::Track->Create(
                title       => $tracks->{$disc_no}->{$track_no}{trackName},
                title_clean => clean_name_catalog( $tracks->{$disc_no}->{$track_no}{trackName} ),
                album_id    => $albumID,
                artist_id   => $trackArtistID,
                track_order => $track_no,
                genre_name  => $tracks->{$disc_no}->{$track_no}{genreName},
                custom_1    => $tracks->{$disc_no}->{$track_no}{trackCustom1},
                custom_2    => $tracks->{$disc_no}->{$track_no}{trackCustom2},
                custom_3    => $tracks->{$disc_no}->{$track_no}{trackCustom3},
                master_id   => $trackMasterID,
            );
            $newTrack->save();

            my $trackID = $newTrack->track_id;

            # Now create a corresponding digital product for this track.

            if ($albumDigitalProductID) {
                my $newProduct = RPS::DB::Item::Product->Create(

                    #product_id => $tracks->{$trackID}{trackProductID},
                    asset_id          => $trackID,
                    product_type_id   => RPS::DB::Item::Product::kProductTypeDigitalTrack,    # 4 == digital track
                    product_status_id => 1,                                                   # 1 == active
                         # product_price_id => 1,     # 1 == full (the default) -jff-
                );
                $newProduct->save();
            }

            if ($albumDigitalProductID) {
                my $digProductTrack = RPS::DB::Item::ProductTrack->Create(
                    product_id  => $albumDigitalProductID,
                    track_id    => $trackID,
                    disc_number => $disc_no,
                    disc_track  => $track_no,
                );
                $digProductTrack->save();
            }

            if ($albumCDProductID) {
                my $physProductTrack = RPS::DB::Item::ProductTrack->Create(
                    product_id  => $albumCDProductID,
                    track_id    => $trackID,
                    disc_number => $disc_no,
                    disc_track  => $track_no,
                );
                $physProductTrack->save();
            }
            if ($albumVinylProductID) {
                my $physProductTrack = RPS::DB::Item::ProductTrack->Create(
                    product_id  => $albumVinylProductID,
                    track_id    => $trackID,
                    disc_number => $disc_no,
                    disc_track  => $track_no,
                );
                $physProductTrack->save();
            }
            if ($albumDVDProductID) {
                my $physProductTrack = RPS::DB::Item::ProductTrack->Create(
                    product_id  => $albumDVDProductID,
                    track_id    => $trackID,
                    disc_number => $disc_no,
                    disc_track  => $track_no,
                );
                $physProductTrack->save();
            }
        }
    }
}

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 STDERR $text . "\n";
    }
}
