<?php

namespace Illuminate\Console\Scheduling;

use Closure;
use Carbon\Carbon;
use LogicException;
use Cron\CronExpression;
use GuzzleHttp\Client as HttpClient;
use Illuminate\Contracts\Mail\Mailer;
use Symfony\Component\Process\Process;
use Symfony\Component\Process\ProcessUtils;
use Illuminate\Contracts\Container\Container;
// namespace
class Event
{
    /**
     * The command string.
     *
     * @var string
     */
    public $command;// The command string

    /**
     * The cron expression representing the event's frequency.
     *
     * @var string
     */
    public $expression = '* * * * * *';
    // The cron expression representing the event's frequency.
    /**
     * The timezone the date should be evaluated on.
     *
     * @var \DateTimeZone|string
     */
    public $timezone;
    // The timezone the data should be evaluated on.
    /**
     * The user the command should run as.
     *
     * @var string
     */
    public $user;// The user the command should run as.

    /**
     * The list of environments the command should run under.
     *
     * @var array
     */
    public $environments = [];// The list of environments the command should run under.

    /**
     * Indicates if the command should run in maintenance mode.
     *
     * @var bool
     */
    public $evenInMaintenanceMode = false;// Indicate if the command should run in maintenance mode.


    /**
     * Indicates if the command should not overlap itself.
     *
     * @var bool
     */
    public $withoutOverlapping = false;// Indicates if the command should not overlap itself

    /**
     * The array of filter callbacks.
     *
     * @var array
     */
    protected $filters = [];// The array of filter callbacks

    /**
     * The array of reject callbacks.
     *
     * @var array
     */
    protected $rejects = [];// The array of reject callbacks

    /**
     * The location that output should be sent to.
     *
     * @var string
     */
    public $output = '/dev/null'; //The location that output should be sent to.

    /**
     * Indicates whether output should be appended.
     *
     * @var bool
     */
    protected $shouldAppendOutput = false;// Indicates whether output should be appended.

    /**
     * The array of callbacks to be run before the event is started.
     *
     * @var array
     */
    protected $beforeCallbacks = [];// The array of callbacks to be run before the event is started.

    /**
     * The array of callbacks to be run after the event is finished.
     *
     * @var array
     */
    protected $afterCallbacks = [];// The array of callbacks to be run after the event is finished

    /**
     * The human readable description of the event.
     *
     * @var string
     */
    public $description;// The human readable description of the event.

    /**
     * Create a new event instance.
     *
     * @param  string  $command
     * @return void
     */
    public function __construct($command)
    {
        $this->command = $command;// set command
        $this->output = $this->getDefaultOutput();// output this path
    }// create a new event instance.

    /**
     * Get the default output depending on the OS.
     *
     * @return string
     */
    protected function getDefaultOutput()
    {
        return (DIRECTORY_SEPARATOR == '\\') ? 'NUL' : '/dev/null';
    }// get the default output depending on the OS[linux/windows]
   // back the difference path to the difference os

    /**
     * Run the given event.
     *
     * @param  \Illuminate\Contracts\Container\Container  $container
     * @return void
     */
    public function run(Container $container)
    {
        $this->runCommandInForeground($container);
    }// Run the given event.
   // use api function runCommandInForeground

    /**
     * Run the command in the foreground.
     *
     * @param  \Illuminate\Contracts\Container\Container  $container
     * @return void
     */
    protected function runCommandInForeground(Container $container)
    {
        $this->callBeforeCallbacks($container);// call Before Call backs

        (new Process(
            trim($this->buildCommand(), '& '), base_path(), null, null, null
        ))->run();// too complex ,

        $this->callAfterCallbacks($container);
    }//Run the command in the foreground

    /**
     * Call all of the "before" callbacks for the event.
     *
     * @param  \Illuminate\Contracts\Container\Container  $container
     * @return void
     */
    protected function callBeforeCallbacks(Container $container)
    {
        foreach ($this->beforeCallbacks as $callback) {
            $container->call($callback);
        }
    }// use the instance call the difference method

    /**
     * Call all of the "after" callbacks for the event.
     *
     * @param  \Illuminate\Contracts\Container\Container  $container
     * @return void
     */
    protected function callAfterCallbacks(Container $container)
    {
        foreach ($this->afterCallbacks as $callback) {
            $container->call($callback);
        }
    }// same to the before, use a way to set the order.

    /**
     * Build the command string.
     *
     * @return string
     */
    public function buildCommand()
    {
        $output = ProcessUtils::escapeArgument($this->output);// a static method to path about the difference os

        $redirect = $this->shouldAppendOutput ? ' >> ' : ' > ';// this not ready for windows.

        if ($this->withoutOverlapping) {
            $command = '(touch '.$this->mutexPath().'; '.$this->command.'; rm '.$this->mutexPath().')'.$redirect.$output.' 2>&1 &';
        } else {
            $command = $this->command.$redirect.$output.' 2>&1 &';
        }// use in shell

        return $this->user ? 'sudo -u '.$this->user.' '.$command : $command;// use for ubuntu server
    }// Build the command string.

    /**
     * Get the mutex path for the scheduled command.
     *
     * @return string
     */
    protected function mutexPath()
    {
        return storage_path('framework/schedule-'.sha1($this->expression.$this->command));
    }// Get the mutex path for the scheduled command.
   // get the different path even for the same person

    /**
     * Determine if the given event should run based on the Cron expression.
     *
     * @param  \Illuminate\Contracts\Foundation\Application  $app
     * @return bool
     */
    public function isDue($app)
    {
        if (! $this->runsInMaintenanceMode() && $app->isDownForMaintenance()) {
            return false;
        }// if runs

        return $this->expressionPasses() &&
               $this->runsInEnvironment($app->environment());
    }// Determine if the given event should run based on the Cron expression.

    /**
     * Determine if the Cron expression passes.
     *
     * @return bool
     */
    protected function expressionPasses()
    {
        $date = Carbon::now();// get Data

        if ($this->timezone) {
            $date->setTimezone($this->timezone);
        }// setTimezo
       // use a factory.
        return CronExpression::factory($this->expression)->isDue($date->toDateTimeString());
    }// Determine if the cron expression passes.