#!/usr/bin/perl
#
# Sample D-Bus "server". This shows how various methods and signals can be
# exported in the simplest possible manner.
#
# Author:  Madison Kelly (mkelly@alteeve.ca)
# Updated: 2008/07/14
# Plug:    http://tle-bu.org
#
# Source code released under the GPLv3.
#  - http://www.gnu.org/licenses/gpl-3.0.txt
# Documentation released under Creative Commons
# Attribution-Noncommercial-Share Alike 3.0 License.
#  - http://creativecommons.org/licenses/by-nc-sa/3.0/
#
# This program file is not meant to be fast or event particularly sane. It's
# primary goal is to show as many possible methods and functions available in
# the Net::DBus* modules as possible in a format that is easy to follow for a
# new user. You will want to read up more on the best way to do various things
# one you have your own code working.
#
# The examples here are somewhat based on the example scripts provided by the
# Net::DBus module. Please check the scripts under the 'examples' directory if
# you would like to see the similarities.
#

# Being good little coder, I make things die early and loudly.
use strict;
use warnings;

# Load in the Net::DBus core module.
use Net::DBus;

# Load in the service module used to export methods and signals.
use Net::DBus::Service;

# Now load the reactor module used to create our event loop that will listen
# for client connections.
use Net::DBus::Reactor;


### At this point, you can include more program functions and features, if you
### wish. In our case though, I will get right to the D-Bus stuff. In the real
### world though, you may wish to read in config files, print greetings or
### start logging or ... whatever.


# From here on, this must be a 'package'. This is required as it will be used
# when I create our object further down. Also, this is often (though not need
# be) the same name used on the object path when the 'new' method is called
# below.
### A note on position of this;
# You can define the package name further up, but it MUST be declared before
# the 'Net::DBus::Object' module is loaded.
package DBusExampleService;

# Now that I've defined our package name, I load the 'Net::DBus::Object'
# module. For info on the 'base' syntax, see: http://perldoc.perl.org/base.html
use base qw(Net::DBus::Object);

# Now load the module that will handle exporting our methods and signals onto
# the D-Bus. Here, I decide to define a default interface to be used by my
# methods and signals when I don't explicitly define an interface. This is an
# optional step. Without it though, you will have to specify an interface below
# at all times.
use Net::DBus::Exporter qw(org.tle_bu.DBusExampleInterface);

# This is the constructor mothod.
sub new
{
	my $class=shift;
	my $service=shift;
	# Here, I create the service and make it available at the object path
	# '/org/tle_bu/DBusExampleObject'. Usually this matches the name of the
	# package, but I am keeping it different to make it a bit easier to
	# see the differences in the example files.
	my $self = $class->SUPER::new($service, "/org/tle_bu/DBusExampleObject");
	bless $self, $class;
	
	return $self;
}


# This method will take two integers and a string and, depending on the string,
# do some math on the two integers and return the resulting real number.
sub doMath
{
	my ($self, $num1, $num2, $symbol)=@_;
	my $result=0;
	my $string="";
	
	print "The 'doMath' method was called in 'service_02.pl' asking what: [$num1 $symbol $num2] is.\n";
	if ( $symbol eq "+" )
	{
		# Add the numbers.
		$result=$num1+$num2;
	}
	elsif ( $symbol eq "-" )
	{
		# Subtract the second number from the first.
		$result=$num1-$num2;
	}
	elsif (( $symbol eq "/" ) || ( $symbol eq "÷" ))
	{
		# Divide the first number by the second.
		if ( $num2 == 0 )
		{
			$string="Divide by 0 error!";
		}
		else
		{
			$result=$num1/$num2;
		}
	}
	elsif (( $symbol eq "*" ) || ( $symbol eq "x" ))
	{
		# Multiple the numbers/
		$result=$num1*$num2;
	}
	else
	{
		$string="Unknown math symbol '$symbol'. Unable to process the numbers: '$num1' and '$num2'. Supported math commands are: '+', '-', '/', '÷', '*' and 'x'.\n";
	}
	
	# Return the result.
	return ($result, $string);
}

# Publish this method on the D-Bus. It will accept two signed integers and a
# string and it will return an 'double' IEEE-754 floating point number and a
# string.
dbus_method("doMath", ["int32", "int32", "string"], ["double", "string"]);

# This creates a signal called 'signalMessage' that emits 'strings'. Note that
# this does not have a coresponding subroutine!
dbus_signal("signalMessage", ["string"]);

# Now create the method called 'emitMessage".
sub emitMessage
{
	# This is a method, so 'self' is also passed in first.
	my $self=shift;
	my $string=shift;
	
	# This will print to the terminal that this service was started on that
	# this method has been called.
	print "Method 'emitMessage' called in 'service_02.pl'.\n";
	
	# For clarity sake, I will put my string into a variable. I could just
	# pass this directly, too.
	$string="Hello, World!" unless defined $string;
	
	# This calls the 'Net::DBus' 'emit_signal' method
	$self->emit_signal("signalMessage", "$string");
	
	return "Emitted signal string: $string";
}

# Now, there is a trick to signals. Something needs to trigger it for
# interested clients to ever hear from the signal. So this is a bit artificial,
# but, this is a very simple method that takes no arguments and returns no
# arguments. It simply triggers the signal and returns.
dbus_method("emitMessage", ["string"], ["string"]);


### Now that I've got the methods and signals done for this package, I need
### to make them available on the D-Bus message bus. To do this, I will create
### a new package called 'main', register a service with the 'session' bus,
### publish the methods above under that service and then create a reactor loop
### to listen for calls to the exported methods above.


# This creates a small package called 'main' used to setup the exporting of any
# methods and signals created above.
package main;

# Create a connection to the Freedesktop.org 'session' bus, which is the bus
# accessible by the user invoking this module only.
my $bus=Net::DBus->session();

# Export a service with the given service name. In this example, I will use the
# name 'org.tle-bu.org.SampleService'. Not that this does not need to relate to
# the Interface name or object path name, but it can if you like.
my $service=$bus->export_service("org.tle-bu.SampleService");

# This exports the methods and signals made available in the
# 'DBusExampleService' package above onto the service I just created above.
my $object=DBusExampleService->new($service);

# This starts the reactor loop for the 'main' package. This makes the service
# exported above, and the object registered with the service, available to
# clients. This loop will wait for calls from clients until it catches a
# signal. For now, exit the loop by hitting '<ctrl>+c'.
Net::DBus::Reactor->main->run();

# Finally, should the program ever run to the end, exit cleanly.
exit 0;
