package RFC::Ysplit;
#$Id: Ysplit.pm,v 1.22 2023/11/29 07:19:12 sqko Exp $
#############################################################
#
#
#       RFC::Ysplit
#
# This module defins the behaviour of a special filter device
# An instance of RFC::Ysplit behaves as a selector switch and 
# sends commands to one of two underlying devices based on the
# status of the control device (which must be of type RFC::BaseRelay
# or a class derived from RFC::BaseRelay).
# 
# The underlying devices to be controlled can be of any type 
# derived from RFC::BaseDevice.
#
# To obtain a RFC::Ysplit instance call the constructor:
#
# $id = RFC::Ysplit->new($name,$rig)
#
# RFC::Ysplit does not export any new member functions besides
# those derived from BaseDevice. However if a member function 
# is called which is not derived directly from baseDevice the
# the RFC::Ysplit instance refers the function call to the
# underlying selected device thourgh autoloading.
#
#
# If more than 2 options for selecting devices is desired,
# use the OutputMultiplex filter class instead.
#
#############################################################

use strict;
use warnings;
use RFC::BaseDevice;
use RFC::Header;
use RFC::Cache;
our @ISA = qw(RFC::BaseDevice);
our $AUTOLOAD;

BEGIN {
    use Exporter ();
    our ($VERSION, @ISA, @EXPORT, @EXPORT_OK, %EXPORT_TAGS);
    $VERSION = 1.0;
    %EXPORT_TAGS = (
        'standard'=>[],
                    );
    Exporter::export_ok_tags('standard');
    Exporter::export_tags('standard');
    @ISA = qw(Exporter);
}

sub new()
{
    my $class = shift;
    my $name = shift;
    my $rig = shift;
    my $self = $class->SUPER::new($name,$rig);
    bless $self,$class;
    $self->{'_type'} = 'Y-split_filter_device';
    $self->{'_setup_tags'} = ['class','mode','control_device','device_type','input_device','device_1','device_2','title','show_plot'];
    $self->{'_reload_tags'} = ['class','mode','control_device','device_type','input_device','device_1','device_2'];
    $self->{'_setup_tags_readonly'} = ['mode'];
    $self->{'_desc'} = {
	'class' => 'Type of filter device',
        'mode' => 'Device control mode',
        'device_type' => 'Type of raw device to control',
        'control_device' => 'Relay device for controlling which device is used',
	'input_device' => 'Optional input device. If it exists, commands from the input device is passed on to one of the output devices',
	'device_1' => 'Device selected if control device is off (0)',
	'device_2' => 'Device selected if control device is on (1)',
    };
    $self->{'_default'} = {
	'class' => 'Y-split',
        'device_type' => 'simplechannel',
        'control_device' => '',
        'device_2' => '',
        'device_1' => '',
        'input_device' => '',
        'show_plot'=>'Yes',
    };
    $self->{'_values_class'} = [RFC::Device::list_filter()];
    $self->{'_values_mode'} = ['Automatic'];
    $self->{'_values_device_type'} = ['',$self->rig->list_types_nofilter()];
    $self->{'_section'} = 'filter_'.$self->name;
    foreach my $tag ($self->setup_tags)
    {
        $self->{'_'.$tag} = $self->get_default($tag);
        my $conf = $self->get_conf($tag);
        $self->{'_'.$tag} = $conf if (defined($conf) && $conf =~ /\w/);
    }
    $self->{'_mode'} = 'Automatic';
    $self->{'_class'} = 'Y-split';
    my @list = ();
    foreach ($self->rig->avaliable_devices($self->{'_device_type'}))
    {
        push @list,$_ unless ($_ eq $self->name()); ## Do not include self ##
    }
    $self->{'_values_device_1'} = ['',@list];
    $self->{'_values_device_2'} = ['',@list];
    $self->{'_values_input_device'} = ['',@list];
    @list = ();
    foreach ($self->rig->avaliable_devices('relay'))
    {
        push @list,$_ unless ($_ eq $self->name()); ## Do not include self ##
    }
    $self->{'_values_control_device'} = ['',@list];
    $self->{'_values_read_function'} = ['read'];
    if ($self->{'_device_type'} && $self->{'_device_1'})
    {
        ### get device if it already exist ###
        my $dev = $self->rig->get_device($self->{'_device_type'},$self->{'_device_1'});
        $self->{'_dev_1'} = $dev if ($dev);
	### Explicitly set readonly on underlying devices ###
	$dev->set_readonly() if ($dev);       
    }
    if ($self->{'_device_type'} && $self->{'_device_2'})
    {
        ### get device if it already exist ###
        my $dev = $self->rig->get_device($self->{'_device_type'},$self->{'_device_2'});
        $self->{'_dev_2'} = $dev if ($dev);
	### Explicitly set readonly on underlying devices ###
	$dev->set_readonly() if ($dev);    
    }
    if ($self->{'_control_device'})
    {
        ### get device if it already exist ###
        my $dev = $self->rig->get_device('relay',$self->{'_control_device'});
        $self->{'_dev'} = $dev if ($dev);       
    }
    if ($self->{'_device_type'} && $self->{'_input_device'})
    {
        ### get device if it already exist ###
        my $dev = $self->rig->get_device($self->{'_device_type'},$self->{'_input_device'});
        $self->{'_idev'} = $dev if ($dev);       
        if ($dev)
        {
            my @arr = $dev->list_set_functions();
            $self->{'_set_fn_names'} = \@arr;
	    $dev->attatch_observer(\$self);
	    $self->{'_readonly'} = 1; ### A slave device can not  be directly controlled, only indirectly ###
        }
    }
    $self->{'_info_string'} = 'Note that filter devices used in a control chain only works correctly if they are included (enabled)';
    ### Register device as a type of the underlying devices so ###
    ### as to show up as that type as well as a filter device! ###
    $self->rig->register_device($self->{'_device_type'},$self);
    ### Note, do NOT run init as part of constructor! ###
    return $self;
}

