#!/usr/bin/perl -w

# Copyright 2011, 2012, 2013, 2014, 2019, 2020 Kevin Ryde

# This file is part of Math-Image.
#
# Math-Image is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by the
# Free Software Foundation; either version 3, or (at your option) any later
# version.
#
# Math-Image is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
# or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
# for more details.
#
# You should have received a copy of the GNU General Public License along
# with Math-Image.  If not, see <http://www.gnu.org/licenses/>.

package App::MathImage::Tk::Perl::WeakAfter;
use 5.008;
use strict;
use Tk;
use Scalar::Util;

our $VERSION = 115;

# uncomment this to run the ### lines
#use Smart::Comments;


sub new {
  my ($class) = @_;
  return bless { }, $class;

  #   # after, repeat, idle
  #   sub new {
  #     my ($class, $type, $widget, $ms, $repeat) = @_;
  #     my $self = bless { widget => $widget }, $class;
  #     Scalar::Util::weaken($self->{'widget'});
  #     my $method = ($ms eq 'idle' ? 'afterIdle'
  #                   : ($repeat||'') eq 'repeat' ? 'repeat' : 'after');
  #     # $self->{'id'} = $widget->$method($ms,$callback,$type);
  #     return $self;
  #   }
}
sub idle {
  my ($self, $widget, $method, @args) = @_;
  if ($self->type ne 'idle') {
    $self->cancel;
  }
  $self->{'widget'} = $widget;
  Scalar::Util::weaken($self->{'widget'});
  $self->{'method'} = $method;
  $self->{'args'} = \@args;
  Scalar::Util::weaken(my $weak_self = $self);
  $self->{'id'} = $widget->afterIdle(\&_do_once, \$weak_self);
}
sub _do_once {
  my ($ref_weak_self) = @_;
  ### WeakAfter _do_once(): map {"$_"} @_

  my $self = $$ref_weak_self || return;
  delete $self->{'id'};

  my $widget = $self->{'widget'} || return;
  my $method = $self->{'method'};
  $widget->$method (@{$self->{'args'}});
}

sub after {
  my ($self, $widget, $ms, $method, @args) = @_;
  $self->cancel;
  $self->{'widget'} = $widget;
  Scalar::Util::weaken($self->{'widget'});
  $self->{'method'} = $method;
  $self->{'args'} = \@args;
  Scalar::Util::weaken(my $weak_self = $self);
  $self->{'id'} = $widget->after($ms, \&_do_once, \$weak_self);
}

sub repeat {
  my ($self, $widget, $ms, $method, @args) = @_;
  $self->cancel;
  $self->{'widget'} = $widget;
  Scalar::Util::weaken($self->{'widget'});
  $self->{'method'} = $method;
  $self->{'args'} = \@args;
  Scalar::Util::weaken(my $weak_self = $self);
  $self->{'id'} = $widget->repeat($ms, \&_do_repeat, \$weak_self);
}
sub _do_repeat {
  my ($ref_weak_self) = @_;
  ### WeakAfter _do_repeat(): map {"$_"} @_

  my $self = $$ref_weak_self || return;
  my $widget = $self->{'widget'} || return;
  my $method = $self->{'method'};
  $widget->$method (@{$self->{'args'}});
}
sub time {
  my ($self, $ms) = @_;
  if (my $id = $self->{'id'}) {
    if ($ms == 0) {
      $self->cancel;
    } else {
      $id->time($ms);
    }
  } else {
    return;
  }
}

sub DESTROY {
  my ($self) = @_;
  $self->cancel;
}
sub cancel {
  my ($self) = @_;
  ### WeakAfter cancel() ...
  if (my $id = delete $self->{'id'}) {
    $id->cancel;
  }
}
sub info {
  my ($self) = @_;
  if (my $widget = $self->{'widget'}) {
    if (my $id = $self->{'id'}) {
      return $widget->afterInfo($id);
    }
  }
  return;
}
sub type {
  my ($self) = @_;
  my $type = '';
  if (my $widget = $self->{'widget'}) {
    if (my $id = $self->{'id'}) {
       (undef,$type) = $widget->afterInfo($id);
    }
  }
  return $type;
}

1;
__END__

# =for stopwords Ryde MathImage
# 
# =head1 NAME
# 
# App::MathImage::Tk::Perl::WeakAfter -- object for an "after" or "idle"
# 
# =head1 SYNOPSIS
# 
#  use App::MathImage::Tk::Perl::WeakAfter;
#  my $ao = App::MathImage::Tk::Perl::WeakAfter->new;
#  $ao->after ($widget,
#              20,          # milliseconds
#              'callback',  # callback method or func
#              123);        # args
#  # calls $widget->callback(123)
# 
# =head1 DESCRIPTION
# 
# I<In progress ...>
# 
# This is an object-oriented approach to a one-shot "after" timer or "idle"
# callback.  If a WeakAfter object is discarded then its timer or idle is
# cancelled.
# 
# =head1 FUNCTIONS
# 
# =over 4
# 
# =item C<$ao = App::MathImage::Tk::Perl::WeakAfter-E<gt>new (key=E<gt>value,...)>
# 
# Create and return a new WeakAfter object.
# 
# =item C<$ao-E<gt>after($widget, $milliseconds, $callback, $arg...)>
# 
# =item C<$ao-E<gt>idle($widget, $callback, $arg...)>
# 
# Setup an after time or idle callback in C<$ao>.  C<after()> sets up a timer
# of the given C<$milliseconds>.  C<idle()> sets up for when main loop is
# idle.  In both cases a callback is made, just once,
# 
#     $widget->$callback($arg...)
# 
# =item C<$str = $ao-E<gt>cancel()>
# 
# Cancel any after or idle callback pending in C<$ao>.  If there's no callback
# pending then do nothing.
# 
# A callback is automatically cancelled if C<$ao> or the target C<$widget> is
# destroyed.
# 
# =item C<$str = $ao-E<gt>type()>
# 
# Return the type of callback, either string "after" or "idle".  If there's no
# callback active in C<$ao> then return C<undef>.
# 
# =back
# 
# =head1 SEE ALSO
# 
# L<Tk::after>,
# L<Tk::callbacks>
# 
# =head1 HOME PAGE
# 
# L<http://user42.tuxfamily.org/math-image/index.html>
# 
# =head1 LICENSE
# 
# Copyright 2011, 2012, 2013, 2014, 2019, 2020 Kevin Ryde
# 
# Math-Image is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the Free
# Software Foundation; either version 3, or (at your option) any later
# version.
# 
# Math-Image is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
# more details.
# 
# You should have received a copy of the GNU General Public License along with
# Math-Image.  If not, see <http://www.gnu.org/licenses/>.
# 
# =cut
