package X10::SchedEvent;

use Time::ParseDate;
use POSIX;

use strict;

use Astro::SunTime;

sub new
{
   my $type = shift;

   my $self = bless { @_ }, $type;

   unless ($self->{macro} && $self->{macro}->isa('X10::Macro'))
   {
      warn "No macro sent to create SchedEvent";
      return undef;
   }

   $self->{verbose} = 1 if $self->{debug};

   $self->{next_time} = int(time);
   $self->reschedule;
   $self->{last_time} = 0;

   return $self;
}



sub run
{
   my $self = shift;

   $self->{logger}->('info', "Running %s",
	$self->{description} || 'unnamed event',
	) if $self->{verbose};

   $self->{macro}->run;
}

sub reschedule
{
   my $self = shift;

   $self->{last_time} = $self->{next_time} || 0;

   my $current = $self->{last_time} || time;
   my $next;

   if (!exists $self->{repeat_type} || $self->{repeat_type} eq 'none')
   {
      return 0;
   }
   elsif ($self->{repeat_type} eq 'day')
   {
      my $new = parsedate(sprintf("today %s", $self->time('today')));

      if ($new <= time || $new <= $current)
      {
         $new = parsedate(sprintf("tomorrow %s", $self->time('tomorrow')));
      }

      # check DOW loop here...
      if (exists $self->{dowmask} && $self->{dowmask} > 0)
      {
         my @newarray = localtime($new);

         while ( ! ( (1 << $newarray[6]) & $self->{dowmask} ) )
         {
            $self->{logger}->('info', "Skipping %s...", strftime("%a %b %e %Y", @newarray))
		if $self->{debug};
            $newarray[3] += 1;		# add one day

            @newarray = localtime(mktime(@newarray));	# normalize

            # find new time on that day...

            my $datestr = strftime("%a %b %e %Y", @newarray);

            my $time = $self->time($datestr);

            $new = parsedate(sprintf("%s %s", $time, $datestr), WHOLE => 1);
            @newarray = localtime($new);
         }

      }

      $next = $new;
   }
   else
   {
      $self->{logger}->('info', "Unsupported repeat type: %s", $self->{repeat_type});
   }


   $self->{next_time} = $next;

   return 1;
}

sub next_time
{
   my $self = shift;

   return $self->{next_time};
}

sub controller
{
   my $self = shift;

   if (@_)
   {
      $self->{macro}->controller(shift);
   }

   return $self->{macro}->controller;
}

sub description
{
   my $self = shift;

   if (@_)
   {
      $self->{description} = shift;
   }

   return $self->{description};
}

sub time
{
   my $self = shift;
   my $date = shift;

   # offsets assumed not to force time across day boundaries...

   my $time;
   my $sign;
   my $offset;

   if ($self->{time} =~ /^\d?\d:\d\d$/)
   {
      $time = $self->{time};
      $offset = 0;
   }
   elsif ($self->{time} =~ /^sunrise(\s*([+-])\s*(\d+))?$/)
   {
      $sign = ($2 eq '-') ? -1 : 1;
      $offset = $3 || 0;
      $time = sun_time(type => 'rise', date => $date,
	latitude => $self->{latitude},
	longitude => $self->{longitude},
	);
   }
   elsif ($self->{time} =~ /^sunset(\s*([+-])\s*(\d+))?$/)
   {
      $sign = ($2 eq '-') ? -1 : 1;
      $offset = $3 || 0;
      $time = sun_time(type => 'set', date => $date,
	latitude => $self->{latitude},
	longitude => $self->{longitude},
	);
   }
   else
   {
      $self->{logger}->('info', "Unknown time string: %s", $self->{time});
      return undef;
   }

   return $time unless $offset;

   my ($hour, $minute) = $time =~ /^(\d?\d):(\d\d)$/;

   $minute += $sign * $offset;

   while ($minute >= 60)
   {
      $minute -= 60;
      $hour++;
   }

   while ($minute < 0)
   {
      $minute += 60;
      $hour--;
   }

   return sprintf("%s:%02s", $hour, $minute);
}


1;

