#  Copyright (c) 1997-2004
#  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.10 2004/10/14 16:12:36 gawrilow Exp $

use strict;
use namespaces;

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

my @repeating;

use Struct (
   '$local_marker',
   '%at_end',		# 'arbitrary key' => Action:
	    		#   \Object or \&sub or [ \&sub, args ... ] or [ \Object, "method", args ... ]
			# For a naked Object, its method 'finish' is called
   '%repeating',	# the same, but each iteration
   '%cleanup',		# the same, but after all at_end actions and even during unwind
);

sub new {
   my $self=&_new;
   $self->repeating=pop @repeating if @repeating;
   $self;
}

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

sub end {
   my ($self)=@_;
   my ($key, $action);
   while (($key, $action)=each %{$self->at_end}) {
      eval { perform($key, $action, "finish") };
      if ($@) {
	 err_print($@); $@="";
      }
   }

   while (($key, $action)=each %{$self->repeating}) {
      eval { perform($key, $action, "proceed") };
      if ($@) {
	 delete $self->repeating->{$key};
	 err_print($@); $@="";
      }
   }
}

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

   push @repeating, $self->repeating;
}

END {
   pop @repeating while @repeating;
}

1
