<%shared>

use Time::ParseDate ();
use POSIX ();

{ # packages scope

no warnings;

package Blinck::RT::Utils;

sub as_simple_date {
  POSIX::strftime ( "%a %b %e", localtime( as_time_tz($_[0]) ) )
}

sub time_as_yyyymmdd_date {
  POSIX::strftime ( "%Y%m%d", localtime( $_[0] ) )
}

sub as_time { scalar  ( Time::ParseDate::parsedate($_[0]) ) }

# FIXME: there seems to be a timezone issue! ZS.
sub as_time_tz { as_time($_[0]) + 2*60*60 }

sub as_friendly_time_left {
  my $dt = POSIX::floor( ( as_time($_[0]) - time() ) / (24*60*60) ) + 1;

  return "tomorrow"    if $dt == 1;
  return "today"       if $dt == 0;
  return "yesterday"    if $dt == -1;
  return "in $dt days" if $dt > 0 and $dt < 7;
  return -$dt . " days ago" if $dt < 0 and $dt > -7;

  #weeks
  $dt = POSIX::floor ( $dt / 7 );
  return "next week"    if $dt == 1;
  return "this week"    if $dt == 0;
  return "last week"    if $dt == -1;
  return "in $dt weeks" if $dt > 0 and  $dt < 4;
  return -$dt . " weeks ago" if $dt < 0 and $dt > -4;

  #months
  $dt = POSIX::floor ( $dt / 4 );
  return "next month"    if $dt == 1;
  return "this month"    if $dt == 0;
  return "last month"    if $dt == -1;
  return "in about $dt months" if $dt > 0 and  $dt < 12;
  return "about " . -$dt . " months ago" if $dt < 0 and $dt > -12;

  #years
  $dt = POSIX::floor ( $dt / 12 );
  return "next year"    if $dt == 1;
  return "this year"    if $dt == 0;
  return "last year"    if $dt == -1;
  return "in about $dt years" if $dt > 0 and  $dt < 100;
  return "about " . -$dt . " years ago" if $dt < 0 and $dt > -100;

  #century
  $dt = POSIX::floor ( $dt / 100 );
  return "next century"    if $dt == 1;
  return "this century"    if $dt == 0;
  return "last century"    if $dt == -1;
  return "in $dt centuries" if $dt > 0;
  return -$dt . " centuries ago" if $dt < 0;

}


sub regex_project { qr/^\s*\[(PROJECT|ONGOING)\]/ }

sub regex_milestone { qr/^\s*\[MILESTONE\]/ }

sub ticket_display_url { '../Ticket/Display.html?id=' }

package Blinck::RT::Milestones;

sub new {
  my $class = shift;
  my $self = bless { milestones => [], @_ }, $class;

  $self->{query} =
    "(Status = 'open' OR Status = 'new' OR Status = 'stalled')"
      ." AND (Subject like '[MILESTONE]%')";

  if ($self->{q}) {
    $self->{query} .= " AND (Queue = '$self->{q}')";
  }
  if ($self->{p}) {
    $self->{query} .= " AND (MemberOf = '$self->{p}')";
  }

  $self
}

sub find_milestones {
  my $self = shift;

  my @mss = ();

  my $ts = new RT::Tickets $HTML::Mason::Commands::session{CurrentUser};
  my ($n, $s) = $ts->FromSQL($self->{query});

  while (my $t = $ts->Next) {
    my $ms = Blinck::RT::Milestone->new($t);
    $ms->find_projects;
    push @mss, $ms;
  }

  $self->{milestones} = \@mss;
}

sub get_milestones { @{$_[0]->{milestones}} }

sub get_milestones_by_due_date {
  sort { Blinck::RT::Utils::as_time($b->due_date) <=> Blinck::RT::Utils::as_time($a->due_date) } $_[0]->get_milestones;
}

package Blinck::RT::Milestone;

sub new {
  my $class = shift;
  my $ticket = shift;

  die 'Usage: new($rt_ticket)' unless ref($ticket) eq 'RT::Ticket';

  my $self = { ticket => $ticket, tasks => [], projects => [] };

  bless $self, $class
}

sub get_ical_dates {
  my $self = shift;

  my $dt  = Blinck::RT::Utils::as_time_tz($self->due_date);
  my $dtt = $dt + 24*60*60; # the next day

  # first and last dates
  ( Blinck::RT::Utils::time_as_yyyymmdd_date($dt),
    Blinck::RT::Utils::time_as_yyyymmdd_date($dtt) )
}

sub description {
  my $self = shift;
  my $out = '';

  $out .= "#" . $self->id . " ; " . $self->name . " ; ";
  $out .= eval { $self->{projects}[0]->name } . " ; ";

  $out
}

sub find_projects {
  my $self = shift;

  my @projects = ();

  my $ts = new RT::Tickets $HTML::Mason::Commands::session{CurrentUser};
  my ($n, $s) = $ts->FromSQL("HasMember='" . $self->id . "'");

  while (my $t = $ts->Next) {
    my $p = Blinck::RT::Project->new;
    $p->set_ticket($t);
    push @projects, $p;
  }

  $self->{projects} = \@projects;
}

sub add_task { push @{$_[0]->{tasks}} , $_[1] }

sub get_tasks {
  my $self = shift;
  @{$self->{tasks}};
}
sub get_tasks_by_priority {
  my $self = shift;
  sort { $b->Priority <=> $a->Priority } $self->get_tasks
}

sub due_date { $_[0]->{ticket}->Due() }

sub name { $_[0]->{ticket}->Subject() }

sub summary_name { $_[0]->name =~ /\[MILESTONE\]\s*(.*)/ }

sub status { $_[0]->{ticket}->Status() }

sub id { $_[0]->{ticket}->Id() }

package Blinck::RT::Project;
use strict;

sub new {
  my $class = shift;
  bless { tasks => [], milestones => [], info => {}, @_ }, $class
}

sub load_by_id {
  my $self = shift;
  my $id = shift;

  $self->{id} = $id;

  my $tickets = new RT::Tickets $HTML::Mason::Commands::session{CurrentUser};
  my ($n, $s) = $tickets->FromSQL("Id='$id'");
  $self->{ticket} = $tickets->Next;
}

sub set_ticket {
  my $self = shift;
  my $ticket = shift;

  $self->{id} = $ticket->Id;
  $self->{ticket} = $ticket;
}

sub find_members {
  my $self = shift;
  my @tasks = ();
  my @milestones = ();

  my $info = {};
  $info->{status_count} = {};
  $info->{milestones} = [];
  $info->{Owners} = {};

  my $tickets = new RT::Tickets $HTML::Mason::Commands::session{CurrentUser};
  my ($n, $s) = $tickets->FromSQL("MemberOf='" . $self->{id} . "'");

  while (my $ticket = $tickets->Next) {
    if ($ticket->Subject =~ Blinck::RT::Utils::regex_milestone()) {
      my $ms = Blinck::RT::Milestone->new($ticket);
      push @milestones, $ms;

      # Find child tickets of milestone
      my $mt = new RT::Tickets $HTML::Mason::Commands::session{CurrentUser};
      my ($n, $s) = $mt->FromSQL("MemberOf='" . $ms->id . "'");
      while (my $t = $mt->Next) {
	$ms->add_task($t);
	# Add it to the tasks list anyway!
        push @tasks, $ticket;
      }
    }
    else {
      push @tasks, $ticket;

      $info->{Owners}{$ticket->OwnerObj->Name}++;
      $info->{status_count}{$ticket->Status}++;
      if ($ticket->Status eq 'open' or $ticket->Status eq 'new') {
	$info->{pending_count}++
      }
      elsif ($ticket->Status eq 'resolved') {
	$info->{done_count}++
      }

    }
  }

  $self->{tasks} = \@tasks;
  $self->{milestones} = \@milestones;
  $self->{info} = $info;
}

sub get_milestones { @{$_[0]->{milestones}} }

sub get_milestones_by_due_date {
  sort { Blinck::RT::Utils::as_time($b->due_date) <=> Blinck::RT::Utils::as_time($a->due_date) } $_[0]->get_milestones;
}

sub get_tasks { @{$_[0]->{tasks}} }

sub get_tasks_by_priority {
  sort { $b->Priority <=> $a->Priority } $_[0]->get_tasks;
}

sub get_details {
  my $self = shift;
  my %data = ();
  my $CustomFields = $self->{ticket}->QueueObj->TicketCustomFields;
  while (my $cf = $CustomFields->Next) {
    my $vals = $self->{ticket}->CustomFieldValues($cf->Id);
    my @out = ();
    while (my $v = $vals->Next) {
      push @out, $v->Content;
    }
    $data{$cf->Name} = join ',', @out;
  }

  \%data
}

sub get_owners { keys %{$_[0]->{info}{Owners}} }
sub get_owner_task_count { $_[0]->{info}{Owners}{$_[1]} }

sub get_tasks_status { keys %{$_[0]->{info}{status_count}} }
sub get_tasks_status_count { $_[0]->{info}{status_count}{$_[1]} }

sub get_completed_percentage {
  my $info = $_[0]->{info};

  my $total = $info->{done_count} + $info->{pending_count};
  return 0 unless $total;

  POSIX::floor( 100 * $info->{done_count} / $total );
}

sub get_summary_issues_as_string {
  my $self = shift;
  my $out = '';

  for my $status ($self->get_tasks_status) {
    $out .= $status . ': ' . $self->get_tasks_status_count($status) . '; ';
  }
  $out .= '(' . $self->get_completed_percentage . '% Completed)';

  $out;
}

sub get_summary_people_as_string {
  my $self = shift;
  my $out = '';

  for my $owner ($self->get_owners) {
    $out .= $owner . '(' . $self->get_owner_task_count($owner) . ') ';
  }

  $out;
}


sub name { $_[0]->{ticket}->Subject }
sub id { $_[0]->{id} }

sub queue { $_[0]->{ticket}->QueueObj->Name }

sub priority { $_[0]->{ticket}->Priority }
sub status { $_[0]->{ticket}->Status }


sub as_uri_ticket { '../Ticket/Display.html?id=' . $_[0]->{id} }



package Blinck::RT::Projects;

sub new {
  my $class = shift;
  my $self = bless { projects => [], orphans => [], @_ }, $class;

  $self->{query_status} =
    "Status = 'open' OR Status = 'new' OR Status = 'stalled'";

  $self->{qname} = "All Projects";
  $self->{qquery} = 'AND (Queue LIKE \'%Projects\')';

  if ($self->{q}) {
    $self->{qname} = $self->{q};
    $self->{qquery} = "AND (Queue = '$self->{q}')";
  }

  $self
}

sub find_projects {
  my $self = shift;

  my @projects = ();

  my $tickets = new RT::Tickets $HTML::Mason::Commands::session{CurrentUser};
  my ($n, $s) = $tickets->FromSQL("($self->{query_status}) AND (Subject like '[PROJECT]' or Subject like '[ONGOING]') $self->{qquery}");

  while (my $ticket = $tickets->Next) {
    my $p = Blinck::RT::Project->new;
    $p->set_ticket($ticket);
    $p->find_members;
    push @projects, $p;
  }

  $self->{projects} = \@projects;
}

sub find_orphans {
  my $self = shift;

  my @orphans = ();

  my $tickets = new RT::Tickets $HTML::Mason::Commands::session{CurrentUser};
  my ($n, $s) = $tickets->FromSQL("($self->{query_status}) AND MemberOf='' $self->{qquery}");
  while (my $ticket = $tickets->Next) {
    next if $ticket->Subject =~ Blinck::RT::Utils::regex_project();
    push @orphans, $ticket;
  }

  $self->{orphans} = \@orphans;
}

sub get_projects { @{$_[0]->{projects}} }

sub get_projects_by_priority {
  sort { $b->priority <=> $a->priority } $_[0]->get_projects;
}

sub get_orphans { @{$_[0]->{orphans}} }


} # end of packages scope

</%shared>
