# [[[ HEADER ]]]
package RPerl::Operation::Expression::SubExpression::Variable;
use strict;
use warnings;
use RPerl::AfterSubclass;
our $VERSION = 0.009_000;

# [[[ OO INHERITANCE ]]]
use parent qw(RPerl::Operation::Expression::SubExpression);
use RPerl::Operation::Expression::SubExpression;

# [[[ OO PROPERTIES ]]]
our hashref $properties = {};

# [[[ SUBROUTINES & OO METHODS ]]]

sub ast_to_rperl__generate {
    { my string_hashref::method $RETURN_TYPE };
    ( my object $self, my string_hashref $modes) = @ARG;
    my string_hashref $rperl_source_group = { PMC => q{} };

    #    RPerl::diag( 'in Variable->ast_to_rperl__generate(), received $self = ' . "\n" . RPerl::Parser::rperl_ast__dump($self) . "\n" );

    my string $self_class = ref $self;

    # unwrap Variable_197 from SubExpression_158, VariableOrLiteral_247, or VarOrLitOrOpStrOrWord_249
    if ( ( $self_class eq 'SubExpression_158' ) or ( $self_class eq 'VariableOrLiteral_247' ) or ( $self_class eq 'VarOrLitOrOpStrOrWord_249' ) ) {
        $self = $self->{children}->[0];
    }

    $self_class = ref $self;
    if ( $self_class eq 'Variable_197' ) {

        # Variable -> VariableSymbolOrSelf VariableRetrieval*
        my string $symbol_or_self = $self->{children}->[0]->{children}->[0];
        $rperl_source_group->{PMC} .= $symbol_or_self;

        foreach my object $variable_retrieval ( @{ $self->{children}->[1]->{children} } ) {
            my string_hashref $rperl_source_subgroup = $variable_retrieval->ast_to_rperl__generate($modes);
            RPerl::Generator::source_group_append( $rperl_source_group, $rperl_source_subgroup );
        }
    }
    else {
        die RPerl::Parser::rperl_rule__replace( 'ERROR ECOGEASRP00, CODE GENERATOR, ABSTRACT SYNTAX TO RPERL: Grammar rule '
                . ($self_class)
                . ' found where SubExpression_158, VariableOrLiteral_247, VarOrLitOrOpStrOrWord_249, or Variable_197 expected, dying' )
            . "\n";
    }
    return $rperl_source_group;
}