return 1;




sub init()
{
    my $self = shift;
    if (!$self->{'_idev'} && $self->{'_input_device'})
    {
        ### get device if it already exist ###
        ### It may have been created after SPDEV constructor ran and call of init() ###
        my $dev = $self->rig->get_device($self->{'_device_type'},$self->{'_input_device'});
        ### Initialise it if it doesn't ###
	unless ($dev)
	{
	    $dev = $self->rig->init_device($self->{'_device_type'},$self->{'_input_device'});
	    $dev->readstring_ignore(1) if ($dev);
	}
        $self->{'_idev'} = $dev if ($dev);       
        if ($dev)
        {
	    $dev->debug($self->debug);
            my @arr = $dev->list_set_functions();
            $self->{'_set_fn_names'} = \@arr;
	    $dev->attatch_observer(\$self);
	    $self->{'_readonly'} = 1; ### A slave device can not  be directly controlled, only indirectly ###
        }
    }
    if (!$self->{'_dev'} && $self->{'_control_device'})
    {
        ### get device if it already exist                                          ###
        ### It may have been created after SPDEV constructor ran and call of init() ###
        my $dev = $self->rig->get_device('relay',$self->{'_control_device'});
        ### Initialise it if it doesn't ###
        unless ($dev)
        {
            $dev = $self->rig->init_device('relay',$self->{'_control_device'});
            $dev->readstring_ignore(1) if ($dev);
        }
        $self->{'_dev'} = $dev if ($dev);
        $dev->debug($self->debug);
	$self->die("Could not initialise filter device of type 'relay' with name: '".$self->{'_device'}."'") unless ($self->{'_dev'});
    }
    if (!$self->{'_dev_1'} && $self->{'_device_type'} && $self->{'_device_1'})
    {
        ### get device if it already exist                                          ###
        ### It may have been created after SPDEV constructor ran and call of init() ###
        my $dev = $self->rig->get_device($self->{'_device_type'},$self->{'_device_1'});
        ### Initialise it if it doesn't ###
        unless ($dev)
        {
            $dev = $self->rig->init_device($self->{'_device_type'},$self->{'_device_1'});
            $dev->readstring_ignore(1) if ($dev);
        }
        $self->{'_dev_1'} = $dev if ($dev);
        $dev->debug($self->debug);
        ### Explicitly set readonly on underlying devices ###
	$dev->set_readonly(); 
	$self->die("Could not initialise filter device of type '".$self->{'_device_type'}."' with name: '".$self->{'_device_1'}."'") unless ($self->{'_dev_1'});
    }
    if (!$self->{'_dev_2'} && $self->{'_device_type'} && $self->{'_device_2'})
    {
        ### get device if it already exist                                          ###
        ### It may have been created after SPDEV constructor ran and call of init() ###
        my $dev = $self->rig->get_device($self->{'_device_type'},$self->{'_device_2'});
        ### Initialise it if it doesn't ###
        unless ($dev)
        {
            $dev = $self->rig->init_device($self->{'_device_type'},$self->{'_device_2'});
            $dev->readstring_ignore(1) if ($dev);
        }
        $self->{'_dev_2'} = $dev if ($dev);
        $dev->debug($self->debug);
	### Explicitly set readonly on underlying devices ###
	$dev->set_readonly();
	$self->die("Could not initialise filter device of type '".$self->{'_device_type'}."' with name: '".$self->{'_device_2'}."'") unless ($self->{'_dev_2'});
    }
}

