package X11::Xlib::Struct;
use strict;
use warnings;
use X11::Xlib ();
use Carp ();

our $VERSION = '0.23';

=head1 NAME

X11::Xlib::Struct - Base class for X11 packed structures

=head1 DESCRIPTION

Base class for the various exposed C-structs of Xlib, which are represented
as a blessed scalar-ref of the raw bytes of the struct.  This makes them more
efficient than fully inflating/deflating perl hashrefs for every Xlib call.

All attribute accessors are defined in XS.

=head1 ATTRIBUTES

=head2 display

A reference to an L<X11::Xlib> instance, automatically set by various functions
that populate structs.  Many times a struct will have pointers that are related
to a particular display, so it is desirable to pair the struct with its display
pointer in order to be able to inflate those other pointers into objects and be
able to call methods on them.

This attribute is implemented "inside-out", so it can't be seen in a dump of the
perl data, and requires help from DESTROY to clean up.  (handled by this class's
destructor)

=cut

*display= *X11::Xlib::Opaque::display;

=head1 METHODS

=head2 new

  my $struct= X11::Xlib::....->new( %optional_fields );

The constructor sets all fields to their initial value (i.e. zero)
and then applies the list of key/value pairs.  Warns on un-known
field names.

=cut

sub new {
    my $class= shift;
    $class= ref $class if ref $class;
    my $self= bless \(my $buffer), $class;
    $self->_initialize;
    $self->apply(@_) if @_; # If arguments, then initialize using apply
    $self;
}

=head2 initialize

Set all struct fields to a sensible initial value (like zero)

=cut

sub initialize {
    shift->_initialize;
}

=head2 pack

  $struct->pack( \%fields, $consume, $warn );

Pack field values into the bytes of the struct.  Only C<%fields> is required.

If C<$consume> is true, then remove any key of C<%fields> that was processed.

If C<$warn> is true, then emit a warning if any un-recognized field was given.

=cut

sub pack {
    my ($self, $fields, $consume, $warn)= @_;
    $fields= { %$fields } unless $consume;
    $self->_pack($fields, 1);
    Carp::carp("Un-used parameters passed to pack: ".join(',', keys %$fields))
        if $warn && keys %$fields;
    return $self;
}

=head2 apply

  $struct->apply( \%fields );
  $struct->apply( field => $val, ... );

Alias for C< pack(\%fields, 1, 1) >.
For each given field, update that member of the struct.
Emits a warning if the hash contains unknown fields.

=cut

sub apply {
    my $self= shift;
    Carp::croak("Expected hashref or even-length list")
        unless 1 == @_ && ref($_[0]) eq 'HASH' or !(1 & @_);
    my %args= @_ == 1? %{ shift() } : @_;
    
    $self->pack(\%args, 1, 1);
}

=head2 unpack

  my $hashref= $struct->unpack();

Extract all fields as Perl data.

=cut

sub unpack {
    my $self= shift;
    $self->_unpack(my $ret= {});
    if ($self->can('display') && defined (my $dpy= $self->display)) {
        # tag all objects returned as belonging to this display
        for (values %$ret) {
            $_->display($dpy) if ref $_ && ref($_)->can('display');
        }
    }
    $ret;
}

=head2 bytes

Access the scalar holding the bytes of the struct.

=cut

sub bytes { ${$_[0]} }
*buffer= *bytes;

# The struct code is all in XS, so all we need to do is declare the package
# inheritence.  Except for XEvent, which is complicated.

require X11::Xlib::XEvent;
@X11::Xlib::XVisualInfo::ISA= ( __PACKAGE__ );
$X11::Xlib::XVisualInfo::VERSION= $VERSION;
@X11::Xlib::XWindowChanges::ISA= ( __PACKAGE__ );
$X11::Xlib::XWindowChanges::VERSION= $VERSION;
@X11::Xlib::XWindowAttributes::ISA= ( __PACKAGE__ );
$X11::Xlib::XWindowAttributes::VERSION= $VERSION;
@X11::Xlib::XSetWindowAttributes::ISA= ( __PACKAGE__ );
$X11::Xlib::XSetWindowAttributes::VERSION= $VERSION;
@X11::Xlib::XSizeHints::ISA= ( __PACKAGE__ );
$X11::Xlib::XSizeHints::VERSION= $VERSION;
@X11::Xlib::XRectangle::ISA= ( __PACKAGE__ );
$X11::Xlib::XRectangle::VERSION= $VERSION;
@X11::Xlib::XRenderPictFormat::ISA= ( __PACKAGE__ );
$X11::Xlib::XRenderPictFormat::VERSION= $VERSION;

1;

__END__

=head1 AUTHOR

Olivier Thauvin, E<lt>nanardon@nanardon.zarb.orgE<gt>

Michael Conrad, E<lt>mike@nrdvana.netE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2009-2010 by Olivier Thauvin

Copyright (C) 2017-2021 by Michael Conrad

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.10.0 or,
at your option, any later version of Perl 5 you may have available.

=cut
