package Win32::WQL;
use strict;
use Win32::OLE; #  qw(EVENTS);
# Events support will follow later
use parent 'Class::Accessor';

=head1 NAME

Win32::WQL - DBI-like wrapper for the WMI

=head1 SYNOPSIS

  use Win32::WQL;
  my $wmi = Win32::WQL->new( machine => 'remote_computer' );
  my $sth = $wmi->prepare(<<'WQL');

    ASSOCIATORS OF {Win32_Directory.Name='C:\\WINNT'}
    WHERE ResultClass = CIM_DataFile

  WQL

  my $remote_files = $sth->execute;
  while (my $file = $remote_files->fetch()) {
      print $file->{Name},"\n";
  };

=head1 OVERVIEW

This module implements a bare bones DBI clone
which is similar yet different. You will most likely
want to use the real thing, L<DBD::WMI>, which
is a compatibility layer over this module.

=cut

our $VERSION = '0.11';

Win32::OLE->Option(Warn => 3);

__PACKAGE__->mk_accessors(qw(statement_class event_iterator_class collection_iterator_class wmi));

=head1 METHODS

=head2 C<< new %ARGS >>

Initializes the thin wrapper over the L<Win32::OLE>
WMI instance. All parameters are optional.

  machine

The parameter is the machine name to connect to. It defaults
to the local machine.

  wmi

A preinitialized WMI object to use. Defaults to creating
a fresh instance.

  statement_class

The class into which the results of C<prepare>
are blessed. Defaults to C<Win32::WQL::Statement>.

  event_iterator_class

The class into which the results of C<fetchrow>
are blessed for event queries. Defaults to
C<Win32::WQL::Iterator::Event>.

  collection_iterator_class

The class into which the results of C<fetchrow>
are blessed for static queries. Defaults to
C<Win32::WQL::Iterator::Collection>.

=cut

sub new {
    my ($package, %args) = @_;
    my $machine = delete $args{machine} || '.';
    my $self = {
        wmi             => Win32::OLE->GetObject("winmgmts:\\\\$machine\\root\\cimV2"),
        statement_class => 'Win32::WQL::Statement',
        event_iterator_class  => 'Win32::WQL::Iterator::Event',
        collection_iterator_class  => 'Win32::WQL::Iterator::Collection',
        %args,
    };
    $package->SUPER::new($self);
};

=head2 C<< $wmi->prepare QUERY >>

Returns a prepared query by calling

    return $self->statement_class->new({
        query => $query,
        wmi => $self,
        iterator_class => $class,
        wmi_method => $method,
    });

=cut

sub prepare {
    my ($self,$query) = @_;

    my ($class,$method,$fetch);
    if ($self->event_query($query)) {
        $class = $self->event_iterator_class;
        $method = 'ExecNotificationQuery';
    } else {
        $class = $self->collection_iterator_class;
        $method = 'ExecQuery';
    };

    return $self->statement_class->new({
        query => $query,
        wmi => $self,
        iterator_class => $class,
        wmi_method => $method,
    });
}

=head2 C<< $wmi->event_query QUERY >>

Determines whether a query is an event query
or a static query.

Event queries return a row whenever a new event
arrives and block if there is no event available.

Static queries are static and return all rows in
one go.

A query is considered an event query if it
matches

     $query =~ /\b__instance(?:\w+)event\b/i
  or $query =~ /\wEvent\b/i

=cut

sub event_query {
    my ($package, $query) = @_;
    return
    (   $query =~ /\b__instance(?:\w+)event\b/i
     or $query =~ /\wEvent\b/i
    )
}

package Win32::WQL::Statement;
use strict;
use Win32::OLE;
use base 'Class::Accessor';

__PACKAGE__->mk_accessors(qw(iterator_class wmi query wmi_method));

sub execute {
    my ($self) = @_;
    my $m = $self->wmi_method;
    my $i = $self->wmi->wmi->$m( $self->query );
    return $self->iterator_class->new({
        wql_iterator => $i
    });
}

package Win32::WQL::Iterator::Collection;
use strict;
use base 'Class::Accessor';
__PACKAGE__->mk_accessors(qw(wql_iterator items));

use Win32::OLE qw(in);
use Data::Dumper;

# Finite, prefetched list

sub new {
    my ($package,$args) = @_;
    my @items = in delete $args->{wql_iterator};
    $args->{items} = \@items;
    $package->SUPER::new($args);
}

sub fetchrow {
    my ($self) = @_;
    if (@{ $self->items }) {
        shift @{ $self->items };
    } else {
        return ()
    };
}

package Win32::WQL::Iterator::Event;
use strict;
use base 'Class::Accessor';
__PACKAGE__->mk_accessors(qw(wql_iterator));

# potentially infinite list

sub fetchrow {
    my ($self) = @_;
    return $self->wql_iterator->NextEvent();
}

1;

=head1 SEE ALSO

L<DBD::WMI> for more examples

=head1 TODO

=over 4

=item * Implement parameters for and credentials

=item * Implement a multiplexer by using multiple, waiting threads so you can C<SELECT>
events from more than one WMI namespace

=back

=head1 REPOSITORY

The public repository of this module is
L<https://github.com/Corion/dbd-wmi>.

=head1 SUPPORT

The public support forum of this module is
L<https://perlmonks.org/>.

=head1 BUG TRACKER

Please report bugs in this module via the RT CPAN bug queue at
L<https://rt.cpan.org/Public/Dist/Display.html?Name=DBD-WMI>
or via mail to L<www-mechanize-phantomjs-Bugs@rt.cpan.org>.

=head1 AUTHOR

Max Maischein C<corion@cpan.org>

=head1 COPYRIGHT (c)

Copyright 2009-2018 by Max Maischein C<corion@cpan.org>.

=head1 LICENSE

This module is released under the same terms as Perl itself.

=cut