package Slim::Networking::mDNS;

# $Id: mDNS.pm 19476 2008-05-06 16:23:00Z andy $
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License,
# version 2.

use strict;
use base qw(Class::Data::Inheritable);

use FindBin qw($Bin);
use File::Slurp;
use File::Spec::Functions qw(:ALL);
use Proc::Background;

use Slim::Utils::Log;
use Slim::Utils::Misc;
use Slim::Utils::Prefs;
use Slim::Utils::OSDetect;

{
	my $class = __PACKAGE__;

	for my $accessor (qw(confFile processObj isInitialized)) {

		$class->mk_classdata($accessor);
	}

	$class->isInitialized(0);
}

my $log = logger('network.mdns');

my $prefs = preferences('server');

my %services = ();

sub init {
	my $class = shift;

	if (Slim::Utils::OSDetect::OS() eq 'win') {

		$log->debug("Skipping initialization on Windows.");

		return 0;
	}

	$log->info("Initializing..");

	my $cacheDir = $prefs->get('cachedir');

	if (!-d $cacheDir) {

		$log->error("cachedir [$cacheDir] isn't set or writeable");
		return 0;
	}

	$class->confFile(catfile($cacheDir, 'mDNS.conf'));

	$class->isInitialized(1);

	return 1;
}

sub addService {
	my ($class, $service, $port) = @_;

	if (!$class->isInitialized) {
		return if !$class->init;
	}

	my $name = $prefs->get('mDNSname');

	if (!$name) {

		$log->info("Blank name, skipping service: $service - TXT - $port");

	} else {

		$log->info("Adding service: $name - $service - TXT - $port");

		$services{$service} = [ $name, $port ];
	}
}

sub removeService {
	my ($class, $service) = @_;

	if (!$class->isInitialized) {
		return if !$class->init;
	}

	$log->info("Removing service: $service");

	delete $services{$service};
}

sub startAdvertising {
	my $class = shift;

	if (!$class->isInitialized) {
		return if !$class->init;
	}

	$log->info("Building configuration.");

	my $mDNSBin = Slim::Utils::Misc::findbin('mDNSResponderPosix') || do {

		$log->warn("Couldn't find mDNSResponderPosix binary");
		return;
	};

	if (!$class->isInitialized) {
		return if !$class->init;
	}

	# Remove any existing configs
	$class->stopAdvertising;

	open(CONF, '>', $class->confFile) or do {

		if ( $log->is_error ) {
			$log->error(sprintf("Couldn't open %s for appending: $!", $class->confFile));
		}
		return;
	};

	# Write out the service information
	while (my ($service, $data) = each %services) {

		my ($name, $port) = @$data;

		# mDNSResponderPosix can handle an extra space at the front,
		# but prints an error if on occurs at the end.
		print CONF "\n";
		print CONF "$name\n";
		print CONF "$service\n";
		print CONF "$port\n";
	}

	close(CONF);

	if (-z $class->confFile) {

		$log->info("Config has 0 size - disabling mDNS");
		return;
	}

	my $command = join(' ', (
		$mDNSBin,
		sprintf('-f %s', $class->confFile),
	));

	$log->info("About to run: $command");

	$class->processObj( Proc::Background->new($command) );

	if ( $log->is_info ) {
		$log->info(sprintf("Process is alive: [%d] with pid: [%d]",
			$class->processObj->alive, $class->processObj->pid
		));
	}
}

sub stopAdvertising {
	my $class = shift;

	if (!$class->isInitialized) {
		return if !$class->init;
	}

	$log->info("Shutting down..");

	my $dead = 0;
	my $pid  = undef;

	if ($class->processObj) {

		$pid = $class->processObj->pid;

		if ($pid) {

			$log->info("Found process pid: $pid");

			$class->processObj->die;
		}

		if (!$class->processObj->alive) {
			$dead = 1;

			$class->processObj(undef);
		}
	}

	if ($dead) {

		if ($pid) {
			$log->info("Killed PID: $pid");
		}

		unlink($class->confFile);

	} else {

		$log->info("Didn't kill any PIDs!");
	}

	return $dead;
}

1;

__END__
