<?php

/**
 * @file
 * Contains archibald_stats_views_query.
 */

class archibald_stats_views_query extends views_plugin_query {

  protected $params = array();
  protected $filters = array();

  /**
   * @{inheritdoc}
   */
  function build(&$view) {
    // Store the view in the object to be able to use it later.
    $this->view = $view;

    $view->init_pager();

    // Let the pager modify the query to add limits.
    $this->pager->query();
  }

  /**
   * @{inheritdoc}
   */
  public function alter(&$view) {
    foreach (module_implements('views_query_alter') as $module) {
      $function = $module . '_views_query_alter';
      $function($view, $this);
    }
  }

  /**
   * @{inheritdoc}
   */
  public function execute(&$view) {
    try {
      $start = microtime(TRUE);

      // By default, we fetch data for all partners we know of.
      if (empty($this->params['partner_id'])) {
        $this->params['partner_id'] = array_keys(archibald_partner_load_all());
      }

      // By default, we aggregate by month.
      if (empty($this->params['aggregation_method'])) {
        $this->params['aggregation_method'] = 'month';
      }

      // If no dates are provided, fetch from today until 3 months ago.
      if (!isset($this->params['to'])) {
        $this->params['to'] = time();
      }
      if (!isset($this->params['from'])) {
        $this->params['from'] = strtotime('-3 months', strtotime($this->params['to']));
      }

      $result = array();
      $fields = $this->fields;
      foreach ($this->params['partner_id'] as $partner_id) {
        // Add the results to the array. The results don't contain the partner
        // ID, but if it's part of the fields, we add it now.
        $result = array_merge($result, array_map(function($item) use($fields, $partner_id) {
          if (in_array('partner', $fields)) {
            $item['partner'] = $partner_id;
          }
          return $item;
        }, archibald_stats_fetch(
          $partner_id,
          $this->params['from'],
          $this->params['to'],
          $this->params['aggregation_method'],
          isset($this->params['lom_id']) ? $this->params['lom_id'] : NULL,
          isset($this->limit) ? $this->limit : NULL,
          isset($this->offset) ? $this->offset : NULL
        )));
      }

      if (!empty($result)) {
        // Store raw response.
        $this->api_raw_results = $result;

        // Store results, casting them to objects.
        $view->result = array_map(function($item) {
          return (object) $item;
        }, $result);

        // We don't know how many pages we have. Always add another page,
        // regardless of where we are. If the current page is empty, or has
        // less rows than the limit, Views will automatically disable the "next"
        // link anyway.
        $view->total_rows = count($view->result) * ($this->pager->current_page + 2);

        // Update pager.
        $this->pager->total_items = $view->total_rows;
        $this->pager->update_page_info();
      }
    }
    catch (Exception $e) {
      $view->result = array();
      $view->total_rows = 0;

      $partner = archibald_partner_load($partner_id);
      watchdog('archibald_stats', "Could not fetch statistics for partner %name (ID: @id). Error: %e", array(
        '%name' => $partner->name,
        '@id' => $partner_id,
        '%e' => $e->getMessage(),
      ), WATCHDOG_ERROR);

      drupal_set_message($e->getMessage(), 'error');
    }

    $view->execute_time = microtime(TRUE) - $start;
  }

  /**
   * @{inheritdoc}
   */
  function add_relationship($left_table, $left_field, $right_table, $right_field) {
    $this->ensure_table($right_table);
    $this->add_field($left_table, $left_field);
    $this->joins[$left_table][$left_field] = array(
      'table' => $right_table,
      'field' => $right_field,
    );
    return FALSE;
  }

  /**
   * @{inheritdoc}
   */
  public function add_field($table_alias, $field, $alias = '', $params = array()) {
    // Make sure an alias is assigned.
    $alias = $alias ? $alias : $field;
    $this->fields[$alias] = $field;
    return $alias;
  }

  /**
   * @{inheritdoc}
   * @noop
   */
  function add_where($group, $field, $value = NULL, $operator = NULL) { }

  /**
   * @{inheritdoc}
   * @noop
   */
  function set_where_group($type = 'AND', $group = NULL, $where = 'where') {
    return NULL;
  }

  /**
   * @{inheritdoc}
   * @noop
   */
  function ensure_table($table, $relationship = NULL, $join = NULL) { }

  /**
   * Add a parameter for the request.
   *
   * @param string $name
   *    The name of the parameter to add, like "partner_id" or "from".
   * @param * $value
   *    The value of the parameter.
   */
  public function add_parameter($name, $value) {
    $this->params[$name] = $value;
  }

  /**
   * Add a filter for the request.
   *
   * @param string $name
   *    The name of the filter to add.
   * @param callable $callback
   *    The callback that must be applied to the result set in order to filter
   *    it. Check array_filter() for more information.
   */
  public function add_filter($name, $callback) {
    $this->filters[$name] = $callback;
  }

}
