package Sidef::Types::Regex::Match {

    use utf8;
    use 5.016;

    use overload
      q{bool} => \&get_value,
      q{""}   => sub { CORE::join(' ', @{$_[0]->{captures}}) },
      q{@{}}  => sub {
        $_[0]->{_cached_cap} //= [map { Sidef::Types::String::String->new($_) } @{$_[0]->{captures}}];
      };

    use parent qw(
      Sidef::Object::Object
    );

    use Sidef::Types::Bool::Bool;

    sub new {
        my (undef, %hash) = @_;

        my @captures;
        if ($hash{regex}{global}) {

            pos($hash{string}) = CORE::int($hash{regex}{pos});
            my $match = $hash{string} =~ /$hash{regex}{regex}/g;

            if ($match) {
                $hash{regex}{pos} = pos($hash{string});

                foreach my $i (1 .. $#{+}) {
                    push @captures, substr($hash{string}, $-[$i], $+[$i] - $-[$i]);
                }

                $hash{matched} = 1;
            }
            else {
                $hash{regex}{pos} = 0;
                $hash{matched} = 0;
            }

            foreach my $key (keys %+) {
                $hash{named_captures}{$key} = $+{$key};
            }
        }
        else {
            $hash{pos} = CORE::int($hash{pos} // 0);
            $hash{pos} = 0 if ($hash{pos} < 0);
            @captures  = substr($hash{string}, $hash{pos}) =~ $hash{regex}{regex};

            $hash{matched}   = (@captures != 0);
            $hash{match_pos} = $hash{matched} ? [$-[0] + $hash{pos}, $+[0] + $hash{pos}] : [];

            foreach my $key (keys %+) {
                $hash{named_captures}{$key} = $+{$key};
            }
        }

        $hash{captures} = \@captures;

        bless \%hash, __PACKAGE__;
    }

    sub get_value {
        $_[0]->{matched};
    }

    sub matched {
        $_[0]->{matched}
          ? (Sidef::Types::Bool::Bool::TRUE)
          : (Sidef::Types::Bool::Bool::FALSE);
    }

    *to_bool       = \&matched;
    *is_successful = \&matched;

    sub pos {
        my ($self) = @_;
        Sidef::Types::Array::Array->new(
                     [@{$self->{_cached_pos} //= [map { Sidef::Types::Number::Number::_set_int($_) } @{$self->{match_pos}}]}]);
    }

    *match_pos = \&pos;

    sub captures {
        my ($self) = @_;
        Sidef::Types::Array::Array->new(
                           [@{$self->{_cached_cap} //= [map { Sidef::Types::String::String->new($_) } @{$self->{captures}}]}]);
    }

    *cap      = \&captures;
    *caps     = \&captures;
    *to_a     = \&captures;
    *to_array = \&captures;

    sub named_captures {
        my ($self) = @_;

        my $hash = Sidef::Types::Hash::Hash->new;
        while (my ($key, $value) = each %{$self->{named_captures}}) {
            $hash->{$key} = Sidef::Types::String::String->new($value);
        }
        $hash;
    }

    *ncap  = \&named_captures;
    *ncaps = \&named_captures;

    sub join {
        my ($self, $sep) = @_;
        Sidef::Types::String::String->new(CORE::join("$sep", @{$self->{captures}}));
    }

    sub string {
        my ($self) = @_;
        Sidef::Types::String::String->new($self->{string});
    }

    sub regex {
        my ($self) = @_;
        $self->{regex};
    }

    sub to_str {
        my ($self) = @_;
        Sidef::Types::String::String->new("$self");
    }

    *to_s = \&to_str;

    sub dump {
        my ($self) = @_;
        my $re     = $self->{regex}->dump;
        my $str    = Sidef::Types::String::String->new($self->{string})->dump;
        Sidef::Types::String::String->new("($str =~ $re)");
    }

    {
        no strict 'refs';
        *{__PACKAGE__ . '::' . '...'} = sub {
            @{$_[0]->{_cached_cap} //= [map { Sidef::Types::String::String->new($_) } @{$_[0]->{captures}}]};
        };
    }
};

1
