#!/usr/bin/php
<?php
namespace ICT\Core\Cli;

use Firehed\ProcessControl\Daemon;
use ICT\Core\Campaign;
use ICT\Core\CoreException;
use ICT\Core\Corelog;
use ICT\Core\Group;
use ICT\Core\Program;
use ICT\Core\Transmission;

require dirname(__DIR__).'/vendor/autoload.php'; // composer
declare(ticks=1);

$campaign_id = $argv[1];
$action = $argv[2];

/* forking */
chdir(__DIR__);
$daemon = new Daemon();
$daemon->setPidFileLocation("../cache/coreCampaign_$campaign_id.pid");
$daemon->setProcessName("coreCampaign_$campaign_id");
$daemon->setStderrFileLocation("../log/campaign_$campaign_id.log");
$daemon->setStdoutFileLocation("../log/campaign_$campaign_id.log");
$daemon->autoRun();

// parent close database conection that y i put here
require_once dirname(__FILE__).'/../core/core.php';

// Set campaign_1.log as target logfile
Corelog::set_file("campaign_$campaign_id.log");


try {
  $campaignDaemon = new CampaignCli($campaign_id);
  switch ($action) {
    case 'start':
      $campaignDaemon->start();
      break;
    case 'stop':
      $campaignDaemon->signal_stop(0);
      break;
    case 'reload':
    default:
      $campaignDaemon->signal_reload(0);
  }

  Corelog::log('Campaign processing completed', Corelog::INFO);

} catch (CoreException $e) {
  Corelog::log('Campaign failed. Error: ' . $e->getMessage(), Corelog::ERROR);
  exit($e->getMessage());
}

Corelog::log('All Done', Corelog::INFO);
exit(0);

/**
 * ******************************************************* CampaignCli class **
 */

class CampaignCli
{
  /**
   * @var int $campaign_id
   */
  private $campaign_id = null;

  /**
   * @var Campaign $oCampaign
   */
  private $oCampaign = null;

  /**
   * @var boolean $is_active
   */
  private $is_active = false;

  /**
   * Delay between each transmission
   * @var integer $delay
   */
  private $delay = 1000000; // one second

  public function __construct($campaign_id)
  {
    $this->campaign_id = $campaign_id;
    $this->load();

    // set stop signal handler
    pcntl_signal(SIGQUIT, array($this, "signal_stop"));
    pcntl_signal(SIGABRT, array($this, "signal_stop"));
    /* produce error  pcntl_signal(SIGKILL, array($this, "signal_stop")); */
    pcntl_signal(SIGTERM, array($this, "signal_stop"));

    // set reload signal handler
    pcntl_signal(SIGHUP, array($this, "signal_reload"));
    pcntl_signal(SIGINT, array($this, "signal_reload"));
    pcntl_signal(SIGUSR1, array($this, "signal_reload"));
    pcntl_signal(SIGUSR2, array($this, "signal_reload"));
  }

  public function signal_reload($signo, $signinfo = null)
  {
    Corelog::log('Reload signal received. signal no: ' . $signo, Corelog::INFO, $signinfo);
    $this->load();
  }

  public function signal_stop($signo, $signinfo = null)
  {
    Corelog::log('Stop signal received. signal no: ' . $signo, Corelog::INFO, $signinfo);
    $this->is_active = false;
  }

  public function load()
  {
    $this->oCampaign = new Campaign($this->campaign_id);
    if (Campaign::STATUS_BROKEN == $this->oCampaign->status) {
      throw new CoreException(423, 'Broken / invalid campaign');
    } else if (Campaign::STATUS_COMPLETED == $this->oCampaign->status) {
      throw new CoreException(423, 'Campaign already completed');
    }
    \ICT\Core\do_login($this->oCampaign->user_id);

    $cpm = empty($this->oCampaign->cpm) ? 2 : $this->oCampaign->cpm;
    $this->delay = (1000000 * 60) / $cpm;
  }

  public function prepare()
  {
    $oProgram = Program::load($this->oCampaign->program_id);
    $oGroup = new Group($this->oCampaign->group_id);
    $listContact = $oGroup->search_contact();
    $count = 0;
    foreach ($listContact as $aContact) {
      try {
        $this->prepare_transmission($oProgram, $aContact['contact_id']);
        $count++;
      } catch (CoreException $e) {
        Corelog::log('Unable to create transmission skipping. error:' . $e->getMessage(), Corelog::WARNING);
      }
    }

    $this->oCampaign->status = Campaign::STATUS_READY;
    $this->oCampaign->save();

    return $count;
  }

  private function prepare_transmission(Program $oProgram, $contact_id)
  {
    $oTransmission = $oProgram->transmission_create($contact_id, $this->oCampaign->account_id, Transmission::OUTBOUND);
    $oTransmission->campaign_id = $this->oCampaign->campaign_id;
    if ($oTransmission->save()) {
      return $oTransmission->transmission_id;
    } else {
      throw new CoreException(417, 'Transmission creation failed');
    }
  }

  public function start()
  {
    if (Campaign::STATUS_RUNNING == $this->oCampaign->status) {
      throw new CoreException(423, 'Campaign already running');
    }

    // set campaign in running mode
    $this->is_active = true;

    if (Campaign::STATUS_NEW == $this->oCampaign->status) {
      $this->prepare();
    }

    // also update campaign status
    $this->oCampaign->status = Campaign::STATUS_RUNNING;
    $this->oCampaign->save();

    // search and process all associated transmissions
    $aFilter = array(
        'campaign_id' => $this->oCampaign->campaign_id,
        'status' => Transmission::STATUS_PENDING
    );
    $listTransmission = Transmission::search($aFilter);
    $final_status = $this->process($listTransmission);

    // Campaign stopped, now update campaign status
    $this->oCampaign->status = $final_status;
    $this->oCampaign->save();
  }

  private function process($listTransmission = array())
  {
    $recent_attempt = round(microtime(true) * 1000);

    foreach ($listTransmission as $aTransmission) {

      // Stop processing if campaign is not active
      if ($this->is_active == false) {
        return Campaign::STATUS_PAUSED;
      }

      // delay is required for system stability
      $current_time = round(microtime(true) * 1000);
      usleep($this->delay - ($current_time - $recent_attempt));
      $recent_attempt = $current_time;

      try { // now send the transmission, in case of error simply skip that transmission
        Corelog::log('Sending transmission with id: ' . $aTransmission['transmission_id'], Corelog::INFO);
        $this->transmission_send($aTransmission['transmission_id']);
      } catch (CoreException $e) {
        Corelog::log('Unable to send transmission skipping. error:' . $e->getMessage(), Corelog::WARNING);
      }
    }

    return Campaign::STATUS_COMPLETED;
  }

  private function transmission_send($transmission_id)
  {
    $oTransmission = new Transmission($transmission_id);
    return $oTransmission->send();
  }
}
