#------------------------------------------------------------
# Copyright (C) 2006 RoyaltyShare, Inc.   All Rights Reserved
#------------------------------------------------------------
package RPS::Track::Track;

# !!! It appears that this class might be going away.
# !!! Need to determine where it is used.
# !!! Will probably replace this with a TrackWithSongAndMaster class, or something like that.
#

use strict;
use warnings;

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

use lib '/app/tools/metadata/lib';
use Metadata::Validate::Harness;

use lib '/app/tools/rps/lib';
use RPS::DB::Item::Track;
use RPS::DB::Item::Artist;
use RPS::DB::Item::TrackLicense;
use RPS::DB::Item::CATrackLicense;
use RPS::DB::Item::TrackContract;
use RPS::DB::Item::ProductTrack;
use RPS::DB::Item::Product;
use RPS::Master::Master;
use RPS::DB::Item::Album;
use RPS::Catalog::TrackAdditionalArtistHash;

use Common::FormObject;

use RPS::Track::TrackSimple;
use base 'RPS::Track::TrackSimple';

sub _initProperties {
    my ( $self, $props ) = @_;

    # Call the inherited method to fill in the basic fields.
    #
    $self->SUPER::_initProperties($props);

    # !!! There can now be multiple track products per track, so this doesn't make sense anymore.
    #
    # add in the digital track product id
    #my $productDBItem = RPS::DB::Item::Product->GetDigitalTrackProduct($props->{track_id});
    #my $productID = $productDBItem->product_id if( $productDBItem );
    #$self->{ProductID} = Common::FormObject::Scalar->new(value => $productID, readOnly => 1);

    # let's add the album name too, so that when tracks are used
    # outside the context of an album we can show the album name.
    #
    my $albumName;
    if ( $props->{album_id} ) {
        my $albumDBItem = RPS::DB::Item::Album->Lookup( album_id => $props->{album_id} );
        $albumName = $albumDBItem->title;
    }
    $self->{AlbumName} = Common::FormObject::Scalar::String->new( value => $albumName, readOnly => 1 );

    # this is used to determine if the track can be deleted or not.
    # if the track is linked to any licenses, contracts or sales then it cannot be deleted.
    #
    # JPK - Extended this to include Canadian licenses.
    #
    my ( $linkedCountLicense, $linkedCountContract, $linkedCComp );
    if ( $props->{track_id} ) {
        my $collLicense = RPS::DB::Item::TrackLicense->GetLicensesByTrackID( $props->{track_id} );
        $linkedCountLicense = $collLicense->size;
        $linkedCComp        = 0;
        while ( $collLicense->hasNext() ) {
            my $obj = $collLicense->next();
            if ( RPS::DB::Item::TrackLicense::kControlledComposition == $obj->type ) {
                $linkedCComp = 1;
                last;
            }
        }

        my $caCollLicense = RPS::DB::Item::CATrackLicense->GetLicensesByTrackID( $props->{track_id} );
        $linkedCountLicense += $caCollLicense->size;
        while ( $caCollLicense->hasNext() ) {
            my $obj = $caCollLicense->next();
            if ( RPS::DB::Item::CATrackLicense::kControlledComposition == $obj->type ) {
                $linkedCComp = 1;
                last;
            }
        }

        my $collContract = RPS::DB::Item::TrackContract->GetByTrackID( $props->{track_id} );
        $linkedCountContract = $collContract->size;
    }
    $self->{LinkedLicenses}  = Common::FormObject::Scalar->new( value => $linkedCountLicense,  readOnly => 1 );
    $self->{LinkedContracts} = Common::FormObject::Scalar->new( value => $linkedCountContract, readOnly => 1 );
    $self->{LinkedCComp}     = Common::FormObject::Scalar->new( value => $linkedCComp,         readOnly => 1 );

    my $trackCount;
    if ( $props->{album_id} ) {
        $trackCount = RPS::DB::Item::Track->GetTrackCountByAlbumID( $props->{album_id} );
    }
    $self->{AlbumTrackCount} = Common::FormObject::Scalar->new( value => $trackCount, readOnly => 1 );

    if ( defined( $props->{master_id} ) && $props->{master_id} > 0 ) {
        $self->{Master} = RPS::Master::Master->new( masterID => $props->{master_id} );
    } else {
        $self->{Master} = RPS::Master::Master->new();
    }

    $self->{AdditionalArtistList} = RPS::Catalog::TrackAdditionalArtistHash->new( trackID => $props->{track_id} );
}

# We want to detect when we're changing the track order.
# So we'll override the TrackOrder accessor to set a flag.
#
sub TrackOrder {
    my ( $self, $newVal ) = @_;

    if ( !defined $newVal ) {
        return $self->{TrackOrder}->access();
    }

    my $oldVal = $self->{TrackOrder}->access();

    # !!! Remember, access returns '1' upon successful assignment.
    # !!!
    my $returnVal = $self->{TrackOrder}->access($newVal);

    if ( $returnVal && $oldVal != $newVal ) {
        $self->{_renumberTracks} = 1;
    }

    return $returnVal;
}

