<commit_before><?php
namespace Poirot\Stream;

use Poirot\Core\BuilderSetterTrait;
use Poirot\Core\ErrorStack;
use Poirot\Core\OpenCall;
use Poirot\Stream\Context\BaseContext;
use Poirot\Stream\Context\Socket\SocketContext;
use Poirot\Stream\Interfaces\Context\iSContext;
use Poirot\Stream\Interfaces\iSResource;
use Poirot\Stream\Interfaces\iStreamClient;

class StreamClient implements iStreamClient
{
    use BuilderSetterTrait;

    /**
     * @var string
     */
    protected $socketUri;

    /**
     * @var float
     */
    protected $timeout;

    /**
     * @var boolean
     */
    protected $persistent;

    /**
     * @var boolean
     */
    protected $noneBlocking;

    /**
     * @var iSContext
     */
    protected $context;

    /** @var iSResource */
    protected $_c__connectedResource;

    // Events ....
    /** @var OpenCall */
    protected $_on__resource_connected;

    /**
     * Construct
     *
     * Note: When specifying a numerical IPv6 address (e.g. fe80::1),
     *       you must enclose the IP in square brackets—for example,
     *       tcp://[fe80::1]:80
     *
     * @param string|array                   $socketUri Socket Uri or Array of Builder Settings
     * @param iSContext|array|resource| null $context   Context Options
     */
    function __construct($socketUri, $context = null)
    {
        if (is_array($socketUri) && !empty($socketUri))
            $this->setupFromArray($socketUri);
        elseif (is_string($socketUri))
            $this->setSocketUri($socketUri);
        else
            throw new \InvalidArgumentException(sprintf(
                'StreamClient Construct give string or array of settings builder as first argument. given "%s".'
                , \Poirot\Core\flatten($socketUri)
            ));

        if ($context !== null) {
            if ($context instanceof iSContext)
                $this->setContext($context);
            else
                $this->getContext()->from($context);
        }
    }


    // ...

    /**
     * Add Closure Callable On Connected Resource
     *
     * - the closure functions will bind to this object
     *
     * closure:
     * function($resource) {
     *   // $this will point to StreamClient(current)
     * }
     *
     *
     * @return OpenCall
     */
    function whenResourceAvailable()
    {
        if (!$this->_on__resource_connected)
            $this->_on__resource_connected = new OpenCall($this);

        return $this->_on__resource_connected;
    }

    /**
     * Set Socket Uri
     *
     * Note: When specifying a numerical IPv6 address (e.g. fe80::1),
     *       you must enclose the IP in square brackets—for example,
     *       tcp://[fe80::1]:80
     *
     * @param string $socketUri
     *
     * @return $this
     */
    function setSocketUri($socketUri)
    {
        $this->socketUri = $socketUri;

        return $this;
    }

    /**
     * Get Current Socket Uri That Stream Built With
     *
     * @return string
     */
    function getSocketUri()
    {
        return $this->socketUri;
    }

    /**
     * Context Options
     *
     * @param iSContext|array|resource $context
     *
     * @throws \InvalidArgumentException
     * @return $this
     */
    function setContext($context)
    {
        if ($context instanceof iSContext)
            $this->context = $context;
        else
            $this->context = new BaseContext($context);

        return $this;
    }

    /**
     * Get Context Options
     *
     * @return iSContext
     */
    function getContext()
    {
        if (!$this->context)
            $this->setContext(new SocketContext);

        return $this->context;
    }

    /**
     * Open Socket Connection To Socket Uri
     *
     * - Initiates a stream or datagram connection to the
     *   destination specified by socketUri.
     *   The type of socket created is determined by the
     *   transport specified using standard URL formatting:
     *   transport://target
     *
     *   ! For Internet Domain sockets (AF_INET) such as
     *     TCP and UDP, the target portion of the socketUri
     *     parameter should consist of a hostname or IP address
     *     followed by a colon and a port number.
     *     For Unix domain sockets, the target portion should
     *     point to the socket file on the filesystem<commit_msg>implement add events with config builder<commit_after><?php
namespace Poirot\Stream;

use Poirot\Core\BuilderSetterTrait;
use Poirot\Core\ErrorStack;
use Poirot\Core\OpenCall;
use Poirot\Stream\Context\BaseContext;
use Poirot\Stream\Context\Socket\SocketContext;
use Poirot\Stream\Interfaces\Context\iSContext;
use Poirot\Stream\Interfaces\iSResource;
use Poirot\Stream\Interfaces\iStreamClient;

class StreamClient implements iStreamClient
{
    use BuilderSetterTrait;

    /**
     * @var string
     */
    protected $socketUri;

    /**
     * @var float
     */
    protected $timeout;

    /**
     * @var boolean
     */
    protected $persistent;

