# $Id$
# This script demonstrates some of the functionality of
# ../lib/Bio/CDAT/VOS/CDAT.pm and ../lib/Bio/CDAT/Root.pm
use lib '../lib';

# i.e. ../lib/Bio/CDAT/VOS/CDAT.pm
use Bio::CDAT::VOS::CDAT;

# I use Bio::NEXUS here to indicate that the architecture
# is hackish enough to intercept calls on generic objects
# that need not be adapted to the architecture (other than
# that they don't use AUTOLOAD)
use Bio::NEXUS::Tree;
use Bio::NEXUS::CharactersBlock;

my $cdat   = new Bio::CDAT::VOS::CDAT;
my $tree   = new Bio::NEXUS::Tree;
my $matrix = new Bio::NEXUS::CharactersBlock;

# Adding objects to the cdat object involves 
# generating unique IDs for the contained objects.
# These IDs are returned by the add call.
# Starting from an ID, you can retrieve the object
# associated with it using $cdat->get_object_by_id( $id ),
# and vice versa using $cdat->get_id_for_object( $obj )
my $matrix_id = $cdat->add_matrix( $matrix );
my $tree_id   = $cdat->add_forest( $tree );

# Adding a listener involves defining, by ID, which
# object is listening, which object is being observed,
# and a handler that is executed when methods are 
# called on the object.
$cdat->add_listener( 
    'listener'   => $matrix_id,
    'observable' => $tree_id,
    'handler'    => sub { 
        # Notice arguments passed to handler: 
        # $_[0] is the listener object
        # $_[1] is the observable object
        # $_[2] is the name of the method that was called
        # $_[3] .. $_[$#_] any additional arguments passed
        # to the call
        my ( $self, $observable, $method, @args ) = @_;
        my $msg = <<"HERE_DOC_WITH_SUBST";


  ========================== EVENT HANDLER TRIGGERED ==========================
  
    INVOCANT: '$observable'
      METHOD: '$method'
    LISTENER: '$self'
        ARGS: '@args' 
        
  =============================================================================       
HERE_DOC_WITH_SUBST
        
        # The handler is currently called every time a public method is called
        # on the observable object. The third argument to the handler gives the
        # the method name. This can be used to filter responses depending on 
        # what was called on the observable.
        if ( $method eq 'set_name' ) {
            
            # The info method is implemented in Bio::CDAT::Root, for
            # logging purposes.
            $cdat->info($msg); 
            
            # Here, the listener ($matrix) responds to the call on the observable
            # ($tree), and changes its character labels.
            $self->set_charlabels( [ "Listener of: '@args'" ] ); 
        }     
    },
);

# Will trigger handler
$tree->set_name( 'Untitled_tree' ); 

# Prints "Listener of: 'Untitled_tree'"
print @{ $matrix->get_charlabels }, "\n";

# Raise verbosity level, so info messages are printed
$Bio::CDAT::Root::LOGLEVEL++;

# Will trigger handler, print info
$tree->set_name( 'New Bio::CDAT tree' );

# Lower verbosity level, only warning and fatal are printed
$Bio::CDAT::Root::LOGLEVEL--;

# Prints "Listener of: 'New Bio::CDAT tree'"
print @{ $matrix->get_charlabels }, "\n"; 

# Set verbosity highest, other constants are:
#        Bio::CDAT::Root::INFO
#        Bio::CDAT::Root::WARNING
#        Bio::CDAT::Root::FATAL
$Bio::CDAT::Root::LOGLEVEL = Bio::CDAT::Root::DEBUG;

# Notice debugging messages
$tree->get_name();

# Make quieter
$Bio::CDAT::Root::LOGLEVEL = Bio::CDAT::Root::WARNING;

# Exceptions:
eval {
    $cdat->throw( 'Bio::CDAT::Exceptions::Foo::Bar', 'An error message.' )
};
if ( $@->isa('Bio::CDAT::Exceptions::Foo::Bar') ) {
    print $@->{'message'};
}