#
# Module Generated by Template::Tiny on Fri Aug  4 11:16:27 UTC 2023
#

package ZMQ::FFI::ZMQ4_1::Context;
$ZMQ::FFI::ZMQ4_1::Context::VERSION = '1.19';
use FFI::Platypus;
use ZMQ::FFI::Util qw(zmq_soname current_tid);
use ZMQ::FFI::Constants qw(:all);
use ZMQ::FFI::ZMQ4_1::Socket;
use ZMQ::FFI::ZMQ4_1::Raw;
use ZMQ::FFI::Custom::Raw;
use Try::Tiny;
use Scalar::Util qw(weaken);

use FFI::Platypus::Memory qw(free malloc);
use FFI::Platypus::Buffer qw(buffer_to_scalar);

use Moo;
use namespace::clean;

with qw(
    ZMQ::FFI::ContextRole
    ZMQ::FFI::ErrorHelper
    ZMQ::FFI::Versioner
);

my $FFI_LOADED;

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

    unless ($FFI_LOADED) {
        ZMQ::FFI::Custom::Raw::load($self->soname);
        ZMQ::FFI::ZMQ4_1::Raw::load($self->soname);
        $FFI_LOADED = 1;
    }

    $self->init()
}


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

    try {
        $self->context_ptr( zmq_ctx_new() );
        $self->check_null('zmq_ctx_new', $self->context_ptr);
    }
    catch {
        $self->context_ptr(-1);
        die $_;
    };

    if ( $self->has_threads ) {
        $self->set(ZMQ_IO_THREADS, $self->threads);
    }

    if ( $self->has_max_sockets ) {
        $self->set(ZMQ_MAX_SOCKETS, $self->max_sockets);
    }
}

sub get {
    my ($self, $option) = @_;

    my $option_val = zmq_ctx_get($self->context_ptr, $option);
    $self->check_error('zmq_ctx_get', $option_val);

    return $option_val;
}

sub set {
    my ($self, $option, $option_val) = @_;

    $self->check_error(
        'zmq_ctx_set',
        zmq_ctx_set($self->context_ptr, $option, $option_val)
    );
}

sub socket {
    my ($self, $type) = @_;

    my $socket;

    try {
        my $socket_ptr = zmq_socket($self->context_ptr, $type);

        $self->check_null('zmq_socket', $socket_ptr);

        $socket = ZMQ::FFI::ZMQ4_1::Socket->new(
            socket_ptr   => $socket_ptr,
            context      => $self, # this will become a weak ref
            type         => $type,
            soname       => $self->soname,
        );
    }
    catch {
        die $_;
    };

    # add the socket to the socket hash
    $self->_add_socket($socket);

    return $socket;
}

sub proxy {
    my ($self, $frontend, $backend, $capture) = @_;

    $self->check_error(
        'zmq_proxy',
        zmq_proxy(
            $frontend->socket_ptr,
            $backend->socket_ptr,
            defined $capture ? $capture->socket_ptr : undef,
        )
    );
}

sub device {
    my ($self, $type, $frontend, $backend) = @_;

    $self->bad_version(
        $self->verstr,
        "zmq_device not available in zmq >= 3.x",
    );
}

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

    return if $self->context_ptr == -1;

    # don't try to cleanup context cloned from another thread
    return unless $self->_tid == current_tid();

    # don't try to cleanup context copied from another process (fork)
    return unless $self->_pid == $$;

    $self->check_error(
        'zmq_ctx_term',
        zmq_ctx_term($self->context_ptr)
    );

    $self->context_ptr(-1);
}

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

    my $public_key_buf = malloc(41);
    my $secret_key_buf = malloc(41);

    $self->check_error(
        'zmq_curve_keypair',
        zmq_curve_keypair($public_key_buf, $secret_key_buf)
    );

    my $public_key = buffer_to_scalar($public_key_buf, 41);
    my $secret_key = buffer_to_scalar($secret_key_buf, 41);
    free($public_key_buf);
    free($secret_key_buf);

    return ($public_key, $secret_key);
}

sub z85_encode {
    my ($self, $data) = @_;
    
    my $dest_buf = malloc(41);
    
    my $checked_data = substr($data, 0, 32);
    
    $self->check_null(
        'zmq_z85_encode',
        zmq_z85_encode( $dest_buf, $checked_data, length($checked_data) )
    );
    
    my $dest = buffer_to_scalar($dest_buf, 41);
    free($dest_buf);
    
    return $dest;
}

sub z85_decode {
    my ($self, $string) = @_;
    
    my $dest_buf = malloc(32);
    
    $self->check_null(
        'zmq_z86_decode',
        zmq_z85_decode($dest_buf, $string)
    );
    
    my $dest = buffer_to_scalar($dest_buf, 32);
    free($dest_buf);
    
    return $dest;
}

sub has_capability {
    my ($self, $capability) = @_;
    return zmq_has($capability);
}


sub _add_socket {
    my ($self, $socket) = @_;
    weaken($self->sockets->{$socket} = $socket);
}

sub _remove_socket {
    my ($self, $socket) = @_;
    delete($self->sockets->{$socket});
}

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

    return if $self->context_ptr == -1;

    # check defined to guard against
    # undef objects during global destruction
    if (defined $self->sockets) {
        for my $socket_k (keys %{$self->sockets}) {
            my $socket = $self->_remove_socket($socket_k);
            $socket->close()
                if defined $socket && $socket->socket_ptr != -1;
        }
    }

    $self->destroy();
}

1;

# vim:ft=perl

__END__

=pod

=encoding UTF-8

=head1 NAME

ZMQ::FFI::ZMQ4_1::Context

=head1 VERSION

version 1.19

=head1 AUTHOR

Dylan Cali <calid1984@gmail.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2023 by Dylan Cali.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut
