#!/usr/bin/perl -w

# $Id: player-gtk-ipc,v 1.1 1999/07/29 18:43:42 daniel Exp $
use strict;
use Audio::MikMod qw(:MikMod :Player);
use Gtk;
use Pixmaps;
use Event qw(loop unloop);
use IPC::ShareLite;
use Time::HiRes qw(usleep);

use constant TRUE  => 1;
use constant FALSE => 0;

# A threaded version might come in the future,
# but for now fork() will have to do.
$SIG{'HUP'}  = \&_real_pause;
$SIG{'USR1'} = \&_real_back;
$SIG{'USR2'} = \&_real_forward;
$SIG{'KILL'} = \&_exit;
$SIG{'INT'}  = \&_exit;
$SIG{'ALRM'} = \&_exit;

my $song_paused = IPC::ShareLite->new(
	'-key'     => 1971,
	'-create'  => 'yes',
	'-destroy' => 'yes'
) or die $!;

$song_paused->store(0);

my ($song_loaded,$song_playing) = (0,0);
my ($songfile,$songpath,$player_busy);
my ($filelabel,$songlabel,$child,$progress_timer);
my $url = 'http://electricrain.com/';

my $subs = {
	'exit'    => \&_exit,
	'back'    => \&_back,
	'play'    => \&_play,
	'pause'   => \&_pause,
	'stop'    => \&_stop,
	'forward' => \&_forward,
	'open'    => \&_open_fw,
};

##############
# Here we go..
Gtk->init;
_init();

my $window = Gtk::Widget->new('Gtk::Window','type' => '-toplevel' );

$window->set_usize(300, 200);
$window->set_title('Foo!');
$window->signal_connect('delete_event' => \&_exit);
$window->realize;

init_ui($window);

_open($ARGV[0]) if defined $ARGV[0];

Gtk->main;

exit;

##############
# Thanks Tom!
sub forksub (&) {
	my $coderef = shift;
	my $pid     = fork();
	die "Can't fork: $!" unless defined $pid;
	return $pid if $pid;
	goto &$coderef;
}

############
# Gtk setup.
sub init_ui ($) {
	my $window = shift;

	my $mask  = '';
    	my $style = $window->get_style;

    	my $vbox = Gtk::VBox->new(FALSE, 1);
	$vbox->border_width(10);
	$window->add($vbox);
	$vbox->show;

	@{$pixmaps->{'logo'}} = ();
	open F, 'mikmod.xpm' or die $!;
	while(<F>) {
		chomp;
		next if m#/* XPM */#;
		next if m#(?:static )?char#;
		push @{$pixmaps->{'logo'}}, $_;
	}
	close F;

	# Draw the logo..
	my $pixmap = Gtk::Gdk::Pixmap->create_from_xpm(
		$window->window,$mask, 'mikmod.xpm'
	);

	my $pixmapwid = Gtk::Pixmap->new($pixmap, $mask);
	$pixmapwid->show;
	$vbox->pack_start($pixmapwid, TRUE, TRUE, 0);

	my $pbar = Gtk::ProgressBar->new;
	$pbar->set_usize(200,20);
	$vbox->pack_start($pbar,1,1,0);
	$pbar->show;
		
	#$progress_timer = Gtk->timeout_add(100, \&progress_timeout, $pbar);

	####################################
	# label for currently open file path 
	$filelabel = Gtk::Label->new('');
	$vbox->pack_start($filelabel, FALSE, FALSE, 0);
	$filelabel->show;

	my $separator = Gtk::HSeparator->new;
	$vbox->pack_start($separator, FALSE, TRUE, 1);
    	$separator->show;

	###################################
	# label for currently open song and error/status messages 
	$songlabel = Gtk::Label->new('');
	$vbox->pack_start($songlabel, FALSE, FALSE, 0);
	$songlabel->show;

	# progress bar

	###################################
	# buttons will go in here 
	my $buttonbox = Gtk::HBox->new(FALSE, 1);
	$vbox->add($buttonbox);
	$buttonbox->show;

	for my $type (qw(exit back play pause stop forward open)) {
		make_button($window, $pixmaps->{$type}, $subs->{$type},  $buttonbox);
	}

	$window->show;
}

