<?php
/**
 * @file
 * Drush commands for Class Grapher
 *
 * - hier: plot class inheritance on a site
 *
 * - Idea: use Javascript Infovis Toolkit instead of GraphViz for nicer
 *   graphs, less server load, and user interaction.
 */

use \OSInet\Class_Grapher\Logger;
use \OSInet\Class_Grapher\Graph;

/**
 * Implement hook_drush_command().
 *
 * Declare classgraph (hier) command.
 */
function classgrapher_drush_command() {
  $items = array();

  $items['classgraph'] = array(
    'description' => 'Graph PHP entities',
    'aliases' => array('hier'),
    'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
    // libraries_load() does not exist on 6.x. PSR0 is bundled in 8.x.
    'core' => array('7'),
    'drupal dependencies' => array('libraries'),
    'arguments' => array(
      'base' => 'The base directory below which to parse source files.',
    ),
    'options' => array(
      'cgdebug' => array(
        'description' => 'Debug level: 0 to 7. Default to 6.',
      ),
      'cgimager' => array(
        'description' => 'Imager to use. Default to "dot". Use "dump" for a debug dump without GraphViz generation. Alternate imagers like circo, (s)fdp or twopi are converted to dot.',
        'example-value' => 'dump,dot,neato',
      ),
      'cgformat' => array(
        'description' => 'Image format. Default to "svg". Not available with the dump imager.',
        'example-value' => implode(',', _drush_classgrapher_get_formats()),
      ),
    ),
  );

  return $items;
}

/**
 * Helper to enumerate GraphViz format filters on the current system.
 *
 * @return array
 *   An array of format names.
 */
function _drush_classgrapher_get_formats() {
  $dotCommand = 'dot -Tinvalid';
  $descriptorSpec = array(
      0 => array('pipe', 'r'),
      1 => array('pipe', 'w'),
      2 => array('pipe', 'w'),
  );

  $process = proc_open($dotCommand, $descriptorSpec, $pipes, NULL, NULL);
  if (!is_resource($process)) {
    drush_set_error('classgrapher', 'GraphViz not found.');
  }

  fclose($pipes[0]);
  fclose($pipes[1]);
  $stderr = stream_get_contents($pipes[2]);
  proc_close($process);

  $sts = preg_match('/(.+):( .* )*/', $stderr, $matches);
  if (!$sts || count($matches) != 3) {
    drush_set_error('classgrapher', 'GraphViz did not return a usable formats list.');
  }
  $formats = explode(' ', trim($matches[2]));
  return $formats;
}

/**
 * Validation callback for classgraph.
 */
function drush_classgrapher_classgraph_validate() {
  // Libraries 7.x-1.x does not include this function, and Drush
  // 'drupal dependencies' can not require a specific branch.
  if (function_exists('libraries_load')) {
    $info = libraries_load('grammar_parser');
  }
  else {
    drush_set_error('classgrapher', 'Libraries 7.x-2.x needed.');
  }

  // Add this library's path to the include path to use PSR0 autoloading.
  $path = explode(':', ini_get('include_path'));
  $path[] = realpath(__DIR__ . '/../..');
  ini_set('include_path', implode(':', $path));

  // Drupal 7 does not include a PSR0 autoloader: use ours.
  include 'misc/psr0.php';
  spl_autoload_register('psr0_autoload');

  // PEAR classes can be included by any PSR0 autoloader, but error messages are
  // less clear, as the autoloader cannot know this is a PEAR class, and the
  // autoloading must be performed from non-namespaced code, or from a dual-
  // compatible autoloader (PSR0 + PEAR), so this is more reliable.
  if (!@include_once 'Image/GraphViz.php') {
    drush_set_error('classgrapher', 'PEAR Image_Graphviz not found.');
  }
}

/**
 * Command callback for classgraph.
 */
function drush_classgrapher_classgraph($base) {
  $optionDefaults = array(
    'imager' => 'dot',
    'debug' => WATCHDOG_INFO,
    'format' => 'svg',
  );

  foreach ($optionDefaults as $name => $default) {
    $$name = drush_get_option("cg$name", $default);
  }

  $graph = new Graph($base, new Logger($debug));
  echo $graph->build($imager, $format);
}