package Guardian::Logger;
use strict;
use warnings;

use Exporter qw(import);

our @EXPORT_OK = qw(New Log GetLogLevels);

use Sys::Syslog qw(:DEFAULT setlogsock);
use POSIX qw(strftime);

# Hash which stores all supported log levels and their priority.
my %loglevels = (
	'off' => '0',
	'err' => '1',
	'info' => '1',
	'debug' => '2',
);

# This hash contains the supported log facilities and their corresponding subroutines.
my %logfacilities = (
	"console" => \&LogFacilityConsole,
	"file" => \&LogFacilityFile,
	"syslog" => \&LogFacilitySyslog,
);


#
## The "Init" (Logger) function.
#
## This function is responsible to initialize the Logger as a class based object.
## It has to be called once before logging can be done.
#
## The following arguments must be passed, when initializing a new Logger:
## "LogLevel" and "LogFacility" with valid values from above.
#
sub Init (%) {
	my ( $class, %args ) = @_;
	my $self = \%args;

	# Fail, if some critical arguments are missing.
	unless ((exists($self->{LogLevel})) && (exists($self->{LogFacility}))) {
		die "Could not initialize the Logger: Too less arguments are given.\n";
	}

	# Use bless to make "$self" to an object of class "$class".
	bless($self, $class);

	# Return the class object.
	return $self;
}

#
## The main "Log" function.
#
## This function is used to handle the messages which are generated on various 
## points in the main programm or its modules. Those messages will contain usefull
## information or deeper details about errors.
#
## The Log function takes care about the configured loglevel and transmitts the
## log messages to the configured log facility.
#
sub Log ($$) {
	my $self = shift;
	my ($level, $message) = @_;

	# Check if we got an invalid loglevel.
	unless(exists($loglevels{$level})) {
                &Log("err", "The logger has been called with an invalid loglevel ($level)!\n");
                return;
        }

	# Get value for the current used loglevel.
	my $current_level = $loglevels{$self->{LogLevel}};

	# Get value for the required loglevel.
	my $required_level = $loglevels{$level};

	# Compare the current and required level to determine,
	# if the message should be handled.
	if ($current_level >= $required_level) {
		# Get the facility, which should be used.
		my $use_facility = $self->{LogFacility};

		# Transmit log message to the correct log facility.
		$logfacilities{$use_facility}->($self, $level, $message);
	}
}

#
## GetLogLevels function.
#
## This really simple function just returns the hash which
## contains all supported log levels.
#
sub GetLogLevels () {
	# Nothing to do, just return the loglevels hash.
	return %loglevels;
}

#
## LogFacilityConsole function.
#
## This is a very simple log facility which just prints the given log
## message to STDOUT.
#
sub LogFacilityConsole ($$) {
	my $self = shift;
	my ($type, $message) = @_;

	# Get current date and time and format like: "Jan 01 00:00:01"
	my $now = strftime "%b %e %H:%M:%S", localtime();

	# Print message on STDOUT.
	print STDOUT "$now \[$type\] $message\n";
}

#
## LogFacilitySyslog function.
#
## This log facility sends a given log message to the system log service (syslog).
#
sub LogFacilitySyslog ($$) {
	my $self = shift;
	my ($type, $message) = @_;

	# The syslog function works best with an array based input,
	# so generate one before passing the message details to syslog.
	my @syslog = ("$type", "<$type> $message");

	# Establish the connection to the syslog service.
	openlog('guardian', 'cons,pid', 'user');

	# Send the log message.
	syslog(@syslog);

	# Close the log handle.
	closelog();
}

#
## LogFacilityFile function.
#
## This log facility will write any given log messages to a specified log file.
#
sub LogFacilityFile ($$) {
	my $self = shift;
	my ($type, $message) = @_;

	# Get current date and time and format like: "Jan 01 00:00:01"
	my $now = strftime "%b %e %H:%M:%S", localtime();

	# Open the logfile for writing.
	open(LOGFILE, '>>', $self->{LogFile}) or die "Could not write to $self->{LogFile}: $!\n";

	# Write log message to file.
	print LOGFILE "$now \[$type\] $message\n";

	# Close filehandle.
	close(FILE);
}

1;