sub make_button ($$$$) {
	my ($window,$xpm,$func,$box) = @_;
	my $mask;

	my $pixmap = Gtk::Gdk::Pixmap->create_from_xpm_d($window->window,$mask,@$xpm);
	my $pixmapwid = Gtk::Pixmap->new($pixmap, $mask);
	$pixmapwid->show;

	my $button = Gtk::Button->new;
	$button->add($pixmapwid);
	$button->signal_connect('clicked' => $func);

	$box->pack_start($button, 1, 1, 0);
	$button->show;
}

sub _open_fw {
	my $fw = Gtk::FileSelection->new('Song Selection');

	$fw->signal_connect('destroy', \&destroy_window, \$fw);

	$fw->ok_button->signal_connect('clicked', sub {
		_open($fw->get_filename);
		$fw->destroy;
	}, $fw);

	$fw->cancel_button->signal_connect('clicked', sub { $fw->destroy });
	$fw->show;
}

sub update_info ($$) {
	my ($fname,$mfile) = @_;

	if ($song_loaded) {
		$fname =~ s#^\S+/##;
		$filelabel->set($fname);
	} else {
		$filelabel->set($url);
	}

	$songlabel->set($mfile);
}

sub progress_update { 1 }

sub progress_timeout {
	my $progressbar = shift;
	my $new_val = $progressbar->get_current_percentage;
	$new_val += 0.02;
	$new_val  = 0.0 if $new_val >= 1.0;
	$progressbar->update($new_val);
	return 1;
}

sub destroy_window ($$$) {
	my ($widget, $wref, $wref2) = @_;
	$$wref = undef;
	$wref2 = undef if defined $wref2;
        return 0;
}

##################
# Sound functions
sub _init {
	MikMod_RegisterAllLoaders();
        MikMod_RegisterAllDrivers();

	if (MikMod_Init()) {
		printf STDERR "Could not initialize sound, reason: %s\n",
			MikMod_strerror();
		_done();
	}
}

sub _play {
	print "In _play()\n";
	if ($song_loaded) {

		if ($song_playing && $song_paused) {
			_pause();
		} elsif ($song_playing) {
			Player_SetPosition(0);
		} else {
			$song_playing = 1;
			Player_Start($songfile);	
			Player_SetPosition(0);
		}

		$child = forksub { _update() }

	} else {

		$song_playing = 0; 
		update_info($songpath, 'Open song first.');
		$song_loaded = 0;
		_open_fw($songpath);
	}
}

sub _update {
	my $paused = $song_paused->fetch;

	while (Player_Active() && $song_playing && !$paused) {
		$player_busy = 1;
		MikMod_Update();
		progress_update();
		$player_busy = 0;
		usleep(10000);
	}
	#$song_playing = 0;
	#Player_Stop();
}

sub _stop {
	print "In _stop()\n";
	$song_paused->store(0);
	$song_playing = 0;
	usleep(1) while $player_busy;
	Player_Stop();
	# pthread_join(decode_thread, NULL);
	kill 9, $child;
	waitpid $child, 0;
}

#sub _pause { kill 'HUP', $child }
sub _pause {
	if ($song_paused->fetch) {
		$song_paused->store(0);
	} else {
		$song_paused->store(1);
	}

	print "In _pause(): ", $song_paused->fetch, "\n";
	Player_TogglePause();
}

sub _back { kill 'USR1', $child }
sub _real_back {
	$song_paused->store(0);
	Player_PrevPosition();
}

sub _forward { kill 'USR1', $child }
sub _real_forward {
	$song_paused->store(0);
	Player_NextPosition();
}

sub _open ($) {
	my $file = shift;
	print "In _open : $file\n";

	$song_playing = 0;
	$song_paused->store(0);

	if ($song_loaded) {
		$song_loaded = 0;

		while ($player_busy) {
			usleep(1);
		}
		Player_Free($songfile);
	}

	$songfile = Player_Load($file, 64, 0);
	
	if (!defined $songfile or !$songfile) {
		$song_loaded = 0;
		update_info($file, 'Error loading file.');

	} else {
		$song_loaded = 1;
		$songpath    = $file;
		update_info($file, Player_LoadTitle($file));
		_play();
	}
	chdir $file;
}

sub _exit {
	print "In _exit()\n";
	_stop() if $song_playing;
	MikMod_DisableOutput();
	MikMod_Exit();
	Gtk->main_quit;
	exit 1;
}