sub validate {
    my ( $self, %args ) = @_;

    my $valid = $self->SUPER::validate(%args);

    # let's validate the track_number
    #
    my $trackCount = RPS::DB::Item::Track->GetTrackCountByAlbumID( $self->AlbumID() );

    if ( $self->TrackID() ) {

        # existing tracks can only be numbered in the current range
        #
        if ( $self->TrackOrder() > $self->AlbumTrackCount() || $self->TrackOrder() < 1 ) {
            $self->{TrackOrder}->setError(Common::FormObject::kErrFieldInvalid);
            $valid = 0;
        }
    } else {

        # new tracks can only be numbered in the current range + 1
        #
        #if($self->TrackOrder() > $self->AlbumTrackCount()+1 || $self->TrackOrder() < 1)
        #{
        #	$self->{TrackOrder}->setError(Common::FormObject::kErrFieldInvalid);
        #	$valid = 0;
        #}
    }

    unless ( $self->_validateMetadata(%args) ) {
        $valid = 0;
    }

    $valid = 0
      unless $self->{AdditionalArtistList}->validate(%args);

    return $valid;
}

sub _validateMetadata() {
    my ( $self, %args ) = @_;
    my $validator = $args{metadataValidator};

    my $currentData = RPS::DB::Item::Track->Lookup( track_id => $self->TrackID )
      if ( $self->TrackID );

    my $scalar_rule_map = {
        Title => {
            legacy   => 'title',
            validate => 'track-title'
        },
        TrackOrder => {
            legacy   => 'track_order',
            validate => 'track-number'
        },
        ClientTrackID => {
            legacy   => 'client_track_id',
            validate => 'client-track-id'
        },
        ParentalAdvisory => {
            legacy   => 'parental_advisory',
            validate => 'explicit'
        },
        MediaType => {
            legacy   => 'media_type',
            validate => 'media-type'
        },
        Custom1 => {
            legacy   => 'custom_1',
            validate => 'track-custom-1'
        },
        Custom2 => {
            legacy   => 'custom_2',
            validate => 'track-custom-2'
        },
        Custom3 => {
            legacy   => 'custom_3',
            validate => 'track-custom-3'
        }
    };

    my $valid = $self->SUPER::_validateMetadata(
        fieldMap          => $scalar_rule_map,
        currentData       => $currentData,
        metadataValidator => $validator
    );

    unless ($validator) {
        Common::Log::Debug( "No validator passed to " . ref($self) . "::_validateMetadata, skipping validation!!!!!" );
        return $valid;
    }

    my $currentMaster = RPS::DB::Item::Master->Lookup( master_id => $self->{Master}->MasterID )
      if ( $self->{Master} && $self->{Master}->MasterID );

    if (   $validator->ignore_current_data()
        || !$currentMaster
        || $currentMaster->isrc ne $self->{Master}->ISRC ) {
        my $compResult = $validator->validate_item( 'isrc', $self->{Master}->ISRC );

        if ($compResult) {
            $self->{Master}->{ISRC}->setError( $compResult->{code} );
            $self->{Master}->{ISRC}->setErrorString( $compResult->{message} );
            $valid = 0;
        }
    }

    if (
        $self->{Master}->ISRC
        && RPS::DB::Item::Track::hasDuplicateISRC(
            album_id   => $self->AlbumID,
            track_id   => $self->TrackID,
            isrc       => $self->{Master}->ISRC,
            media_type => $self->MediaType
        )
      ) {
        $self->{Master}->{ISRC}->setError(Common::FormObject::kErrDuplicate);
        $valid = 0;
    }

    my $master_valid = 1;
    $master_valid = $self->{Master}->_validateMetadata(
        fieldMap          => $scalar_rule_map,
        currentData       => $currentData,
        metadataValidator => $validator
    ) if ( $self->{Master} );

    my ( $pGenre, $sGenre ) = RPS::DB::Item::Genre->getGenreNames( album_id => $self->AlbumID );

    my $song_valid = 1;
    if ( $self->{Master} && $self->{Master}->{Song} ) {
        my $compResult = $validator->validate_item(
            'track-composer',
            $self->{Master}->{Song}->ComposerName, undef,
            'primary_genre'   => $pGenre,
            'secondary_genre' => $sGenre
        );
        if ($compResult) {
            $self->{Master}->{Song}->{ComposerName}->setError( $compResult->{code} );
            $self->{Master}->{Song}->{ComposerName}->setErrorString( $compResult->{message} );
            $song_valid = 0;
        }
    }

    #
    # We also need to validate the composer of a song. The problem is we need
    # album ID to get the correct genre.  So we need to test that constraint here
    # instead of in the song object.
    #

    return $valid && $master_valid && $song_valid;
}

sub save {
    my ($self) = @_;

    my $renumberFlag = $self->{_renumberTracks};

    my $trackDBObj = $self->_saveTrack();

    # Clear the flag before we init
    #
    $self->{_renumberTracks} = 0;

    $self->{AdditionalArtistList}->save( trackID => $trackDBObj->track_id );

    $self->_init( _dbItem => $trackDBObj );

    if ($renumberFlag) {
        RPS::DB::Item::Track->RenumberTracks($trackDBObj);
    }

}

