#---------------------------------------------------------------
# ____                   _ _         ____  _
#|  _ \ ___  _   _  __ _| | |_ _   _/ ___|| |__   __ _ _ __ ___
#| |_) / _ \| | | |/ _` | | __| | | \___ \| '_ \ / _` | '__/ _ \
#|  _ < (_) | |_| | (_| | | |_| |_| |___) | | | | (_| | | |  __/
#|_| \_\___/ \__, |\__,_|_|\__|\__, |____/|_| |_|\__,_|_|  \___|
#            |___/             |___/
#
# Copyright (C) 2010 RoyaltyShare, Inc.   All Rights Reserved
#---------------------------------------------------------------
package RPS::Import::Universal::v19;

use strict;

use Date::Calc qw(Days_in_Month Decode_Month);

use lib '/app/tools/common/lib';
use Common::Country;

use lib '/app/tools/rps/lib';
use RPS::Import::ServiceMap;

use base 'RPS::Import::MapFaceBase';
use base 'RPS::Import::Importer';

sub _fieldMap {
    my $self = shift;
    my $version = $self->version;
    my %field_map = (
        19 => {
            dateBegin     => 'R',
            serviceID     => 'D',
            productType   => 'F',
            labelName     => 'A',
            upc           => 'I',
            albumName     => 'H',
            isrc          => 'N',
            trackName     => 'K',
            units         => 'Q',
            price         => 'R',
        },
        22 => {
            dateBegin     => 'N',
            serviceID     => 'D',
            productType   => 'F',
            labelName     => 'A',
            upc           => 'I',
            albumName     => 'H',
            isrc          => 'M',
            trackName     => 'K',
            units         => 'O',
            price         => 'N',
        },
        25 => {
            dateBegin     => 'Q',
            serviceID     => 'D',
            productType   => 'F',
            labelName     => 'A',
            upc           => 'I',
            albumName     => 'H',
            isrc          => 'N',
            trackName     => 'K',
            units         => 'R',
            price         => 'Q',
        },      
        26 => {
            dateBegin     => 'O',
            serviceID     => 'D',
            productType   => 'F',
            labelName     => 'A',
            upc           => 'I',
            albumName     => 'H',
            isrc          => 'N',
            trackName     => 'K',
            units         => 'P',
            price         => 'O',
        },             
        27 => {
            dateBegin     => 'O',
            serviceID     => 'D',
            productType   => 'F',
            labelName     => 'A',
            upc           => 'J',
            albumName     => 'I',
            isrc          => 'N',
            trackName     => 'L',
            units         => 'P',
            price         => 'O',
        },             
        38 => {
            labelName     => 'A',
            serviceID     => 'D',
            dateBegin     => 'N3',
            productType   => 'F',
            upc           => 'I',
            albumName     => 'H',
            isrc          => 'M',
            trackName     => 'K',
            units         => 'O',
            price         => 'N',
        },
        45 => {
            labelName     => 'B',
            serviceID     => 'D',
            dateBegin     => 'N3',
            productType   => 'F',
            upc           => 'I',
            albumName     => 'H',
            isrc          => 'M',
            trackName     => 'K',
            units         => 'O',
            price         => 'N',
        },
        50 => {
            labelName     => 'B',
            serviceID     => 'D',
            dateBegin     => 'O',
            productType   => 'F',
            upc           => 'I',
            albumName     => 'H',
            isrc          => 'N',
            trackName     => 'K',
            units         => 'P',
            price         => 'O',
        },
        57 => {
            labelName     => 'B',
            serviceID     => 'D',
            dateBegin     => 'N',
            productType   => 'F',
            upc           => 'I',
            albumName     => 'H',
            isrc          => 'M',
            trackName     => 'K',
            units         => 'O',
            price         => 'N',
        },
        58 => {
            labelName     => 'B',
            serviceID     => 'D',
            dateBegin     => 'O',
            productType   => 'F',
            upc           => 'I',
            albumName     => 'H',
            isrc          => 'N',
            trackName     => 'K',
            units         => 'P',
            price         => 'O',
        },
    );
    return $field_map{$version};
}

sub _unverifiedFieldMap {
    my $self = shift;
    my $version = $self->version;
    my %field_map = (
        19 => {
            albumArtist   => 'G',
            trackArtist   => 'J',
            salesType     => 'C',
        },
        22 => {
            albumArtist   => 'G',
            trackArtist   => 'J',
            salesType     => 'C',
        },
        25 => {
            albumArtist   => 'G',
            trackArtist   => 'J',
            salesType     => 'C',
        },
        26 => {
            albumArtist   => 'G',
            trackArtist   => 'J',
            salesType     => 'C',
        },                     
        27 => {
            albumArtist   => 'H',
            trackArtist   => 'K',
            salesType     => 'C',
        },                     
        38 => {
            albumArtist   => 'G',
            trackArtist   => 'J',
            salesType     => 'C',
        },
        45 => {
            albumArtist   => 'G',
            trackArtist   => 'J',
            salesType     => 'C',
        },
        50 => {
            albumArtist   => 'G',
            trackArtist   => 'J',
            salesType     => 'C', # Sales channel
        },
        57 => {
            albumArtist   => 'G',
            trackArtist   => 'J',
            salesType     => 'C', # Sales channel
        },
        58 => {
            albumArtist   => 'G',
            trackArtist   => 'J',
            salesType     => 'C', # Sales channel
        },
    );
    return $field_map{$version};
}

