/**
  * This method is the one in charge of executing a step of a specific
  * item, analyze results and set the next step or errors.
  *
  * @param \TooBasic\Workflows\ItemFlowRepresentation $flow Item flow to be
  * executed.
  * @param \TooBasic\Workflows\Item $item Item to be analyzed
  * @return boolean Returns TRUE the current step pointed the execution of
  * a next step and the flow is not in a stopping status.
  * @throws \TooBasic\Workflows\WorkflowsException
  */
 public function runFor(ItemFlowRepresentation $flow, Item $item)
 {
     //
     // Default values.
     $continue = true;
     $logParams = array('workflow' => $flow->workflow);
     //
     // Loading all required settings.
     $this->load();
     //
     // Global dependencies.
     global $WKFLDefaults;
     //
     // Guessing the current step name.
     $currentStep = $flow->step;
     if (!$currentStep) {
         //
         // Getting the fist step from workflow when there's none.
         $currentStep = $this->_config->startsAt;
         $flow->step = $currentStep;
         $flow->persist();
     }
     $this->_log->log(LGGR_LOG_LEVEL_INFO, "Current step: '{$currentStep}'", $logParams);
     //
     // Shortcuts.
     $stepConfig = $this->_config->steps->{$currentStep};
     $stepPath = Paths::Instance()->customPaths($WKFLDefaults[WKFL_DEFAULTS_PATHS][WKFL_DEFAULTS_PATH_STEPS], Names::SnakeFilename($stepConfig->manager), Paths::ExtensionPHP);
     $stepClass = Names::ClassNameWithSuffix($stepConfig->manager, WKFL_CLASS_SUFFIX_STEP);
     $this->_log->log(LGGR_LOG_LEVEL_DEBUG, "        path: '{$stepPath}'", $logParams);
     $this->_log->log(LGGR_LOG_LEVEL_DEBUG, "       class: '{$stepClass}'", $logParams);
     $this->_log->log(LGGR_LOG_LEVEL_DEBUG, '      config: ' . json_encode($stepConfig), $logParams);
     //
     // Checking if the step's class is already loaded.
     if (!class_exists($stepClass)) {
         //
         // Checking class code file.
         if (!$stepPath || !is_readable($stepPath)) {
             $msg = "Unable to access class for step '{$currentStep}'.";
             $this->_log->log(LGGR_LOG_LEVEL_FATAL, $msg, $logParams);
             throw new WorkflowsException($msg);
         }
         require_once $stepPath;
     }
     //
     // Checking class existence.
     if (!class_exists($stepClass)) {
         $msg = "Class '{$stepClass}' not defined.";
         $this->_log->log(LGGR_LOG_LEVEL_FATAL, $msg, $logParams);
         throw new WorkflowsException($msg);
     }
     //
     // Getting a step execution class.
     $step = new $stepClass($item);
     //
     // Sharing log pointer.
     $step->setLog($this->_log);
     //
     // Step execution
     $step->execute();
     //
     // Reloading item to avoid outdated data.
     $item->reload();
     //
     // Checking that thers a connection configuration for the current
     // item status.
     $connection = false;
     if (isset($stepConfig->connections->{$item->status()})) {
         $connection = $stepConfig->connections->{$item->status()};
     } else {
         $this->_log->log(LGGR_LOG_LEVEL_ERROR, "Unkwnon connection for item status '{$item->status()}'.", $logParams);
         $continue = false;
         $flow->status = WKFL_ITEM_FLOW_STATUS_FAILED;
         $flow->persist();
     }
     //
     // Checking if there's a connection configuration to analyze.
     if ($connection) {
         //
         // Checking what action should be taken.
         if (isset($connection->step) || isset($connection->status)) {
             //
             // Setting the next step to take.
             if (isset($connection->step)) {
                 $flow->step = $connection->step;
             }
             //
             // Setting new flows status.
             if (isset($connection->status)) {
                 $flow->status = $connection->status;
                 $continue = false;
             }
         } else {
             $this->_log->log(LGGR_LOG_LEVEL_ERROR, "Neither step not status configuration for connection on status '{$item->status()}'.", $logParams);
             $continue = false;
             $flow->status = WKFL_ITEM_FLOW_STATUS_FAILED;
         }
         //
         // If the flow has been set to wait, it should check if
         // the waiting has conditions.
         if ($flow->status == WKFL_ITEM_FLOW_STATUS_WAIT && isset($connection->wait)) {
             //
             // Checking configuration.
             if (!isset($connection->wait->attempts) || !isset($connection->wait->status)) {
                 $this->_log->log(LGGR_LOG_LEVEL_ERROR, "Waiting configuration is incorrect.", $logParams);
                 $continue = false;
                 $flow->status = WKFL_ITEM_FLOW_STATUS_FAILED;
             } else {
                 //
                 // Incrising the attempts count.
                 $flow->attempts++;
                 //
                 // Checking it already tried to many
                 // times.
                 if ($flow->attempts > $connection->wait->attempts) {
                     //
                     // Changing flow status and
                     // resetting counts.
                     $flow->status = $connection->wait->status;
                     $flow->attempts = 0;
                     $this->_log->log(LGGR_LOG_LEVEL_INFO, "Maximum attempts reached ({$connection->wait->attempts} attempts), changing flow status to '{$connection->wait->status}'.", $logParams);
                 } else {
                     $this->_log->log(LGGR_LOG_LEVEL_INFO, "Increasing attempts to {$flow->attempts}.", $logParams);
                 }
             }
         } else {
             //
             // The rest of the status keep the attempts to
             // zero.
             $flow->attempts = 0;
         }
         //
         // Saving flow status.
         $flow->persist();
         $this->_log->log(LGGR_LOG_LEVEL_INFO, "Next step: '{$flow->step}'. Next flow status: '{$flow->status}'.", $logParams);
     }
     return $continue;
 }