package RT::Condition::UntouchedInBusinessHours;
require RT::Condition::Generic;

use strict;
use Time::Local;
use vars qw/@ISA/;

@ISA = qw(RT::Condition::Generic);

=head1 NAME

L<RT::Condition::UntouchedInBusinessHours> - Check for untouched Tickets within business hours

=head1 SYNOPSIS

=head2 CLI

  rt-crontool 
	--search RT::Search::ModuleName 
	--search-arg "The Search Argument" 
	--condition RT::Condition::UntouchedInBusinessHours 
	--condition-arg "The Condition Argument"
	--action RT::Action:ActionModule
	--template 'Template Name or ID'

=head1 DESCRIPTION

L<RT::Condition::UntouchedInBusinessHours> is a RT Condition which will check 
for untouched Tickets within business hours.
Untouched means in this case really untouched from time of ticket creation.

=head1 CONFIGURATION

=head2 Condition Argument

The L<RT::Condition::UntouchedInBusinessHours> need exactly 4 arguments to work.
Each part of the argument is separated by colons.

	--condition RT::Condition::UntouchedInBusinessHours 
	--condition-arg 1:7:15:0

	1 is the time in hours for escalation
	7 is the start hour of the working day
	15 is the end of of the working day
	0 is a 5 day week from monday to friday
	1 is a 7 day week from monday to sunday


=head2 Example cron call

	rt-crontool 
		--search RT::Search::FromSQL 
		--search-arg "Queue = 'General' AND ( Status = 'new' ) AND Owner = 'Nobody'" 
		--condition RT::Condition::UntouchedInBusinessHours 
		--condition-arg 1:7:15:0 
		--action RT::Action::RecordComment 
		--template 'Unowned tickets'

=head1 Limitations

At this moment only week/weekends from mo-fr/sa-su are supported. If you have different scenario 
(like arabic countries) you have to change inside the source code ($wd =~ m/(Sun|Sat)/io)

Also no public holidays are excluded.

=head1 NOTES

=head2 Reporting

Send reports to L</AUTHOR> or to the RT mailing lists.

=head2 AUTHOR

    Torsten Brumm <torsten.brumm@googlemail.com>

=head2 COPYRIGHT

This program is free software; you can redistribute
it and/or modify it under the same terms as Perl itself.

The full text of the license can be found in the
Perl distribution.

=cut

my ($est, $tc, $bsh, $beh, $te, $we);
my ($sec,$min,$hour,$mday,$mon,$year,$wd);
my $ActualDate;
my $ticketObj;
my $tickid;
my $EscalationDate;

sub IsApplicable {
	my $self = shift;
	$ticketObj = $self->TicketObj;
	$tickid = $ticketObj->Id;
	$tc = $ticketObj->CreatedObj->Unix;
	($est, $bsh, $beh, $we) = split(/:/, $self->Argument);
	$ActualDate = local_actual_date_sec(RT::Date->new($RT::SystemUser));
	$EscalationDate = escalate(normalize($tc, $bsh, $beh, $we), $bsh, $beh, $est, $we);
	if ( $EscalationDate <= $ActualDate ) {
		return 1;
	}
	return undef;
}

sub local_actual_date_sec {
        my $self = shift;
        $self->SetToNow;
        my $Ausgabe_local_actual_date_sec = $self->Unix;
        return ($self->Unix);
}

sub escalate {
	my ($tc, $bs, $be, $et, $we) = @_;
	my ($rs, $es);
	my $bar = 60*60;
	$rs = $es = $et*$bar;
	my $elapsed = 0;
	my ($sec,$min,$hour,$mday,$mon,$year,$wd) = fromepoch($tc);
	my $beepo = timegm(0,0,$be,$mday,$mon,$year);
	my ($besec,$bemin,$behour,$bemday,$bemon,$beyear,$bewd) = fromepoch($beepo);
	my $foo = $beepo-$tc;
	if ($es < $foo) { # escalate on the same day within BH
		return $tc+$es;
	}

	if ($hour < $behour) {
		$elapsed += $foo;
		$rs -= $foo;
		($sec,$min,$hour,$mday,$mon,$year,$wd) = fromepoch($tc+86400);
		$tc = timegm(0,0,$bs,$mday,$mon,$year);
		$tc = normalize($tc, $bs, $be, $we);
		($sec,$min,$hour,$mday,$mon,$year,$wd) = fromepoch($tc);
	}

	while(int($rs / ($bar*($be -$bs)))) {
		$elapsed += ($bar * ($be -$bs));
		$rs -= ($bar * ($be - $bs));
		($sec,$min,$hour,$mday,$mon,$year,$wd) = fromepoch($tc+86400);
		$tc = timegm(0,0,$bs,$mday,$mon,$year);
		$tc = normalize($tc, $bs, $be, $we);
		($sec,$min,$hour,$mday,$mon,$year,$wd) = fromepoch($tc);
	}

	$tc = normalize($tc+$rs, $bs, $be);
	return $tc;
}

sub normalize {
	my ($time, $bs, $be, $we) = @_;
	my ($sec,$min,$hour,$mday,$mon,$year,$wd) = fromepoch($time);
	my $bens = timegm(0,0,$bs,$mday,$mon,$year);
	my $bend = timegm(0,0,$be,$mday,$mon,$year);
	if ($time >= $bend) {
		($sec,$min,$hour,$mday,$mon,$year,$wd) = fromepoch($time+86400);
		$time = $bens = timegm(0,0,$bs,$mday,$mon,$year);
		$bend = timegm(0,0,$be,$mday,$mon,$year);
	}
	if ($time < $bens) {
		$time = timegm(0,0,$bs,$mday,$mon,$year);
	}
	if (! $we) {
		while ($wd =~ m/(Sun|Sat)/io) {
			($sec,$min,$hour,$mday,$mon,$year,$wd) = fromepoch($time + 86400);
			$time = timegm(0,0,$bs,$mday,$mon,$year);
		}
	}
	return $time;
}

sub fromepoch {
	my ($t) = @_;
	my @time = gmtime($t);
	my $ss = ($time[0]<10) ? "0".$time[0] : $time[0];
	my $mm = ($time[1]<10) ? "0".$time[1] : $time[1];
	my $hh = ($time[2]<10) ? "0".$time[2] : $time[2];
	my $day = $time[3];
	my $month = ($time[4] < 10) ? "0".($time[4]) : $time[4];
	my $wday = ("Sun","Mon","Tue","Wed","Thu","Fri","Sat")[$time[6]];
	my $year = $time[5] + 1900;
	my $offset = timelocal(localtime) - timelocal(gmtime);
	my $sign ="+";
	if ($offset < 0) {
		$sign ="-";   
		$offset *= -1;
	}
	my $offseth = int($offset/3600);
	my $offsetm = int(($offset - $offseth*3600)/60);
	my $tz = sprintf ("%s%0.2d%0.2d", $sign, $offseth, $offsetm);
	return ($ss, $mm, $hh, $day, $month, $year, $wday);
}

eval "require RT::Condition::UntouchedInBusinessHours_Vendor";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/UntouchedInBusinessHours_Vendor.pm});
eval "require RT::Condition::UntouchedInBusinessHours_Local";
die $@ if ($@ && $@ !~ qr{^Can't locate RT/Condition/UntouchedInBusinessHours_Local.pm});

1;

