<?php

namespace Illuminate\Redis;

use Closure;
use Predis\Client;
use Illuminate\Support\Arr;
use Illuminate\Contracts\Redis\Database as DatabaseContract;
// this is Redis namespace , father is Database Contract
class Database implements DatabaseContract
{// class Database implements Database Contract
    /**
     * The host address of the database.
     *
     * @var array
     */
    protected $clients;// The host address of the database.
   // host not hot , ok
   // host address is like 127.0.0.1

    /**
     * Create a new Redis connection instance.
     *
     * @param  array  $servers
     * @return void
     */
    public function __construct(array $servers = [])
    {
        $cluster = Arr::pull($servers, 'cluster');// cluster is lots of servers

        $options = (array) Arr::pull($servers, 'options');// pull servers
       // get options

        if ($cluster) {// if it is a cluster ,use first way to make it
            $this->clients = $this->createAggregateClient($servers, $options);
        } else {// other way
            $this->clients = $this->createSingleClients($servers, $options);
        }
    }// Create a new Redis connection instance

    /**
     * Create a new aggregate client supporting sharding.
     *
     * @param  array  $servers
     * @param  array  $options
     * @return array
     */
    protected function createAggregateClient(array $servers, array $options = [])
    {
        return ['default' => new Client(array_values($servers), $options)];
       // set the with server and options
    }//Create a new aggregate client supporting sharding

    /**
     * Create an array of single connection clients.
     *
     * @param  array  $servers
     * @param  array  $options
     * @return array
     */
    protected function createSingleClients(array $servers, array $options = [])
    {
        $clients = [];// create Single Clients make clients

        foreach ($servers as $key => $server) {// a Single Client
            $clients[$key] = new Client($server, $options);
        }

        return $clients;
    }// make a single Clients

    /**
     * Get a specific Redis connection instance.
     *
     * @param  string  $name
     * @return \Predis\ClientInterface|null
     */
    public function connection($name = 'default')
    {
        return Arr::get($this->clients, $name ?: 'default');
    }//Get a specific Redis connection instance.
   // Get a instance about you  self want

    /**
     * Run a command against the Redis database.
     *
     * @param  string  $method
     * @param  array   $parameters
     * @return mixed
     */
    public function command($method, array $parameters = [])
    {
        return call_user_func_array([$this->clients['default'], $method], $parameters);
    }// Run a command against the Redis database
   // use this method call_user_func_array

    /**
     * Subscribe to a set of given channels for messages.
     *
     * @param  array|string  $channels
     * @param  \Closure  $callback
     * @param  string  $connection
     * @param  string  $method
     * @return void
     */
    public function subscribe($channels, Closure $callback, $connection = null, $method = 'subscribe')
    {
        $loop = $this->connection($connection)->pubSubLoop();// loop

        call_user_func_array([$loop, $method], (array) $channels);// call user func array

        foreach ($loop as $message) {
            if ($message->kind === 'message' || $message->kind === 'pmessage') {
                call_user_func($callback, $message->payload, $message->channel);// call_user_func
            }
        }// loop like message

        unset($loop);
    }//Subscribe

    /**
     * Subscribe to a set of given channels with wildcards.
     *
     * @param  array|string  $channels
     * @param  \Closure  $callback
     * @param  string  $connection
     * @return void
     */
    public function psubscribe($channels, Closure $callback, $connection = null)
    {
        return $this->subscribe($channels, $callback, $connection, __FUNCTION__);
    }// Subscribe to a set of given channels with wildcards

    /**
     * Dynamically make a Redis command.
     *
     * @param  string  $method
     * @param  array   $parameters
     * @return mixed
     */
    public function __call($method, $parameters)
    {
        return $this->command($method, $parameters);
    }// very good method,
}