# Gmusicbrowser: Copyright (C) 2005-2011 Quentin Sculo <squentin@free.fr>
# Lastfm_pcGet: Copyright (C) Markus Klinga (laite) <laite@gmx.com>
#
# This file is a plugin to Gmusicbrowser.
# It is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 3, as
# published by the Free Software Foundation.


=gmbplugin LASTFM_PCGET
name	lastfm_pcGet
title	playcount fetcher (v0.2)
desc	Downloads playcount, tag correction and loved-status for currently playing song
=cut

# TODO

package GMB::Plugin::LASTFM_PCGET;
use strict;
use warnings;
use constant
{	CLIENTID => 'gmusicbrowser / last.fm_pcget', VERSION => '0.2',
	OPT => 'PLUGIN_LASTFM_PCGET_',#used to identify the plugin's options
	APIKEY => '58b7cf77be819f5473bffee8d781d9c0',
};

::SetDefaultOptions(OPT, pcvalues => 'always', multiple => 'split_evenly',checkcorrections => 1, titlechange => 'change_all', 
		artistchange => 'change_all', synclovedwithlabel => 0, labeltosync => 'Loved', syncmethod => 'from_lastfm');

use utf8;
use Encode;
use Digest::MD5 'md5_hex';
require $::HTTP_module;

my $self=bless {},__PACKAGE__;
my $Log=Gtk2::ListStore->new('Glib::String');
my $waiting;
my $oldID = -1;
my $Datafile = $::HomeDir.'lastfm_corrections';
my $Banfile = $::HomeDir.'lastfm_corrections.banned';
my @corrections;
my @checks; my @banned;
my $s2; 
my $LOVED=0; my $token;
my $SONGTOLOVE = -1;
my $REQUESTED_METHOD;

my %button=
(	class	=> 'Layout::Button',
	state	=> sub {($LOVED==1)? 'loved' : 'not_loved'},
	stock	=> {loved => 'lastfm_heart', not_loved => 'lastfm_heart_empty' },
	tip	=> "Love this track",	
	click1	=> sub {ToggleLoved(1)},
	click3	=> sub {ToggleLoved(0)},
	autoadd_type	=> 'button main',
	event	=> 'lastfm_LovedStatus',
);
 

sub Start
{
	Layout::RegisterWidget(lastfmLoveButton=>\%button);
	::Watch($self,PlayingSong=> \&SongChanged);
	loadCorrections();
	loadBanned();
}
sub Stop
{
	$waiting->abort if ($waiting);
	::UnWatch_all($self);
	Layout::RegisterWidget('lastfmLoveButton');
}