sub _saveTrack {
    my ($self) = @_;

    # -------------------------------
    # Handle the Master
    # -------------------------------
    $self->{Master}->save();
    my $masterID = $self->{Master}->MasterID();
    $self->MasterID($masterID);

    my $dbObj = $self->SUPER::_saveTrack();

    return $dbObj;

}

sub OLDsave {
    my $self = shift @_;

    # -------------------------------
    # Handle the Track artist here
    # -------------------------------

    # Translate artist name to artist id, creating the artist if necessary.
    # Use the $artistObj to set artist_id in the dbItem.
    #
    my $artistObj;
    $artistObj = RPS::DB::Item::Artist->Lookup( name => $self->ArtistName() );
    if ( !defined $artistObj ) {
        $artistObj = RPS::DB::Item::Artist->Create( name => $self->ArtistName() );
        ## only good place to set the clean name for artists atm
        $artistObj->name_clean( Common::Util::clean_name_catalog( $self->ArtistName() ) );
        $artistObj->save();
    }

    # -------------------------------
    # Handle the Master
    # -------------------------------
    $self->{Master}->save();
    my $masterID = $self->{Master}->MasterID();

    # -------------------------------
    # Save myself (Track)
    # -------------------------------
    #

    # These are flags we'll use below to determine how to re-number
    # the tracks. We need to know if this is a new track,
    # if a track number is being changed we need to remember what
    # the original track number was.
    #
    my $renumberTracks = 0;
    my $trackDBObj;
    if ( $self->TrackID() ) {
        $trackDBObj = RPS::DB::Item::Track->Lookup( track_id => $self->TrackID() );

        # are we changing track number?
        #
        if ( $trackDBObj->track_order != $self->TrackOrder() ) {
            $renumberTracks = 1;
        }
    } else {
        $trackDBObj     = RPS::DB::Item::Track->Create();
        $renumberTracks = 1;
    }

    $trackDBObj->album_id( $self->AlbumID() );
    $trackDBObj->track_order( $self->TrackOrder() );
    $trackDBObj->title( $self->Title() );
    $trackDBObj->mechanical_exempt( $self->MechanicalExempt() );
    $trackDBObj->ca_mechanical_exempt( $self->CAMechanicalExempt() );
    $trackDBObj->parental_advisory( $self->ParentalAdvisory() );

    # !!! These fields have not been added to the form, yet.
    #
    $trackDBObj->client_track_id( $self->ClientTrackID() );
    $trackDBObj->genre_name( $self->GenreName() );
    $trackDBObj->custom_1( $self->Custom1() );
    $trackDBObj->custom_2( $self->Custom2() );
    $trackDBObj->custom_3( $self->Custom3() );

    # foreign keys
    #
    $trackDBObj->master_id($masterID);
    $trackDBObj->artist_id( $artistObj->artist_id );

    # Derrive clean names
    #
    $trackDBObj->title_clean( Common::Util::clean_name_catalog( $self->Title() ) );

    $trackDBObj->save();

    # reload
    #
    $self->_init( _dbItem => $trackDBObj );

    if ($renumberTracks) {
        RPS::DB::Item::Track->RenumberTracks($trackDBObj);
    }
}

sub delete {
    my ($self) = @_;

    # first we need to check if this Track have any License, CComps, contracts,
    # or sales associated with it
    # if yes to any of the above, then we can't delete so return the appropriate message
    if ( $self->LinkedLicenses() > 0 ) {
        $self->setError(Common::FormObject::kErrTrackHasLicenses);
        return undef;
    }

    if ( $self->LinkedContracts() > 0 ) {
        $self->setError(Common::FormObject::kErrTrackHasContracts);
        return undef;
    }

    # We need to check all of the digital track products for sales before we start deleting any of them.
    my $products = RPS::DB::Item::Product->GetAllDigitalTrackProducts( $self->TrackID() );
    while ( $products->hasNext() ) {
        my $product = $products->next();
        if ( !RPS::DB::Item::Product->CanDelete( $product->product_id() ) ) {
            $self->setError(Common::FormObject::kErrTrackHasSales);
            return undef;
        }
    }

    # If we made it this far, then it must be okay to delete them all.
    $products = RPS::DB::Item::Product->GetAllDigitalTrackProducts( $self->TrackID() );
    while ( $products->hasNext() ) {
        my $product = $products->next();
        $product->delete();
    }

    # delete the Master
    #
    $self->{Master}->delete();

    # delete product track items
    #
    my $productTracks = RPS::DB::Item::ProductTrack->GetByTrackID( $self->TrackID() );
    while ( $productTracks->hasNext() ) {
        my $dbObj = $productTracks->next();
        $dbObj->delete();
    }

    # ok, now we can delete ourself (the track)
    #
    my $track = RPS::DB::Item::Track->Lookup( track_id => $self->TrackID() );
    $track->delete();

    RPS::DB::Item::Album->RenumberTracks( $self->AlbumID() );

    return 1;
}

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