    /**
     * @var boolean
     */
    protected $noneBlocking;

    /**
     * @var iSContext
     */
    protected $context;

    /** @var iSResource */
    protected $_c__connectedResource;

    // Events ....
    /** @var OpenCall */
    protected $_on__resource_connected;

    /**
     * Construct
     *
     * Note: When specifying a numerical IPv6 address (e.g. fe80::1),
     *       you must enclose the IP in square brackets—for example,
     *       tcp://[fe80::1]:80
     *
     * @param string|array                   $socketUri Socket Uri or Array of Builder Settings
     * @param iSContext|array|resource|null  $context   Context Options
     */
    function __construct($socketUri, $context = null)
    {
        if (is_array($socketUri) && !empty($socketUri))
            $this->setupFromArray($socketUri);
        elseif (is_string($socketUri))
            $this->setSocketUri($socketUri);
        else
            throw new \InvalidArgumentException(sprintf(
                'StreamClient Construct give string or array of settings builder as first argument. given "%s".'
                , \Poirot\Core\flatten($socketUri)
            ));

        if ($context !== null) {
            if ($context instanceof iSContext)
                $this->setContext($context);
            else
                $this->getContext()->from($context);
        }
    }


    // ...

    /**
     * Add Closure Callable On Connected Resource
     *
     * - the closure functions will bind to this object
     *
     * closure:
     * function($resource) {
     *   // $this will point to StreamClient(current)
     * }
     *
     *
     * @return OpenCall
     */
    function whenResourceAvailable()
    {
        if (!$this->_on__resource_connected)
            $this->_on__resource_connected = new OpenCall($this);

        return $this->_on__resource_connected;
    }

    /**
     * Proxy to StreamClient::whenResourceAvailable for setupFromArray
     *
     * ['when_resource' =>
     *   ['method_name' => \Closure],
     *   ['method_name', \Closure],
     * ]
     *
     * @param $methods
     */
    protected function setWhenResource(array $methods)
    {
        if ( count($methods) <= 2 && array_filter($methods, function($item) { return is_callable($item); }) )
            ##! 'when_resource' => ['dump_debug' => function($resource) {k($resource);}]
            $methods = [$methods];

        foreach($methods as $method) {
            $name = null;
            if (is_array($method)) {
                if (count($method) === 2) {
                    ##! ['method_name', \Closure]
                    $name = $method[0]; $fn = $method[1];
                } elseif (array_values($method) !== $method) {
                    ##! ['method_name' => \Closure]
                    $name = key($method);
                    $fn = current($method);
                }
            }

            if ($name === null)
                throw new \InvalidArgumentException('Unknown Method Type Provided For '.\Poirot\Core\flatten($method));

            $this->whenResourceAvailable()->addMethod($name, $fn);
        }
    }

    /**
     * Set Socket Uri
     *
     * Note: When specifying a numerical IPv6 address (e.g. fe80::1),
     *       you must enclose the IP in square brackets—for example,
     *       tcp://[fe80::1]:80
     *
     * @param string $socketUri
     *
     * @return $this
     */
    function setSocketUri($socketUri)
    {
        $this->socketUri = $socketUri;

        return $this;
    }

    /**
     * Get Current Socket Uri That Stream Built With
     *
     * @return string
     */
    function getSocketUri()
    {
        return $this->socketUri;
    }

    /**
     * Set Default Base Context Options
     *
     * @param iSContext|array|resource $context
     *
     * @throws \InvalidArgumentException
     * @return $this
     */
    function setContext($context)
    {
        if ($context instanceof iSContext)
            $this->context = $context;
        else
            $this->context = new BaseContext($context);

        return $this;
    }

    /**
     * Get Default Base Context Options
     *
     * @return iSContext
     */
    function getContext()
    {
        if (!$this->context)
            $this->setContext(new SocketContext);

        return $this->context;
    }

    /**
     * Open Socket Connection To Socket Uri
     *
     * - Initiates a stream or datagram connection to the
     *   destination specified by socketUri.
     *   The type of socket created is determined by the
     *   transport specified using standard URL formatting:
     *   transport://target
     *
     *   ! For Internet Domain sockets (AF_INET) such as
     *     TCP and UDP, the target portion of the socketUri
     *     parameter should consist of a hostname or IP address
     *     followed by a colon and a port number.
     *     For Unix domain sockets, the target portion should
     *     point to the socket file on the filesystem
{'fraction_non_alphanumeric': 0.11163302018788726, 'fraction_numerical': 0.003997601439136518, 'mean_word_length': 1.932883939038687, 'pattern_counts': {'":': 0, '<': 6, '<?xml version=': 0, '>': 40, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '457973', 'n_tokens_mistral': 2890, 'n_tokens_neox': 2768, 'n_words': 731}