#------------------------------------------------------------
# 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.
###