sub _headerIdentifier { (  upc => qr/UP(C|S)/ ) }

sub currencyCode { 'USD' }

sub countryCode {
    my $self = shift;

    return 'US' if( $self->_version != 57 );

    # version 57 uses the filename to determine country
    #
    if( !exists $self->{_countryCode} )
    {
        if( $self->filename =~ /[\s_]US[\s_]/i )
        {
            $self->{_countryCode} = 'US';
        }
        elsif( $self->filename =~ /[\s_]CA[\s_]/i )
        {
            $self->{_countryCode} = 'CA';
        }
        else
        {
            $self->fail("Unable to determine country code from ". $self->filename);
        }
    }
    return $self->{_countryCode};
}

my %gFormatMediaMap = (
    'ad supported streams' => {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'broadcast linear programming' => {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'broadcast vod' => {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'conditional mastertone' => {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'ephemeral plays' => {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'fixed plays' => {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'kiosk' =>
    {
        formatType  => RPS::File::Sale::FORMAT_JUKEBOX,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'mobile audio streams' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'ota permanent downloads' =>
    {
        formatType  => RPS::File::Sale::FORMAT_DOWNLOAD,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'permanent downloads' =>
    {
        formatType  => RPS::File::Sale::FORMAT_DOWNLOAD,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'portable sub client plays' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'portable sub device plays' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'portable sub programmed plays' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'portable sub streams' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'portable sub video dvc plays' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeVideo,
    },
    'programmed streaming' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'programmed streaming (non-royalty bearing)' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'programmed streaming-capped (non-royalty bearing)' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },    
    'programmed video streaming' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeVideo,
    },
    'ringbacks' =>
    {
        formatType  => RPS::File::Sale::FORMAT_RINGTONE,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'standard mobile ringsounds' =>
    {
        formatType  => RPS::File::Sale::FORMAT_RINGTONE,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'streams' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'subscription mastertones' =>
    {
        formatType  => RPS::File::Sale::FORMAT_RINGTONE,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'subscription ota permanent downloads (audio)' =>
    {
        formatType  => RPS::File::Sale::FORMAT_DOWNLOAD,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'subscription programmed streaming' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'tethered/conditional downloads' =>
    {
        formatType  => RPS::File::Sale::FORMAT_TETHERED,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'user generated streams (umg audio / user video)' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeAudio,
    },
    'video downloads' =>
    {
        formatType  => RPS::File::Sale::FORMAT_DOWNLOAD,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeVideo,
    },
    'video fixed play' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeVideo,
    },
    'video streams' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeVideo,
    },
    'video streams (sponsorship)' =>
    {
        formatType  => RPS::File::Sale::FORMAT_STREAM,
        mediaType   => RPS::DB::Item::MediaType::kMediaTypeVideo,
    },
);

sub saleDates {
    my $self = shift;
    return( $self->{_dateBegin}, $self->{_dateEnd} );
}

sub upc {
    my $self = shift;
    my $upc = $self->_getByFieldName('upc');
    return Common::Util::normalize_upc($upc);
}

sub isrc {
    my $self = shift;
    my $isrc = $self->_getByFieldName('isrc');
    return (!defined $self->productType || $self->productType eq RPS::File::Sale::TYPE_TRACK) ? $isrc : undef;
}

sub trackName {
    my $self = shift;
    my $name = $self->_getByFieldName('trackName');
    return (!defined $self->productType || $self->productType eq RPS::File::Sale::TYPE_TRACK) ? $name : undef;
}

sub artistName {
    my $self = shift;
    my $name;
    my $trackArtist = $self->_getByFieldName('trackArtist');
    my $albumArtist = $self->_getByFieldName('albumArtist');
    
    if( $self->productType eq RPS::File::Sale::TYPE_TRACK )
    {
        $name = $trackArtist;
    }
    elsif( $self->productType eq RPS::File::Sale::TYPE_ALBUM )
    {
        $name = $albumArtist;
    }

    if( !defined $self->productType )
    {
        $name = (defined $trackArtist) ? $trackArtist : $albumArtist;
    }

    if( $name && $name =~ /(\w*),\s*(\w*)/ )  # Lastname, Firstname
    {
        $name = $2 . ' ' . $1; # Firstname Lastname
    }
    return $name;
}

sub serviceID {
    my $self = shift;
    my $service = $self->_getByFieldName('serviceID');
    my $serviceID = $self->getServiceID(lc ($service)) || $RPS::Import::ServiceMap::SERVICE_ALIASES{ lc $service };
    return $serviceID if( $serviceID );

    return $self->handleError("Unknown service: $service", RPS::DB::Item::SaleImportError::kErrorService);
}

sub productType {
    my $self = shift;
    my $type = $self->_getByFieldName( 'productType' ) || return;

    if( $type =~ /^complete album$/i )
    {
        return RPS::File::Sale::TYPE_ALBUM;
    } 
    elsif( $type =~ /^track$/i )
    {
        return RPS::File::Sale::TYPE_TRACK;
    } 
    else
    {
        return $self->handleError("Unknown product type: $type", RPS::DB::Item::SaleImportError::kErrorProductType);
    }
}

sub _processHeader {
    my $self = shift;
    my %args = @_;

    $self->SUPER::_processHeader();  # Let MapFace have first crack at the header ...

    my %months = (jan => 1, feb => 2, mar => 3, apr => 4, may => 5, jun => 6,
                  jul => 7, aug => 8, sep => 9, oct => 10, nov => 11, dec => 12);

    my $dt = $self->_getByFieldName('dateBegin');
    if( $dt =~ /revenue umg \w{2,3} \d{2} \((\w{3,9})\s*(\d{2})\)?/i )
    {
        # if it's the whole month name, we need to strip off the first 3 chars
        my $_month = lc( substr( $1, 0, 3 ));
        my $year   = $2;

        my $month = $months{$_month};
        $year += 2000;

        my $sdate = sprintf("%04d-%02d-01", $year, $month);
        my $edate = sprintf("%04d-%02d-%02d", $year, $month, Days_in_Month($year, $month));

        $self->{_dateBegin} = $sdate;
        $self->{_dateEnd}   = $edate;
    }
}

sub formatType {
    my $self = shift;
    
    my $salesType = lc $self->_getByFieldName('salesType');
    
    my($formatID, $mediaType) = $self->_getFormatMedia( $salesType );
    
    if ($formatID) 
    {
        return $formatID;
    }
    else
    {
        return $self->handleError("Unknown sales channel: $salesType", RPS::DB::Item::SaleImportError::kErrorFormatType );
    }
}

sub mediaType {
    my $self = shift;
    
    my $salesType = lc $self->_getByFieldName('salesType');
    
    my($formatID, $mediaType) = $self->_getFormatMedia( $salesType );    

    if ($mediaType)
    {
        return $mediaType;
    }
    else
    {
        return $self->handleError("Unknown sales channel: $salesType", RPS::DB::Item::SaleImportError::kErrorMediaType );
    }
}

sub _getFormatMedia {
    my($self, $salesType) = @_;

    my $formatID;
    my $mediaType;
    if ( exists $gFormatMediaMap{$salesType} )
    {
        $formatID    = $gFormatMediaMap{$salesType}->{formatType};
        $mediaType   = $gFormatMediaMap{$salesType}->{mediaType};
    }

    return( $formatID, $mediaType );
}

sub units {
    my $self = shift;
    my $units = $self->SUPER::units();
    my $price = $self->SUPER::price();

    # The 'sign' of the units is driven by the price.
    # If the price is negative, we'll store negative units,
    # regardless of whether the units were positive or negative
    # in the sale file.  Accordingly, the price is always
    # positive.  This ensures that returns are recorded correctly.
    #
    if( defined($price) && $price != 0 )
    {
        return $price < 0 ? abs($units) * -1 : abs($units);
    }
    else
    {
        return $units;
    }
}

sub unitPrice {
    my $self = shift;
    return ( 0 == $self->price ) ? 0 : $self->SUPER::unitPrice();
}

sub _processSheet {
    my $self     = shift;
    my $sheet    = shift;
    my $sheetnum = shift;

    if( $self->{sheetname} =~ /^Digital Sales$/i ||
        $self->{sheetname} =~ /^EL7_Sales_\d{4}\d{2}$/i || # seen in FB8395
        $self->{sheetname} =~ /^Sheet1$/i && $self->_version == 26 ||  # FB9349
       ($self->{sheetname} =~ /^Sheet1$/i && $self->_version == 38)) # FB11459
    {
        return $self->SUPER::_processSheet( $sheet, $sheetnum );
    }
}

sub _isValidSaleRecord {
    my $self = shift;

    # Assume that if the label, service and config columns are blank, then this
    # is a line we want to skip.
    #
    my $labelName = $self->_getByFieldName('labelName');
    my $serviceID = $self->_getByFieldName('serviceID');
    my $productType = $self->_getByFieldName('productType');

    return 0 if( '' eq $labelName && '' eq $serviceID && '' eq $productType );

    return ( defined($self->units) && ($self->trackName || $self->albumName) );
}

#####    MapFace    #####
#

sub _mapFaceCompatibleVersions {
    return( 19,22,25,26,27,38,45,50,57,58 );
}

sub _mapFaceServiceID {
    return('serviceID');
}

sub _mapFaceCountryCode {
    return('countryCode');
}

sub _mapFaceProductType {
    return('productType');
}

sub _mapFaceFormatType {
    return('salesType');
}

sub _mapFaceMediaType {
    return('salesType');
}

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