sub prefbox
{
	my $vbox=Gtk2::VBox->new(::FALSE, 2);
	my $vbox2=Gtk2::VBox->new(::FALSE, 2);
	my $vbox3=Gtk2::VBox->new(::FALSE, 2);
	my $sg1=Gtk2::SizeGroup->new('horizontal');
	my $sg2=Gtk2::SizeGroup->new('horizontal');
	my $entry1=::NewPrefEntry(OPT.'USER',_"Username: ", sizeg1 => $sg1,sizeg2=>$sg2);
	
	my $listcombo= ::NewPrefCombo( OPT.'pcvalues', { always => 'Always set last.fm value', biggest => 'Set bigger amount', smallest => 'Set smaller amount',});
	my $l=Gtk2::Label->new('How to deal with different playcount values: ');

	my $listcombo2= ::NewPrefCombo( OPT.'multiple', { playing => 'Set only playing tracks playcount', split_evenly => 'Split playcount evenly', separate => 'Set every tracks playcount separately', as_one => 'Handle different songs as one',});
	my $l2=Gtk2::Label->new('In case of multiple tracks with same artist and title: ');

	my $button2=Gtk2::Button->new();
	$button2->signal_connect(clicked => \&Correct);
	$button2->set_label("Show Corrections");

	my $check=::NewPrefCheckButton(OPT."checkcorrections",'Check for corrections',horizontal=>1);
	my $cl=Gtk2::Label->new();
	
	if (scalar@corrections == 1){$cl->set_label(" ".scalar@corrections." correction noted");}
	else {$cl->set_label(" ".scalar@corrections." corrections noted");}

	my $listcombo3= ::NewPrefCombo( OPT.'titlechange', { change_one => 'Change only the noted track', change_all => 'Change all songs with same artist/title',});
	my $l3=Gtk2::Label->new('When correcting track title: ');

	my $listcombo4= ::NewPrefCombo( OPT.'artistchange', {change_one => 'Change only for the noted track', change_all => 'Change all tracks from artist'});
	my $l4=Gtk2::Label->new('When correcting artist: ');

	#track-love
	my $lcheck1 = ::NewPrefCheckButton(OPT.'synclovedwithlabel','Sync last.fm loved tracks with label ');	
	my $lentry1 = ::NewPrefEntry(OPT.'labeltosync', undef, sizeg1 => $sg1,sizeg2=>$sg2);
	my $lcombo1 = ::NewPrefCombo(OPT.'syncmethod', {from_lastfm => 'Sync only from last.fm to local database', from_local => 'Sync only from local database to last.fm', both_ways_and => 'Sync with method "last.fm AND local"', both_ways_or => 'Sync with method "last.fm OR local"'});
	
	my $frame1=Gtk2::Frame->new(_" Playcount fetching ");
	my $frame2=Gtk2::Frame->new(_" Track/Artist correction ");
	my $frame3=Gtk2::Frame->new(_" Track loving ");

	$vbox=::Vpack($entry1,[$l,$listcombo],[$l2,$listcombo2]);
	$vbox2=::Vpack($check,[$l3,$listcombo3,$l4,$listcombo4],[$button2,$cl]);
	$vbox3=::Vpack([$lcheck1,$lentry1, $lcombo1]);

	$frame1->add($vbox);
	$frame2->add($vbox2);
	$frame3->add($vbox3);
	
	my $fi = ::Vpack( $frame2, $frame3, $frame1);
	$fi->add(::LogView($Log));
	
	return $fi;
}

sub ToggleLoved
{
	my $love = shift;

	if (not defined $::Options{OPT.$::Options{OPT.'USER'}.'sessionkey'})
	{
		my$dialog = Gtk2::MessageDialog->new (undef,
                                      'destroy-with-parent',
                                      'question', # message type
                                      'yes-no', # which set of buttons?
                                      "You need last.fm sessionkey for this action.\nDo you want to configure it now?");
		my $response = $dialog->run;		
		GetSessionKey() if ($response eq 'yes');
		$dialog->destroy;
	}
	else
	{
		$REQUESTED_METHOD = ($love)? 'love' : 'unlove';
	
		return unless ((defined $::Options{OPT.$::Options{OPT.'USER'}.'sessionkey'}) and (defined $::Options{OPT.'USER'}));
		
		$SONGTOLOVE = $::SongID; #save this here in case it takes too much time to process request
	
		my $sk = $::Options{OPT.$::Options{OPT.'USER'}.'sessionkey'};
		my $user = $::Options{OPT.'USER'};
		my ($artist,$title) = Songs::Get($::SongID,qw/artist title/);

		my $signature = "api_key".APIKEY.'artist'.$artist.'methodtrack.'.$REQUESTED_METHOD.'sk'.$sk.'track'.$title.'bfe7a3fd2eacbd28336cc0dfc9b2dd4d';
		utf8::encode($signature);
		$signature = md5_hex($signature);
		$artist =~ s/&/\%26/; $title =~ s/&/\%26/;
		
		my $post = 'method=track.'.$REQUESTED_METHOD.'&track='.$title.'&artist='.$artist.'&api_key='.APIKEY.'&sk='.$sk.'&api_sig='.$signature;
		utf8::encode($post);

		Send(\&HandleLoveRequest,'http://ws.audioscrobbler.com/2.0/',$post);
		
	}	
}