sub notify
{
    my $self = shift;
    $self->init();
    my $data = RFC::Cache->new();
    my @response = $self->SUPER::notify(@_);
    push @response, $self->device($data)->notify(@_) if ($self->device($data));
    return @response;
}

sub observe($$)
{
    my $self = shift;
    my $action = shift;
    my $value = shift;
    my $data = RFC::Cache->new();
    return $self->device($data)->observe($action,$value,@_) if ($self->device($data));
}




sub read
{
    my $self = shift;
    return $self->device(@_)->read(@_);
}

sub test
{
    my $self = shift;
    return 0 unless ($self->{'_dev'} && $self->{'_dev'}->test(@_));
    return $self->device(@_)->test(@_);
}

sub test_string
{
    my $self = shift;
    my $data = undef;
    if (@_)
    {
        $data = $_[0];
    }
    else
    {
        $data = RFC::Cache->new();
        $data->debug($self->debug) if ($self->debug);
    }
    $self->check_cache($data);
    my @arr = ();
    my $string = 'Y-split filter device '.$self->name.': ';
    my $name = $self->device($data)->name;
    my $raw = $self->{'_dev'}->read($data);
    my $val = $self->read($data);
    push @arr,'Status of control device: '.$raw;
    push @arr,'Controlled device: '.$name;
    push @arr,'Device value: '.$val;
    $string .= join(', ',@arr)."\n";
    return $string;
}

sub Accept
{
    my $self = shift;
    my $visitor= shift;
    return undef if ($self->{'_seen_visitors'}{$visitor});
    $self->{'_dev'}->Accept($visitor);
    $self->{'_dev_1'}->Accept($visitor) if ($self->{'_dev_1'});
    $self->{'_dev_2'}->Accept($visitor) if ($self->{'_dev_2'});
    $visitor->VisitComplex($self);
    $self->{'_seen_visitors'}{$visitor} = 1;
    return undef;
}


sub get_status
{
    my $self = shift;
    return $self->{'_dev'}->read(@_) if ($self->{'_dev'});
    return 0;
}

sub device
{
    my $self = shift;
    $self->init();
    if ($self->get_status(@_))
    {
	return $self->{'_dev_2'};
    }
    else
    {
	return $self->{'_dev_1'};
    }
}

sub readonly_value($)
{
    my $self = shift;
    my $tag = shift;
    return undef unless ($tag);
    ### allow call to get 'mode' for undefined devices ###
    return 'Na' if ($tag eq 'mode' && !$self->device);
    return $self->device(@_)->mode() if ($tag eq 'mode');
    return $self->device(@_)->readonly_value($tag);
}


sub read_named
{
    my $self = shift;
    return $self->device(@_)->read_named(@_);
}


sub AUTOLOAD()
{
    ################################################################
    ### AUTOLOAD() , redirect all method calls not defined by    ###
    ### Basedevice to the underlying device                      ###
    ################################################################
    my $self = shift;
    {
        no strict "refs";
        $AUTOLOAD =~ /\:\:(\w+)$/;
	return $self->device()->$1(@_);
    }
}

sub print_config()
{
    my $self = shift;
    $self->init();
    my @arr = ();
    push @arr,'Name: '.$self->name;
    push @arr,'Type: '.$self->type;
    push @arr,'Control device: '.$self->{'_dev'}->name() if ($self->{'_dev'});
    push @arr,'Type: '.$self->{'_dev'}->type if ($self->{'_dev'});
    push @arr,'Device 1: '.$self->{'_dev_1'}->name if ($self->{'_dev_1'});
    push @arr,'Type: '.$self->{'_dev_1'}->type if ($self->{'_dev_1'});
    push @arr,'Device 2: '.$self->{'_dev_2'}->name if ($self->{'_dev_2'});
    push @arr,'Type: '.$self->{'_dev_2'}->type if ($self->{'_dev_2'});
    return join(', ',@arr);
}