sub ast_to_cpp__generate__CPPOPS_PERLTYPES {
    { my string_hashref::method $RETURN_TYPE };
    ( my object $self, my string_hashref $modes) = @ARG;
    my string_hashref $cpp_source_group = { CPP => q{// <<< RP::O::E::SE::V __DUMMY_SOURCE_CODE CPPOPS_PERLTYPES >>>} . "\n" };

    #...
    return $cpp_source_group;
}

sub ast_to_cpp__generate__CPPOPS_CPPTYPES {
    { my string_hashref::method $RETURN_TYPE };
    ( my object $self, my string_hashref $modes) = @ARG;
    my string_hashref $cpp_source_group = { CPP => q{} };

#    RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES(), received $self = ' . "\n" . RPerl::Parser::rperl_ast__dump($self) . "\n" );
#    RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES(), received $modes = ' . Dumper($modes) . "\n" );

    my string $self_class = ref $self;

    # unwrap Variable_197 from SubExpression_158, VariableOrLiteral_247, or VarOrLitOrOpStrOrWord_249
    if ( ( $self_class eq 'SubExpression_158' ) or ( $self_class eq 'VariableOrLiteral_247' ) or ( $self_class eq 'VarOrLitOrOpStrOrWord_249' ) ) {
        $self = $self->{children}->[0];
    }

    $self_class = ref $self;
    if ( $self_class eq 'Variable_197' ) {

        # Variable -> VariableSymbolOrSelf VariableRetrieval*
        my string $symbol_or_self = $self->{children}->[0]->{children}->[0];
        my string_arrayref $types = [];

#        RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES(), have pre-modification $symbol_or_self = ' . $symbol_or_self . "\n" );

        substr $symbol_or_self, 0, 1, q{};    # remove leading $ sigil

        # determine first type
        if ( $symbol_or_self eq 'self' ) {
            $symbol_or_self = 'this';  # Perl OO $self becomes C++ OO this
            $types->[0] = $modes->{_symbol_table}->{_namespace};
        }
        else {
            # variable retrieval (individual or multiple chained) can only be initiated from a variable, which must be in the symtab, in a namespace, in a subroutine
            $types->[0]
                = $modes->{_symbol_table}->{ $modes->{_symbol_table}->{_namespace} }->{ $modes->{_symbol_table}->{_subroutine} }->{$symbol_or_self}->{type};
        }

#        RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES(), have post-modification $symbol_or_self = ' . $symbol_or_self . "\n" );

        $cpp_source_group->{CPP} = $symbol_or_self;
        my integer $i_max = ( scalar @{ $self->{children}->[1]->{children} } ) - 1;
        for my integer $i ( 0 .. $i_max ) {
            my object $variable_retrieval = $self->{children}->[1]->{children}->[$i];
#            RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES(), for loop $i = ' . $i . ', have $types->[$i] = ' . $types->[$i] . "\n" );
#            RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES(), for loop $i = ' . $i . ', have $variable_retrieval = ' . "\n" . RPerl::Parser::rperl_ast__dump($variable_retrieval) . "\n" );

            my string_hashref $cpp_source_subgroup = $variable_retrieval->ast_to_cpp__generate__CPPOPS_CPPTYPES( $symbol_or_self, $types->[$i], $modes );
            RPerl::Generator::source_group_append( $cpp_source_group, $cpp_source_subgroup );

            # determine next type
            if ( $i < $i_max ) {
                if ( $types->[$i] =~ /_arrayref$/ ) { # array
                    $types->[ $i + 1 ] = substr $types->[$i], 0, ( ( length $types->[$i] ) - 9 );    # strip trailing '_arrayref'
                }
                elsif ( $types->[$i] =~ /_hashref$/ ) { # hash
                    $types->[ $i + 1 ] = substr $types->[$i], 0, ( ( length $types->[$i] ) - 8 );    # strip trailing '_hashref'
                }
                elsif ( $types->[$i] =~ /sse_number_pair$/xms ) { # SSE number pair
                    $types->[ $i + 1 ] = 'number';
                }
                elsif ( exists $rperlnamespaces_generated::RPERL->{ $types->[$i] . '::' } ) { # scalar
                    die 'ERROR ECOGEASCP30, CODE GENERATOR, ABSTRACT SYNTAX TO C++: Variable retrieval, can not retrieve data subcomponent from scalar type ' . $types->[$i] . ', dying' . "\n";
                }
                else { # user-defined type AKA class
                    if ( ( ref $variable_retrieval ) eq 'VariableRetrieval_199' ) {    # VariableRetrieval -> OP02_HASH_THINARROW SubExpression '}'
                        my object $subexpression = $variable_retrieval->{children}->[1];
                        if (( ( ref $subexpression ) eq 'SubExpression_157' )          # SubExpression -> Literal
                            or ( ( ref $subexpression ) eq 'VariableOrLiteral_248' )          # VariableOrLiteral -> Literal
                            or ( ( ref $subexpression ) eq 'VarOrLitOrOpStrOrWord_250' )    # VarOrLitOrOpStrOrWord -> Literal
                            )
                        {
                            my string $number_or_string_literal = $subexpression->{children}->[0]->{children}->[0];
                            $number_or_string_literal = eval "return $number_or_string_literal";  # strip quotations from string and underscores from number, etc.

                            if ((substr $types->[$i], -2, 2) ne '::') { $types->[$i] .= '::'; }  # _properties in _symbol_table are keyed by class/package names ending in ::
#                            RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES() #0, for loop $i = ' . $i . ', have $modes->{_symbol_table}->{ $types->[$i] }->{_properties} = ' . $modes->{_symbol_table}->{ $types->[$i] }->{_properties} . "\n" );

                            # check if OO property exists in self class or parent class
                            if ( exists $modes->{_symbol_table}->{ $types->[$i] }->{_properties}->{$number_or_string_literal} ) {
                                $types->[ $i + 1 ] = $modes->{_symbol_table}->{ $types->[$i] }->{_properties}->{$number_or_string_literal}->{type};
                            }
                            else {
                                my $parent_package_names = RPerl::CompileUnit::Module::Class::parent_and_grandparent_package_names(substr $types->[$i], 0, -2);
                                my boolean $property_found = 0;
                                foreach my $parent_package_name (@{$parent_package_names}) {
                                    if (exists $modes->{_symbol_table}->{ $parent_package_name . q{::} }->{_properties}->{$number_or_string_literal}) {
                                        $types->[ $i + 1 ] = $modes->{_symbol_table}->{ $parent_package_name . q{::} }->{_properties}->{$number_or_string_literal}->{type};
                                        $property_found = 1;
                                        last;
                                    }
                                }
                                if (not $property_found) {
                                    if ((not exists $modes->{dependencies}) or
                                        (not defined $modes->{dependencies}) or
                                        ($modes->{dependencies} ne 'ON')) {
                                        die 'ERROR ECOGEASCP31 #0, CODE GENERATOR, ABSTRACT SYNTAX TO C++: Variable retrieval, dependencies disabled, can not verify validity of OO property ' . $number_or_string_literal . ' in user-defined class ' . $types->[$i] . ', dying' . "\n";
                                    }
                                    else {
                                        die 'ERROR ECOGEASCP32 #0, CODE GENERATOR, ABSTRACT SYNTAX TO C++: Variable retrieval, can not retrieve invalid OO property ' . $number_or_string_literal . ' in user-defined class ' . $types->[$i] . ', dying' . "\n";
                                    }
                                }
                            }
                        }
                        else {
                            my string $subexpression_generated = ($subexpression->ast_to_cpp__generate__CPPOPS_CPPTYPES( $modes ))->{CPP};
                            die 'ERROR ECOGEASCP33, CODE GENERATOR, ABSTRACT SYNTAX TO C++: Variable retrieval, can not determine OO property name (and thus type) from non-literal subexpression...' . "\n" . $subexpression_generated . "\n" . '...in user-defined class ' . $types->[$i] . ', dying' . "\n";
                        }
                    }
                    elsif ( ( ref $variable_retrieval ) eq 'VariableRetrieval_200' ) {        # VariableRetrieval -> OP02_HASH_THINARROW WORD '}'
                        if ((substr $types->[$i], -2, 2) ne '::') { $types->[$i] .= '::'; }  # _properties in _symbol_table are keyed by class/package names ending in ::
                        my string $word = $variable_retrieval->{children}->[1];
#                        RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES() #1, for loop $i = ' . $i . ', have $word = ' . $word . "\n" );
#                        RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES() #1, for loop $i = ' . $i . ', have $types->[$i] = ' . $types->[$i] . "\n" );
#                        RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES() #1, for loop $i = ' . $i . ', have $modes->{_symbol_table} = ' . Dumper($modes->{_symbol_table}) . "\n" );
#                        RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES() #1, for loop $i = ' . $i . ', have $modes->{_symbol_table}->{ $types->[$i] } = ' . $modes->{_symbol_table}->{ $types->[$i] } . "\n" );
#                        RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES() #1, for loop $i = ' . $i . ', have $modes->{_symbol_table}->{ $types->[$i] }->{_properties} = ' . $modes->{_symbol_table}->{ $types->[$i] }->{_properties} . "\n" );

                        # check if OO property exists in self class or parent class
                        if ( exists $modes->{_symbol_table}->{ $types->[$i] }->{_properties}->{$word} ) {
                            $types->[ $i + 1 ] = $modes->{_symbol_table}->{ $types->[$i] }->{_properties}->{$word}->{type};
                        }
                        else {
                            my $parent_package_names = RPerl::CompileUnit::Module::Class::parent_and_grandparent_package_names(substr $types->[$i], 0, -2);
#                            RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES(), for loop $i = ' . $i . ', have $parent_package_names = ' . Dumper($parent_package_names) . "\n" );
                            my boolean $property_found = 0;
                            foreach my $parent_package_name (@{$parent_package_names}) {
#                                RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES(), for loop $i = ' . $i . ', have $parent_package_name = ' . $parent_package_name . "\n" );
#                                RPerl::diag( 'in Variable->ast_to_cpp__generate__CPPOPS_CPPTYPES(), for loop $i = ' . $i . ', have $modes->{_symbol_table} = ' . Dumper($modes->{_symbol_table}) . "\n" );
                                if (exists $modes->{_symbol_table}->{ $parent_package_name . q{::} }->{_properties}->{$word}) {
                                    $types->[ $i + 1 ] = $modes->{_symbol_table}->{ $parent_package_name . q{::} }->{_properties}->{$word}->{type};
                                    $property_found = 1;
                                    last;
                                }
                            }
                            if (not $property_found) {
                                if ((not exists $modes->{dependencies}) or
                                    (not defined $modes->{dependencies}) or
                                    ($modes->{dependencies} ne 'ON')) {
                                    die 'ERROR ECOGEASCP31 #1, CODE GENERATOR, ABSTRACT SYNTAX TO C++: Variable retrieval, dependencies disabled, can not verify validity of OO property ' . $word . ' in user-defined class ' . $types->[$i] . ', dying' . "\n";
                                }
                                else {
                                    die 'ERROR ECOGEASCP31 #2, CODE GENERATOR, ABSTRACT SYNTAX TO C++: Variable retrieval, can not retrieve invalid OO property ' . $word . ' in user-defined class ' . $types->[$i] . ', dying' . "\n";
                                }
                            }
                        }
                    }
                    else {
                        die 'ERROR ECOGEASCP34, CODE GENERATOR, ABSTRACT SYNTAX TO C++: Variable retrieval, can not use arrayref retrieval on object of user-defined class ' . $types->[$i] . ', dying' . "\n";
                    }
                }
            }
        }
    }
    else {
        die RPerl::Parser::rperl_rule__replace( 'ERROR ECOGEASCP00, CODE GENERATOR, ABSTRACT SYNTAX TO C++: Grammar rule '
                . ($self_class)
                . ' found where SubExpression_158, VariableOrLiteral_247, VarOrLitOrOpStrOrWord_249, or Variable_197 expected, dying' )
            . "\n";
    }
    return $cpp_source_group;
}

1;    # end of class