sub HandleLoveRequest
{
	my @response = @_;
	my $allIsWell=0;
	foreach my $line (@response) {
		$allIsWell = 1 if ($line =~ m/lfm status=\"ok\"/);
	}
	
	 if ($allIsWell) {
	 	SetLoved($REQUESTED_METHOD);
		my $action = ($REQUESTED_METHOD eq 'love')? '+' : '-';
	 	Songs::Set($SONGTOLOVE, $action.'label' => $::Options{OPT.'labeltosync'});
	 	Log('Successfully '.$REQUESTED_METHOD.'d track '.Songs::Get($SONGTOLOVE,'artist').' - '.Songs::Get($SONGTOLOVE,'title'));
	 }
	 else { Log('ERROR: Something went wrong when tried to love track.');}
	
	$SONGTOLOVE = -1;
	return $allIsWell;
}


sub GetSessionKey
{	
	my $user=$::Options{OPT.'USER'};
	return 0 unless defined $user && $user ne '';

	my $signature = md5_hex("api_key".APIKEY.'methodauth.gettoken'.'bfe7a3fd2eacbd28336cc0dfc9b2dd4d');
	Send(\&HandleLastfmToken,'http://ws.audioscrobbler.com/2.0/?method=auth.gettoken&api_key='.APIKEY);

}

sub HandleLastfmToken
{
	my @response = @_;
	foreach my $line (@response) {
		if ($line =~ m/<token>(.+)<\/token>/){ $token = $1; last;}
	}
	return 0 unless (defined $token);

	my $dialog = Gtk2::Dialog->new ('Confirmation', undef,[qw/modal destroy-with-parent/]);
	$dialog->set_position('center-always');
	$dialog->set_border_width(4);
	
    my $label = Gtk2::Label->new("This property needs to have permission to your last.fm data in order to work.\nPlease give your permission in last.fm internet page BEFORE pressing continue!\nYou can do this in following address:");
    my $label2 = Gtk2::Label->new;
    $label2->set_markup('<span underline="single" color="blue">'.::PangoEsc('http://www.last.fm/api/auth/?api_key='.APIKEY.'&token='.$token).'</span>'); 
    $label2->set_ellipsize('middle');
    $label2->set_max_width_chars(50);
    
    my $next = Gtk2::Button->new('I have accepted permission in last.fm, continue!');
    $next->set_sensitive(0);
    my $runbutton = Gtk2::Button->new('Open in browser');
    
    $runbutton->signal_connect(clicked => sub {
    	::main::openurl('http://www.last.fm/api/auth/?api_key='.APIKEY.'&token='.$token);
    	$next->set_sensitive(1);
    });

	$next->signal_connect(clicked => sub {
		$dialog->destroy;
		
		#then final authing
		my $signature = md5_hex("api_key".APIKEY.'methodauth.getsessiontoken'.$token.'bfe7a3fd2eacbd28336cc0dfc9b2dd4d');
		Send(\&HandleLastfmSessionKey,'http://ws.audioscrobbler.com/2.0/?method=auth.getsession&api_key='.APIKEY.'&token='.$token.'&api_sig='.$signature);		
	});
    
    my $dform = ::Vpack($label,$label2);
    $dform->add(::Hpack('-',$next,'-',$runbutton));
	$dialog->get_content_area ()->add ($dform);
	$dialog->set_default_response(1);
	$dialog->show_all;

	$dialog->run;
	$dialog->destroy();
}

sub HandleLastfmSessionKey
{
	my @resp = @_;
	my $key;
	my $pict = 'info'; 
	my $message = "Session key was acquired successfully!\nYou can now start loving and unloving tracks!";
	my $topic = 'Last.fm - plugin configured';
	
	for my $line (@resp) {
		if ($line =~ m/<key>(.+)<\/key>/){ $key = $1; last;}
	}
	
	if (defined $key){
		$::Options{OPT.$::Options{OPT.'USER'}.'sessionkey'} = $key;
		Log('Successfully acquired last.fm session key for user: '.$::Options{OPT.'USER'}.'!');
	}
	else { 
		Log('ERROR! No sessionkey found! Did you give permission for it?');
		$message = 'Sessionkey couldn\'t be acquired! Are you sure you gave your permission for it?';
		$topic = 'ERROR';
		$pict = 'error';
	}

	my $dialog = Gtk2::MessageDialog->new( undef, [qw/modal destroy-with-parent/], $pict,'ok',$message );
	$dialog->set_position('center-always');
	$dialog->set_title($topic);
	$dialog->show_all;

	$dialog->run;
	$dialog->destroy;

	return (defined $key)? 1 : 0;
}

sub Send
{	my ($response_cb,$url,$post)=@_;
	my $cb=sub
	{	my @response=(defined $_[0])? split "\012",$_[0] : ();
		$waiting = undef;
		&$response_cb(@response);
	};
	$waiting=Simple_http::get_with_cb(cb => $cb,url => $url,post => $post);
}


sub SongChanged
{
	return if ($oldID == $::SongID);
	
	$oldID = $::SongID;
	Sync();
}


sub Log
{	my $text=$_[0];
	$Log->set( $Log->prepend,0, localtime().'  '.$text );
	warn "$text\n" if $::debug;
	if (my $iter=$Log->iter_nth_child(undef,200)) { $Log->remove($iter); }
}

sub loadCorrections()
{
	open(my $fh, '<', $Datafile) or return;
	@corrections = <$fh>;
	close($fh);
}

sub saveCorrections()
{
		return 'no datafile' unless defined $Datafile;

		my $content = '';
		
		foreach my $a (@corrections) { if ($a =~ m/(.+)\t(.+)\t(.+)/) {$content .= $a;}}

		open my $fh,'>',$Datafile or warn "Error opening '$Datafile' for writing : $!\n";
		print $fh $content   or warn "Error writing to '$Datafile' : $!\n";
		close $fh;
}

sub checkCorrection()
{
	return if ($waiting);

	my ($artist,$title) = Songs::Get($::SongID,qw/artist title/);
	my $url = 'http://ws.audioscrobbler.com/2.0/?method=track.getcorrection&artist='.::url_escapeall($artist).'&track='.::url_escapeall($title).'&api_key='.APIKEY;

	my $correcttrack = '';
	my $correctartist = '';
	
	my $cb=sub
	{	
		$waiting=undef;
		my @r=(defined $_[0])? split "\012",$_[0] : ();
		foreach my $a (@r) 
		{
			#if there is no corrections, <name> won't exist
			#if there is, it's always track first, then artist
			
			if ( $a =~ m/<name>(.+)<\/name>/)
			{
				 if ($correcttrack eq '') { $correcttrack = $1; }
				 elsif ($correctartist eq '') { $correctartist = $1; }
			}
		}
		
		if ($correcttrack ne '') 
		{
			if (my $utf8=Encode::decode_utf8($correcttrack)) {$correcttrack=$utf8}
			if (my $utf8=Encode::decode_utf8($correctartist)) {$correctartist=$utf8}
			
			$correcttrack =~ s/\&amp\;/\&/g;
			$correctartist =~ s/\&amp\;/\&/g;
			
			my $new_correction = Songs::Get($::SongID,'fullfilename')."\t".$correctartist."\t".$correcttrack."\n";
			my $is_banned = 0;

			#then we check if current file has been banned or if it's already on @corrections
			foreach my $bb (@banned) { if ($bb eq $new_correction) { $is_banned = 1; }} 
			foreach my $bb (@corrections) { if ($bb eq $new_correction) { $is_banned = 1; }} 

			if ($is_banned == 0)
			{
				my ($artist,$title) = Songs::Get($::SongID,qw/artist title/);
				my $crr = (($artist eq $correctartist) or ($title eq $correcttrack))? (($artist ne $correctartist)? 'ARTIST' : 'TITLE') : ('ARTIST & TITLE');
				Log('New correction found for '.$crr.': '.$artist.' - '.$title.'  ->  '.$correctartist.' - '.$correcttrack);
				push(@corrections,$new_correction); 
				saveCorrections();
			} 
		}
	};
	my $waiting=Simple_http::get_with_cb(cb => $cb,url => $url,post => '');
}

sub findSimilar
{
	my $initialfilter = $_[0];
	my $oldartist = lc $_[1];
	my $oldtitle = lc $_[2];
	
	my @final = ();
	
	foreach my $a (@$initialfilter) 
	{
		my $artist = lc Songs::Get($a,'artist');
		my $title = lc Songs::Get($a,'title');
		
		if ((($oldartist eq '') or ($oldartist eq $artist)) and (($oldtitle eq '') or ($oldtitle eq $title))) { push @final,$a;}
	}
	
	return \@final;
}

sub GetPcValueSetting
{
	my ($newvalue,$oldvalue) = shift;
	
	$oldvalue = 0 unless (defined $oldvalue);
	if ($::Options{OPT.'pcvalues'} eq 'always') { return $newvalue; }
	elsif ($::Options{OPT.'pcvalues'} eq 'biggest') { return ($newvalue > $oldvalue)? $newvalue : $oldvalue;}
	elsif ($::Options{OPT.'pcvalues'} eq 'smallest') {return ($newvalue < $oldvalue)? $newvalue : $oldvalue; }
	
	Log('Error in GetPcValueSetting! Returning zero.');
	return 0; 
}

sub DistributeEvenly
{
	my ($value,$items,$songid) = @_;
	
	$songid = -1 unless (defined $songid);
	my $whole = int($value/scalar@$items);
	my $rest = $value%scalar@$items;
	my %valuehash;

	foreach (@$items)
	{
		if (($rest > 0) and ($_ != $songid)) { 
			$valuehash{$_} = $whole+1; 
			$rest--; 
		}
		else { 
			$valuehash{$_} = $whole;
		}
	}
	
	return \%valuehash;
}

sub SetValue
{
	my ($songid,$artist,$album,$title,$newvalue) = @_;
	
	my $multifilter=findSimilar(Filter->newadd(1,'title:s:'.$title, 'artist:s:'.$artist)->filter,$artist,$title);
	my %tochange;
	
	if ($::Options{OPT.'multiple'} eq 'playing') { 
		$tochange{$songid} = GetPcValueSetting($newvalue,Songs::Get($songid,'playcount'));
	}
	elsif ($::Options{OPT.'multiple'} eq 'split_evenly') {
		my $sum = 0;
		$sum += (Songs::Get($_,'playcount') || 0) for (@$multifilter);
		my $value = GetPcValueSetting($newvalue,$sum);
		%tochange = %{DistributeEvenly($value,$multifilter,$songid)};
	}
	elsif ($::Options{OPT.'multiple'} eq 'separate') {
		for (@$multifilter){
			$tochange{$_} = GetPcValueSetting($newvalue,Songs::Get($_,'playcount'));
		}
	}
	elsif ($::Options{OPT.'multiple'} eq 'as_one')
	{	
		my $avg = 0;
		$avg += (Songs::Get($_,'playcount') || 0) for (@$multifilter);
		$avg /= scalar@$multifilter;
		for (@$multifilter){
			$tochange{$_} = GetPcValueSetting($newvalue,$avg);
		}
	}
	
	my $num=0; my $pre='';
	for my $id (keys %tochange)
	{
		$num++;
		my $songoc = Songs::Get($id,'playcount');
		my ($art,$alb,$tit) = Songs::Get($id,qw/artist album title/);
		$pre = '['.($num).'/'.(scalar (keys %tochange)).'] ';

		if ((not defined $songoc) or ($tochange{$id} != $songoc))
		{
			Songs::Set($id,'playcount' => $tochange{$id});
			Log($pre."Set playcount to ".$tochange{$id}." for ".$art." - ".$alb." - ".$tit);
		}
		else { Log($pre."Nothing to change (playcount ".$tochange{$id}.") for ".$art." - ".$alb." - ".$tit) }
	}
	
	return 1;
}

#this is called on songchange and when loverequest has been (successfully) handled
sub SetLoved
{
	my $loved = shift;
	
	if ($loved !~ /\d+/) { $loved = ($loved eq 'love')? 1 : 0;}
	
	if ($loved != $LOVED){
		$LOVED = $loved;
		::HasChanged('lastfm_LovedStatus');
	}

	return 1;
}
sub Sync()
{
	my $user = $::Options{OPT.'USER'};
	my $love = 0; #by default we show no love

	if ($user eq '') { return;}

	my $artist = Songs::Get($::SongID,'artist');
	my $album = Songs::Get($::SongID,'album');
	my $title = Songs::Get($::SongID,'title');

	my $url = 'http://ws.audioscrobbler.com/2.0/?method=track.getinfo&username='.$user.'&api_key='.APIKEY.'&artist='.::url_escapeall($artist).'&track='.::url_escapeall($title);
	
	my $cb=sub
	{	
		$waiting=undef;
		my @r=(defined $_[0])? split "\012",$_[0] : ();
		my $foundpc=0;
		foreach my $a (@r) 
		{
			if ( $a =~ m/<userplaycount>(\d+)<\/userplaycount>/){
				SetValue($::SongID,$artist,$album,$title,$1);
				$foundpc=1;
			}
		
			if ( $a =~ m/<userloved>(\d+)<\/userloved>/)	{
				$love = $1;
			}
		}
		
		if (!$foundpc) { Log('No previous playcount for '.$artist.' - '.$title);}
		
		SetLoved($love);
		SyncLabel($love,$::SongID) if ($::Options{OPT.'synclovedwithlabel'});

		if ($::Options{OPT.'checkcorrections'} == 1) {checkCorrection();}
	};
	my $waiting=Simple_http::get_with_cb(cb => $cb,url => $url,post => '');
}

# 'loved' here comes from last.fm
sub SyncLabel
{
	my ($loved,$songid) = @_;
	$songid ||= $SONGTOLOVE;
	my $local_loved = HasSyncLabel($songid);
	return 0 unless ($songid > -1);
	return 1 if ($loved == $local_loved); #no need to change anything if they're the same already
	
	my $which = 0;
	
	#last.fm value must be changed
	$which = 1 if (($::Options{OPT.'syncmethod'} eq 'both_ways_and') and ($loved)); # 0 && 1 = 0 [local && lastfm = for last.fm]
	$which = 1 if (($::Options{OPT.'syncmethod'} eq 'both_ways_or') and (!$loved)); # 1 || 0 = 1 
	$which = 1 if ($::Options{OPT.'syncmethod'} eq 'from_local'); # 0 > 1 = 0

	#local value must change
	$which = 2 if (($::Options{OPT.'syncmethod'} eq 'both_ways_and') and (!$loved)); # 1 && 0 = 0 [local && lastfm = for LOCAL]
	$which = 2 if (($::Options{OPT.'syncmethod'} eq 'both_ways_or') and ($loved)); # 0 || 1 = 1 
	$which = 2 if ($::Options{OPT.'syncmethod'} eq 'from_lastfm');
	
	return 1 if (!$which); #nothing to change after all

	if ($which == 1)
	{
		if (defined $::Options{OPT.$::Options{OPT.'USER'}.'sessionkey'}) #don't do anything if user hasn't configured sk yet
		{
			Log('Syncing local label for '.Songs::Get($songid,'artist').' - '.Songs::Get($songid,'title').' to last.fm');
			ToggleLoved(!$loved);
		}
	}
	else 
	{
		Log('Syncing last.fm loved track - status to local label for '.Songs::Get($songid,'artist').' - '.Songs::Get($songid,'title'));
		my $action = ($local_loved)? '-' : '+';
		Songs::Set($songid, $action.'label' => $::Options{OPT.'labeltosync'});
	}
	
	return 1;
}

sub HasSyncLabel
{
	my $songid = shift;
	my ($dh) = Songs::BuildHash('id', $::Library, undef, 'label');
	my $found = 0;
	
	for (@{$$dh{$songid}})
	{
		if ($_ eq $::Options{OPT.'labeltosync'}) {
			$found = 1;
			last;
		}
	}
	
	return $found;
}

sub Correct
{
	$s2 = Gtk2::Dialog->new(_"last.fm suggestions",undef,'destroy-with-parent');
	@checks = ();
	
	$s2->set_default_size(700, 400);
	$s2->set_position('center-always');
	$s2->set_border_width(6);

	# Contents: textentry, searchbutton, stopbutton and scrollwindow (for radiobuttons).
	$s2->{selall}= my $selall = ::NewIconButton('gtk-select-all', _"Select all");
	$s2->{selnone}	= my $selnone	  = ::NewIconButton('gtk-clear', _"Select none");
	$s2->{remsel}= my $remsel = ::NewIconButton('gtk-delete', _"Hide selected");
	$s2->{corsel}	= my $corsel	  = ::NewIconButton('gtk-save', _"Correct selected");
	$s2->{close}= my $close = ::NewIconButton('gtk-close', _"Close");
	$s2->{label1} = my $label1 = Gtk2::Label->new('These are last.fm\'s suggestions for corrections');
	$s2->{vbox}	= my $vbox = Gtk2::VBox->new(0,0);
	
	my $scrwin = Gtk2::ScrolledWindow->new();
	$scrwin->set_policy('automatic', 'automatic');
	$scrwin->add_with_viewport($vbox);
	$s2->get_content_area()->add( ::Vpack($label1,'_', $scrwin,[$selall, $selnone, $remsel, $corsel,$close]) );

	foreach my $a (@corrections)
	{
		if ($a =~ m/(.+)\t(.+)\t(.+)/)
		{
			push(@checks, Gtk2::CheckButton->new(Songs::Get(Songs::FindID($1),'artist')." - ".Songs::Get(Songs::FindID($1),'title')." -> ".$2." - ".$3));
		}
	}
	$s2->{vbox}->pack_start($_,0,0,0) foreach @checks;

	# Handle the relevant events (signals).
	#
	$close ->signal_connect(clicked => sub {$s2->destroy();});

	$remsel ->signal_connect(clicked => sub { confirm_action(1);});
	$corsel ->signal_connect(clicked => sub { confirm_action(2);});
	
	$s2->signal_connect(response => sub {$s2->destroy();});
	$s2->signal_connect(destroy => \&saveBanned);
	$selall->signal_connect(clicked  => sub {
		foreach my $c (@checks) { $c->set_active(1);}
	});
	$selnone->signal_connect(clicked  => sub {
		foreach my $c (@checks) { $c->set_active(0);}
	});

	$s2->show_all();
}

sub loadBanned()
{
	open(my $fh, '<', $Banfile) or return;
	@banned = <$fh>;
	close($fh);
}
sub saveBanned
{
	#write banned files to 'lastfm_corrections.banned' so they don't appear ever again
	return 'no datafile' unless defined $Banfile;

	my $content = '';
	
	return if (scalar@banned == 0);
	
	foreach my $a (@banned) { $content .= $a;}

	open my $fh,'>',$Banfile or warn "Error opening '$Banfile' for writing : $!\n";
	print $fh $content   or warn "Error writing to '$Banfile' : $!\n";
	close $fh;
}

sub confirm_action()
{
	my $type = $_[0];
	my $dialog = Gtk2::Dialog->new ('Confirmation', undef,[qw/modal destroy-with-parent/],'gtk-ok'     => 'ok','gtk-cancel' => 'cancel');
	$dialog->set_position('center-always');
	$dialog->set_border_width(4);

	$dialog->signal_connect(response => sub {$_[0]->destroy;
		if ($_[1] eq 'ok') 
		{ 
			if ($type == 1) {remove_selected();}
			elsif ($type == 2) {correctSelected(0);}
		}
	});
	
    my $label = Gtk2::Label->new();
    my $label2 = Gtk2::Label->new();
    if ($type == 1) 
    { 
    	$label->set_label('Selected corrections will be removed from list.');
    	$label2->set_label('To see them again you have to manually edit banned-file (see README).')
    }
    elsif ($type == 2) 
    { 
    	$label->set_label('Selected corrections will now be applied to filetags.');
    	$label2->set_label('You can\'t undo this operation.')
    	
    }
    $dialog->get_content_area ()->add ($label);
    $dialog->get_content_area ()->add ($label2);
    $dialog->show_all;
}

sub okDialog
{
	my $text1 = $_[0];
	my $text2 = $_[1];
	
	my $dialog = Gtk2::Dialog->new ('Success', undef,[qw/modal destroy-with-parent/],'gtk-ok'     => 'ok');
	$dialog->set_position('center-always');
	$dialog->set_border_width(4);

	$dialog->signal_connect(response => sub {$_[0]->destroy;});
	
    my $label = Gtk2::Label->new();
    my $label2 = Gtk2::Label->new();
   	$label->set_label($text1);
   	$label2->set_label($text2);

    $dialog->get_content_area ()->add ($label);
    $dialog->get_content_area ()->add ($label2);
    $dialog->show_all;
	
}
sub remove_selected
{
	my $ramount=0;
	for (my $c=(scalar@checks-1);$c >= 0; $c--)
	{
		if ($checks[$c]->get_active) 
		{ 
			push (@banned,$corrections[$c]); 
			$s2->{vbox}->remove($checks[$c]);
			splice @checks, $c, 1;				
			splice @corrections, $c, 1;
			$ramount++;
		}
	}
	saveBanned();
	saveCorrections();
	okDialog('Succesfully removed '.$ramount.' tracks from suggestions list','Succesfully updated \'lastfm_corrections.banned\'');
}
sub correctSelected()
{
	my @correctIDs = ();
	my $tamount=0;
	my $camount=0;

	for (my $c=0; $c < scalar@checks; $c++)
	{
		if ($corrections[$c] =~ m/(.+)\t(.+)\t(.+)/)
		{
			push @correctIDs, Songs::FindID($1);
		}
	}


	for (my $c=(scalar@checks)-1; $c >= 0; $c--)
	{
		if ($corrections[$c] =~ m/(.+)\t(.+)\t(.+)/)
		{
			my $ID = Songs::FindID($1);
			
			my $oldartist =  Songs::Get(Songs::FindID($1),'artist');
			my $oldtitle =  Songs::Get(Songs::FindID($1),'title');
			
			my $newartist = $2;
			my $newtitle = $3;
			
			my $changetype;
			
			if ($newtitle ne $oldtitle) { $changetype = 1;}
			if ($newartist ne $oldartist) { $changetype = 2;}
			if (($newartist ne $oldartist) and ($newtitle ne $oldtitle)) { $changetype = 3;}
			
			my $trackfilter;
			my @changeTitle;
			my @changeArtist;
			
			if (($changetype == 1) or ($changetype == 3))
			{	
				$trackfilter = findSimilar(Filter->newadd(1,'title:s:'.$oldtitle, 'artist:s:'.$oldartist)->filter,$oldartist,$oldtitle); 
				if ($::Options{OPT.'titlechange'} eq 'change_all') { foreach my $a (@$trackfilter) { push @changeTitle,$a;} }
			}
			
			if (($changetype == 2) or ($changetype == 3))
			{
				$trackfilter = findSimilar(Filter->newadd(1,'artist:s:'.$oldartist)->filter,$oldartist,''); 
				if ($::Options{OPT.'artistchange'} eq 'change_all') { foreach my $a (@$trackfilter) { push @changeArtist,$a;} }
			}

			#don't fix something that is already in the @corrections!			
			foreach my $ch (@correctIDs)
			{
				for (my $ca=0;$ca<scalar@changeArtist;$ca++) { if (($changeArtist[$ca] != $ID) and ($changeArtist[$ca] == $ch)) { splice @changeArtist, $ca, 1;}}
				for (my $ct=0;$ct<scalar@changeTitle;$ct++) { if (($changeTitle[$ct] != $ID) and ($changeTitle[$ct] == $ch)) { splice @changeTitle, $ct, 1;}}
			}
			
			if ($checks[$c]->get_active) 
			{ 
				$camount++;
				if ((scalar@changeTitle == 0) and (scalar@changeArtist == 0))
				{
					Songs::Set($ID, artist=> $newartist, title => $newtitle);
					Log('Corrected tag for '.$oldartist.' - '.$oldtitle.' -> '.$newartist.' - '.$newtitle);
					push @correctIDs, $ID;	
					$tamount++;
				}
				else
				{
					if (scalar@changeTitle > 0) 
					{
						Songs::Set(\@changeTitle, title => $newtitle);
						Log('Corrected title tag to \''.$newtitle.'\' for '.scalar@changeTitle.' tracks');
						foreach my $cT (@changeTitle) { push @correctIDs, $cT; }					
						$tamount += scalar@changeTitle;
					}
					if (scalar@changeArtist > 0) 
					{
						Songs::Set(\@changeArtist, artist => $newartist);
						Log('Corrected artist tag to \''.$newartist.'\' for '.scalar@changeArtist.' tracks');
						foreach my $cA (@changeArtist) { push @correctIDs, $cA; }					
						$tamount += scalar@changeArtist;
					}
				}
				$s2->{vbox}->remove($checks[$c]);
 				splice @checks, $c, 1;				
 				splice @corrections, $c, 1;
			}				
		}
	}
	saveCorrections();
	okDialog('Succesfully updated '.$camount.' tracks from suggestions list','Total of '.$tamount.' tracks were updated');
}

1;