#  Copyright (c) 1997-2006
#  Ewgenij Gawrilow, Michael Joswig (Technische Universitaet Berlin, Germany)
#  http://www.math.tu-berlin.de/polymake,  mailto:polymake@math.tu-berlin.de
#
#  This program 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 2, or (at your option) any
#  later version: http://www.gnu.org/licenses/gpl.txt.
#
#  This program 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.
#-----------------------------------------------------------------------------
#  $Project: polymake $$Id: Scope.pm,v 1.11 2006/02/01 21:04:04 gawrilow Exp $

use strict;
use namespaces;

package Poly::Scope;
use Poly::Ext;

use Struct (
   '$local_marker',
   '%deferred',		# 'arbitrary key' => Action:
	    		#   \Object or \&sub or [ \&sub, args ... ] or [ \Object, "method", args ... ]
			# For a naked Object, its method 'proceed' is called
   '@proceeded',	# holds anything returned by performed deferred actions (to be deleted during unwind)
   '%cleanup',		# the same, but during unwind.  The default method is 'finish'.  Key must be a reference
);

sub perform {
   my ($key, $action, $method)=@_;
   if (is_object($action)) {
      $action->$method($key);
   } elsif (is_code($action)) {
      $action->($key);
   } elsif (is_ARRAY($action)) {
      $key->$method(@$action);
   } else {
      err_print( "Poly::Scope - don't know what to do with a '$_[2]' action: $key => ", ref($action) || "'$action'" );
      ()
   }
}

sub perform_deferred {
   my ($self)=@_;
   my ($key, $action);
   while (my ($key, $action)=each %{$self->deferred}) {
      push @{$self->proceeded}, perform($key, $action, "proceed");
   }
   %{$self->deferred}=();
}

sub DESTROY {
   my ($self)=@_;
   while (my ($key, $action)=each %{$self->cleanup}) {
      local $@;
      eval { perform($key, $action, "cleanup") };
      err_print($@) if $@;
   }
   unwind($self->local_marker);
}

my $unique_key="a";

sub add_unique {
   my (undef, $key, $action, $is_empty)=@_;
   if (defined($_[0])) {
      if (!defined($is_empty) || !$is_empty->($_[0])) {
	 ++$unique_key while exists $self->deferred->{"$key#$unique_key"};
	 $self->deferred->{"$key#$unique_key"}=$_[0];
      }
   }
   $_[0]=$action;
}

sub add_deferred_action {
   my $self=shift;
   add_unique($self->{$_[0]}, @_);
}


1
