<?php
// $Id: api.module,v 1.26 2006/08/10 02:58:46 JonBob Exp $

/**
 * @file
 * Generates and displays API documentation pages.
 *
 * This is an implementation of a subset of the Doxygen documentation generator
 * specification, tuned to produce output that best benefits the Drupal code base.
 * It is designed to assume the code it documents follows Drupal coding conventions,
 * and supports the following Doxygen constructs:
 *   @ mainpage
 *   @ file
 *   @ defgroup
 *   @ ingroup
 *   @ addtogroup (as a synonym of @ ingroup)
 *   @ param
 *   @ return
 *   @ link
 */

/**
 * Implementation of hook_help().
 */
function api_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('Generates and displays API documentation pages.');
  }
}

/**
 * Implementation of hook_menu().
 */
function api_menu($may_cache) {
  $items = array();
  $access = user_access('access content');

  if ($may_cache) {
    $items[] = array('path' => 'api', 'title' => t('API reference'),
      'access' => user_access('access content'),
      'callback' => 'api_page_main');

    $result = db_query('SELECT branch_name, title FROM {api_branch}');
    while ($branch = db_fetch_object($result)) {
      $items[] = array('path' => 'api/'. $branch->branch_name, 'title' => $branch->title,
        'callback' => 'api_page_branch', 'access' => $access,
        'callback arguments' => array($branch->branch_name));
      $items[] = array('path' => 'api/'. $branch->branch_name .'/function_dump', 'title' => t('functions'),
        'callback' => 'api_page_function_dump', 'access' => $access,
        'callback arguments' => array($branch->branch_name),
        'type' => MENU_CALLBACK);
      $items[] = array('path' => 'api/'. $branch->branch_name .'/function', 'title' => t('functions'),
        'callback' => 'api_page_listing', 'access' => $access,
        'callback arguments' => array($branch->branch_name, 'function'));
      $items[] = array('path' => 'api/'. $branch->branch_name .'/constant', 'title' => t('constants'),
        'callback' => 'api_page_listing', 'access' => $access,
        'callback arguments' => array($branch->branch_name, 'constant'));
      $items[] = array('path' => 'api/'. $branch->branch_name .'/file', 'title' => t('files'),
        'callback' => 'api_page_listing', 'access' => $access,
        'callback arguments' => array($branch->branch_name, 'file'));
      $items[] = array('path' => 'api/'. $branch->branch_name .'/group', 'title' => t('topics'),
        'callback' => 'api_page_listing', 'access' => $access,
        'callback arguments' => array($branch->branch_name, 'group'));
        
      $items[] = array('path' => 'apis', 'title' => t('api search'),
        'callback' => 'api_search_listing', 'access' => $access,
        'type' => MENU_CALLBACK);
         
    }
    $items[] = array('path' => 'api/autocomplete',
        'callback' => 'api_autocomplete', 'access' => $access,
        'type' => MENU_CALLBACK);
    $items[] = array('path' => 'admin/api', 'title' => t('API reference'),
      'access' => user_access('administer API reference'),
      'callback' => 'api_page_admin');
  }
  else {
    if (arg(0) == 'api') {
      if (is_string(arg(3))) {
        $items[] = array('path' => 'api/'. arg(1) .'/function/'. arg(3), 'title' => t('function'),
          'callback' => 'api_page_function', 'access' => $access,
          'callback arguments' => array(arg(1), arg(3)),
          'type' => MENU_CALLBACK);
        $items[] = array('path' => 'api/'. arg(1) .'/function/'. arg(3) .'/documentation', 'title' => t('view documentation'),
          'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
        $items[] = array('path' => 'api/'. arg(1) .'/function/'. arg(3) .'/references', 'title' => t('list references'),
          'callback' => 'api_page_function_references', 'access' => $access,
          'callback arguments' => array(arg(1), arg(3)),
          'type' => MENU_LOCAL_TASK);
      }

      if (is_string(arg(3))) {
        $items[] = array('path' => 'api/'. arg(1) .'/constant/'. arg(3), 'title' => t('constant'),
          'callback' => 'api_page_constant', 'access' => $access,
          'callback arguments' => array(arg(1), arg(3)),
          'type' => MENU_CALLBACK);
      }

      if (strpos($_GET['q'], 'api/'. arg(1) .'/file/') !== FALSE) {
        $matches = array();
        preg_match('!(api/'. arg(1) .'/file/)(.*?)(/source|/file|$)!', $_GET['q'], $matches);
        $main_url = $matches[1] . $matches[2];
        $file_name = $matches[2];

        $items[] = array('path' => $main_url, 'title' => t('file'),
          'callback' => 'api_page_file', 'access' => $access,
          'callback arguments' => array(arg(1), $file_name),
          'type' => MENU_CALLBACK);
        $items[] = array('path' => $main_url .'/documentation', 'title' => t('view documentation'),
          'type' => MENU_DEFAULT_LOCAL_TASK, 'weight' => -10);
        $items[] = array('path' => $main_url .'/source', 'title' => t('view source'),
          'callback' => 'api_page_file_source', 'access' => $access,
          'callback arguments' => array(arg(1), $file_name),
          'type' => MENU_LOCAL_TASK);
      }

      if (is_string(arg(3))) {
        $items[] = array('path' => 'api/'. arg(1) .'/group/'. arg(3), 'title' => t('topic'),
          'callback' => 'api_page_group', 'access' => $access,
          'callback arguments' => array(arg(1), arg(3)),
          'type' => MENU_CALLBACK);
      }
    }
  }

  return $items;
}

/**
 * Implementation of hook_perm().
 */
function api_perm() {
  return array('administer API reference');
}

/**
 * Menu callback; displays a branch listing.
 */
function api_page_main() {
  // if you've gotten here, but there is another argument to the url
  // we'll assume that it's a search
  if(arg(1)){
    if(arg(2)){
      return api_search_listing(arg(1), arg(2));
    }
    else {
      return api_search_listing(variable_get('api_default_branch', 'HEAD'), arg(1));
    }
  }
  $output = '';

  $result = db_query('SELECT branch_name, title FROM {api_branch}');
  while ($branch = db_fetch_object($result)) {
    $form = array();
    $form[$branch->branch_name] = array(
      '#type' => 'fieldset',
      '#title' => $branch->title,
    );
    $form[$branch->branch_name]['branch_name'] = array(
      '#type' => 'value',
      '#value' => $branch->branch_name,
    );
    $form[$branch->branch_name]['search'] = array(
      '#title' => t('Function, file or topic name'),
      '#type' => 'textfield',
      '#autocomplete_path' => 'api/autocomplete/'. $branch->branch_name, 
      '#default_value' => '',
      '#required' => TRUE,
    );
    $form[$branch->branch_name]['submit'] = array(
      '#type' => 'submit',
      '#value' => t('Look up'),
    );
    $output .= drupal_get_form('api_page_main_'. $branch->branch_name, $form, 'api_page_main');
  }
  return $output;
}

function api_page_main_submit($form_id, $form_values) {
  drupal_goto('api/'. $form_values['branch_name'] .'/'. $form_values['search']);
}

/**
 * Menu callback; perform a global search for documentation.
 */
function api_search_listing($branch_name, $search_text = ''){
  if($search_text == ''){
    $search_text = $branch_name;
    $branch_name = variable_get('api_default_branch', 'HEAD');
  }
  
  drupal_set_title(t('API Search for &ldquo;%search&rdquo; in %branch', array('%search' => $search_text, '%branch' => $branch_name)));

  // Exact match.
  $result = db_query("SELECT * FROM {api_documentation} WHERE branch_name = '%s' AND object_name = '%s'", $branch_name, $search_text);
  if (!$count = db_num_rows($result)) {
    // Wildcard search.
    $result = db_query("SELECT * FROM {api_documentation} WHERE branch_name = '%s' AND object_name LIKE '%%%s%%'", $branch_name, $search_text);
    $count = db_num_rows($result);
  };
  
  switch($count){
    case 0:
      return t("Search found no results");
      
    case 1:
      $item = db_fetch_object($result);
      drupal_goto("api/$item->branch_name/$item->object_type/$item->object_name");
      break;
      
    default:
      
      $result = pager_query("SELECT * FROM {api_documentation} WHERE branch_name = '%s' AND object_name LIKE '%%%s%%'". tablesort_sql(_api_listing_headers()), 50, 0, NULL, $branch_name, $search_text);
      return api_render_listing($result);
  }
}

/**
 * Prepare a listing of potential documentation matches for a branch.
 */
function api_autocomplete($branch_name, $search){
  $matches = array();
  $result = db_query("SELECT object_name FROM {api_documentation} WHERE object_name LIKE '%%%s%%' AND branch_name = '%s' ORDER BY LENGTH(object_name) LIMIT 20", $search, $branch_name);
  while ($r = db_fetch_object($result)) {
    $matches[$r->object_name] = check_plain($r->object_name);
  }
  print drupal_to_js($matches);
  exit();
}

/**
 * Menu callback; displays the main documentation page.
 */
function api_page_branch($branch_name) {
  $result = db_query("SELECT documentation FROM {api_documentation} WHERE object_name = '%s' AND branch_name = '%s' AND object_type = 'mainpage'", $branch_name, $branch_name);
  if ($branch = db_fetch_object($result)) {
    return api_link_documentation($branch->documentation, $branch_name);
  }
  else {
    drupal_not_found();
  }
}

/**
 * Menu callback; displays an object listing.
 */
function api_page_listing($branch_name, $object_type) {
  $result = pager_query("SELECT * FROM {api_documentation} WHERE branch_name = '%s' AND object_type = '%s'". tablesort_sql(_api_listing_headers()), 50, 0, NULL, $branch_name, $object_type);
  return api_render_listing($result);

}

function _api_listing_headers(){
  $headers = array();
  $headers[] = array('data' => t('Name'), 'field' => 'title');
  $headers[] = array('data' => t('Location'), 'field' => 'file_name');
  $headers[] = t('Description');
  return $headers;
}

/**
 * Render a table with an overview of documentation objects.
 */
function api_render_listing($result){
  $headers = _api_listing_headers();

  $rows = array();
  while ($object = db_fetch_object($result)) {
    $row = array();
    $row[] = l($object->title, 'api/'. $object->branch_name .'/'. $object->object_type .'/'. $object->object_name);
    $row[] = l(str_replace('/', '/ ', $object->file_name), 'api/'. $object->branch_name .'/file/'. $object->file_name);
    $row[] = api_link_documentation($object->summary, $object->branch_name);
    $rows[] = $row;
  }

  $output = theme('table', $headers, $rows);
  $output .= theme('pager', NULL, 50, 0);
  return $output;
}

/**
 * Menu callback; produces a textual listing of all functions for use in IDEs.
 */
function api_page_function_dump($branch_name) {
  $result = db_query("SELECT d.title, d.summary, f.signature FROM {api_documentation} d INNER JOIN {api_function} f ON d.did = f.did WHERE d.branch_name = '%s' AND d.object_type = 'function'", $branch_name);
  while ($object = db_fetch_object($result)) {
    print($object->signature);
    print(' ### '. $object->summary ."\n");
  }
}

/**
 * Menu callback; displays documentation for a function.
 */
function api_page_function($branch_name, $object_name) {
  theme('add_style', drupal_get_path('module', 'api') .'/api.css');

  $result = db_query("SELECT d.did, d.title, d.file_name, d.documentation, d.code, f.signature, f.start_line, f.parameters, f.return FROM {api_documentation} d INNER JOIN {api_function} f ON d.did = f.did WHERE d.object_name = '%s' AND d.branch_name = '%s' AND d.object_type = 'function'", $object_name, $branch_name);
  if ($function = db_fetch_object($result)) {
    $output = '';

    $output .= '<h3>'. t('Definition') .'</h3>';
    $output .= '<p><small>'. $function->signature .'</small><br />';
    $output .= l($function->file_name, 'api/'. $branch_name .'/file/'. $function->file_name) .', '. t('line') .' '. $function->start_line .'</p>';

    if (!empty($function->documentation)) {
      $output .= '<h3>'. t('Description') .'</h3>';
      $output .= api_link_documentation($function->documentation, $branch_name);
    }

    if (!empty($function->parameters)) {
      $output .= '<h3>'. t('Parameters') .'</h3>';
      $output .= api_link_documentation($function->parameters, $branch_name);
    }

    if (!empty($function->return)) {
      $output .= '<h3>'. t('Return value') .'</h3>';
      $output .= api_link_documentation($function->return, $branch_name);
    }

    $headers = array(array('data' => t('Name'), 'field' => 'd.title'), t('Description'));
    $rows = array();
    $result = db_query("SELECT d.object_name, d.title, d.summary FROM {api_reference} r INNER JOIN {api_documentation} d ON r.to_did = d.did AND d.object_type = 'group' WHERE r.from_did = %d". tablesort_sql($headers), $function->did);
    while ($group = db_fetch_object($result)) {
      $rows[] = array(
        l($group->title, 'api/'. $branch_name .'/group/'. $group->object_name),
        api_link_documentation($group->summary, $branch_name));
    }
    if (count($rows) > 0) {
      $output .= '<h3>'. t('Related topics') .'</h3>';
      $output .= theme('table', $headers, $rows);
    }

    if (!empty($function->code)) {
      $output .= '<h3>'. t('Code') .'</h3>';
      $output .= api_link_code($function->code, $branch_name);
    }

    drupal_set_title($function->title);
    return $output;
  }
  else {
    drupal_not_found();
  }
}

/**
 * Menu callback; displays all functions that reference another function.
 */
function api_page_function_references($branch_name, $object_name) {
  theme('add_style', drupal_get_path('module', 'api') .'/api.css');

  $result = db_query("SELECT d.did, d.title FROM {api_documentation} d WHERE d.object_name = '%s' AND d.branch_name = '%s' AND d.object_type = 'function'", $object_name, $branch_name);
  if ($function = db_fetch_object($result)) {
    $output = '';

    $headers = array(array('data' => t('Name'), 'field' => 'd.title'), t('Description'));
    $rows = array();
    $result = db_query("SELECT d.object_name, d.title, d.summary FROM {api_reference} r INNER JOIN {api_documentation} d ON r.to_did = d.did AND d.object_type = 'group' WHERE r.from_did = %d". tablesort_sql($headers), $function->did);
    while ($group = db_fetch_object($result)) {
      $rows[] = array(
        l($group->title, 'api/'. $branch_name .'/group/'. $group->object_name),
        api_link_documentation($group->summary, $branch_name));
    }
    if (count($rows) > 0) {
      $output .= '<h3>'. t('Related topics') .'</h3>';
      $output .= theme('table', $headers, $rows);
    }

    $headers = array(array('data' => t('Name'), 'field' => 'd.title'), array('data' => t('Location'), 'field' => 'd.file_name'), t('Description'));
    $rows = array();
    $result = db_query("SELECT d.object_name, d.title, d.summary, d.file_name FROM {api_reference} r INNER JOIN {api_documentation} d ON r.from_did = d.did AND d.object_type = 'function' WHERE r.to_did = %d". tablesort_sql($headers), $function->did);
    while ($reference = db_fetch_object($result)) {
      $rows[] = array(
        l($reference->title, 'api/'. $branch_name .'/function/'. $reference->object_name),
        l($reference->file_name, 'api/'. $branch_name .'/file/'. $reference->file_name),
        api_link_documentation($reference->summary, $branch_name));
    }
    if (count($rows) > 0) {
      $output .= '<h3>'. t('Functions that call %name()', array('%name' => $function->title)) .'</h3>';
      $output .= theme('table', $headers, $rows);
    }

    $headers = array(array('data' => t('Name'), 'field' => 'd.title'), array('data' => t('Location'), 'field' => 'd.file_name'), t('Description'));
    $rows = array();
    $result = db_query("SELECT d.object_name, d.title, d.summary, d.file_name FROM {api_reference} r INNER JOIN {api_documentation} d ON r.to_did = d.did AND d.object_type = 'function' WHERE r.from_did = %d". tablesort_sql($headers), $function->did);
    while ($reference = db_fetch_object($result)) {
      $rows[] = array(
        l($reference->title, 'api/'. $branch_name .'/function/'. $reference->object_name),
        l($reference->file_name, 'api/'. $branch_name .'/file/'. $reference->file_name),
        api_link_documentation($reference->summary, $branch_name));
    }
    if (count($rows) > 0) {
      $output .= '<h3>'. t('Functions called by %name()', array('%name' => $function->title)) .'</h3>';
      $output .= theme('table', $headers, $rows);
    }

    drupal_set_title($function->title);
    return $output;
  }
  else {
    drupal_not_found();
  }
}

/**
 * Menu callback; displays documentation for a constant.
 */
function api_page_constant($branch_name, $object_name) {
  theme('add_style', drupal_get_path('module', 'api') .'/api.css');

  $result = db_query("SELECT d.did, d.title, d.file_name, d.documentation, d.code FROM {api_documentation} d WHERE d.object_name = '%s' AND d.branch_name = '%s' AND d.object_type = 'constant'", $object_name, $branch_name);
  if ($constant = db_fetch_object($result)) {
    $output = '';

    $output .= '<h3>'. t('Definition') .'</h3>';
    $output .= '<p>'. l($constant->file_name, 'api/'. $branch_name .'/file/'. $constant->file_name) .'</p>';

    if (!empty($constant->documentation)) {
      $output .= '<h3>'. t('Description') .'</h3>';
      $output .= api_link_documentation($constant->documentation, $branch_name);
    }

    $headers = array(array('data' => t('Name'), 'field' => 'd.title'), t('Description'));
    $rows = array();
    $result = db_query("SELECT d.object_name, d.title, d.summary FROM {api_reference} r INNER JOIN {api_documentation} d ON r.to_did = d.did AND d.object_type = 'group' WHERE r.from_did = %d". tablesort_sql($headers), $constant->did);
    while ($group = db_fetch_object($result)) {
      $rows[] = array(
        l($group->title, 'api/'. $branch_name .'/group/'. $group->object_name),
        api_link_documentation($group->summary, $branch_name));
    }
    if (count($rows) > 0) {
      $output .= '<h3>'. t('Related topics') .'</h3>';
      $output .= theme('table', $headers, $rows);
    }

    if (!empty($constant->code)) {
      $output .= '<h3>'. t('Code') .'</h3>';
      $output .= api_link_code($constant->code, $branch_name);
    }

    drupal_set_title($constant->title);
    return $output;
  }
  else {
    drupal_not_found();
  }
}

/**
 * Menu callback; displays documentation for a file.
 */
function api_page_file($branch_name, $object_name) {
  theme('add_style', drupal_get_path('module', 'api') .'/api.css');

  $result = db_query("SELECT d.did, d.title, d.documentation, f.version FROM {api_documentation} d INNER JOIN {api_file} f ON d.did = f.did WHERE object_name = '%s' AND branch_name = '%s' AND object_type = 'file'", $object_name, $branch_name);
  $file = db_fetch_object($result);
  if (!$file) {
    $result = db_query("SELECT d.object_name FROM {api_documentation} d WHERE d.object_name LIKE '%%/%s' AND branch_name = '%s' AND object_type = 'file'", $object_name, $branch_name);
    if ($object_name = db_result($result)) {
      drupal_goto('api/'. $branch_name .'/file/'. $object_name);
    }
  }
  if ($file) {
    $output = '';

    if (!empty($file->version)) {
      $output .= '<h3>'. t('Version') .'</h3>';
      $output .= '<p>'. $file->version .'</p>';
    }

    if (!empty($file->documentation)) {
      $output .= '<h3>'. t('Description') .'</h3>';
      $output .= api_link_documentation($file->documentation, $branch_name);
    }

    $headers = array(array('data' => t('Name'), 'field' => 'title'), t('Description'));
    $rows = array();
    $result = db_query("SELECT title, object_name, summary FROM {api_documentation} WHERE file_name = '%s' AND branch_name = '%s' AND object_type = 'constant'". tablesort_sql($headers), $object_name, $branch_name);
    while ($object = db_fetch_object($result)) {
      $rows[] = array(
        l($object->title, 'api/'. $branch_name .'/constant/'. $object->object_name),
        api_link_documentation($object->summary, $branch_name));
    }
    if (count($rows) > 0) {
      $output .= '<h3>'. t('Constants') .'</h3>';
      $output .= theme('table', $headers, $rows);
    }

    $headers = array(array('data' => t('Name'), 'field' => 'title'), t('Description'));
    $rows = array();
    $result = db_query("SELECT title, object_name, summary FROM {api_documentation} WHERE file_name = '%s' AND branch_name = '%s' AND object_type = 'function'". tablesort_sql($headers), $object_name, $branch_name);
    while ($object = db_fetch_object($result)) {
      $rows[] = array(
        l($object->title, 'api/'. $branch_name .'/function/'. $object->object_name),
        api_link_documentation($object->summary, $branch_name));
    }
    if (count($rows) > 0) {
      $output .= '<h3>'. t('Functions') .'</h3>';
      $output .= theme('table', $headers, $rows);
    }

    drupal_set_title($file->title);
    return $output;
  }
  else {
    drupal_not_found();
  }
}

/**
 * Menu callback; displays source code for a file.
 */
function api_page_file_source($branch_name, $object_name) {
  theme('add_style', drupal_get_path('module', 'api') .'/api.css');

  $result = db_query("SELECT title, code FROM {api_documentation} WHERE object_name = '%s' AND branch_name = '%s' AND object_type = 'file'", $object_name, $branch_name);
  if ($file = db_fetch_object($result)) {
    drupal_set_title($file->title);
    return api_link_code($file->code, $branch_name);
  }
  else {
    drupal_not_found();
  }
}

/**
 * Menu callback; displays documentation for a group.
 */
function api_page_group($branch_name, $object_name) {
  theme('add_style', drupal_get_path('module', 'api') .'/api.css');

  $result = db_query("SELECT did, title, documentation FROM {api_documentation} WHERE object_name = '%s' AND branch_name = '%s' AND object_type = 'group'", $object_name, $branch_name);
  if ($group = db_fetch_object($result)) {
    $output = '';

    if (!empty($group->documentation)) {
      $output .= api_link_documentation($group->documentation, $branch_name);
    }

    $headers = array(array('data' => t('Name'), 'field' => 'd.title'), array('data' => t('Location'), 'field' => 'd.file_name'), t('Description'));
    $rows = array();
    $result = db_query("SELECT d.object_name, d.title, d.summary, d.file_name FROM {api_reference} r INNER JOIN {api_documentation} d ON r.from_did = d.did AND d.object_type = 'constant' WHERE r.to_did = %d". tablesort_sql($headers), $group->did);
    while ($object = db_fetch_object($result)) {
      $rows[] = array(
        l($object->title, 'api/'. $branch_name .'/constant/'. $object->object_name),
        l($object->file_name, 'api/'. $branch_name .'/file/'. $object->file_name),
        api_link_documentation($object->summary, $branch_name));
    }
    if (count($rows) > 0) {
      $output .= '<h3>'. t('Constants') .'</h3>';
      $output .= theme('table', $headers, $rows);
    }

    $headers = array(array('data' => t('Name'), 'field' => 'd.title'), array('data' => t('Location'), 'field' => 'd.file_name'), t('Description'));
    $rows = array();
    $result = db_query("SELECT d.object_name, d.title, d.summary, d.file_name FROM {api_reference} r INNER JOIN {api_documentation} d ON r.from_did = d.did AND d.object_type = 'function' WHERE r.to_did = %d". tablesort_sql($headers), $group->did);
    while ($object = db_fetch_object($result)) {
      $rows[] = array(
        l($object->title, 'api/'. $branch_name .'/function/'. $object->object_name),
        l($object->file_name, 'api/'. $branch_name .'/file/'. $object->file_name),
        api_link_documentation($object->summary, $branch_name));
    }
    if (count($rows) > 0) {
      $output .= '<h3>'. t('Functions') .'</h3>';
      $output .= theme('table', $headers, $rows);
    }

    drupal_set_title($group->title);
    return $output;
  }
  else {
    drupal_not_found();
  }
}

/**
 * Menu callback; displays the administration page.
 */
function api_page_admin() {
  $output = '';
  $form = array();

  $result = db_query('SELECT branch_name, title, directory FROM {api_branch}');
  while ($branch = db_fetch_object($result)) {
    $form[$branch->branch_name] = array('#tree' => TRUE, '#type' => 'fieldset');
    $form[$branch->branch_name]['branch_name'] = array(
      '#title' => t('Short name'),
      '#type' => 'textfield',
      '#default_value' => $branch->branch_name,
    );
    $form[$branch->branch_name]['title'] = array(
      '#title' => t('Long name'),
      '#type' => 'textfield',
      '#default_value' => $branch->title,
    );
    $form[$branch->branch_name]['directory'] = array(
      '#title' => t('Directory name'),
      '#type' => 'textfield',
      '#default_value' => $branch->directory,
      '#description' => t('The absolute path of the directory to index. Multiple paths may be given, separated by colons, e.g.: "/mysite/drupal:/mysite/developer".'),
    );
    
    $radios[$branch->branch_name] = $branch->title;
    
  }

  $form['new'] = array('#tree' => TRUE, '#type' => 'fieldset');
  $form['new']['branch_name'] = array(
    '#title' => t('Short name'),
    '#type' => 'textfield',
  );
  $form['new']['title'] = array(
    '#title' => t('Long name'),
    '#type' => 'textfield',
  );
  $form['new']['directory'] = array(
    '#title' => t('Directory name'),
    '#type' => 'textfield',
    '#description' => t('The absolute path of the directory to index. Multiple paths may be given, separated by colons, e.g.: "/mysite/drupal:/mysite/developer".'),
  );
  
  global $base_url;
  
  if($radios){
    if(!variable_get('api_default_branch', FALSE)){
      list($first_key) = array_keys($radios);
      variable_set('api_default_branch', $first_key);
    }
    
    $form['default_branch'] = array(
      '#type' => 'radios',
      '#title' => t('Default branch'),
      '#default_value' => variable_get('api_default_branch', $first_key),
      '#description' => t('Allows searching at %baseurl/apis/<code>your+search+here</code>', array('%baseurl' => $base_url)),
      '#options' => $radios,     
    );
  }

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save changes'),
  );

  $output .= '<h3>'. t('Branches to index'). '</h3>';
  $output .= drupal_get_form('api_page_admin', $form);

  $output .= '<h3>'. t('PHP Manual') .'</h3>';
  $form = array();
  $form['api_php_funcsummary'] = array(
    '#type' => 'textfield',
    '#default_value' => variable_get('api_php_funcsummary', 'http://cvs.php.net/viewcvs.cgi/*checkout*/phpdoc/funcsummary.txt'),
    '#description' => t('The URL of the PHP function summary document.'),
  );
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Index PHP manual pages'),
  );
  $output .= drupal_get_form('api_php_manual_index', $form);

  $output .= '<h3>'. t('Refresh index') .'</h3>';
  $output .= '<p>'. t('Parse all indexed code files again, even if they have not been modified.') .'</p>';
  $form = array();
  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Reindex'),
  );
  $output .= drupal_get_form('api_reindex', $form);

  return $output;
}

function api_page_admin_submit($form_id, $form_values) {
  foreach ($form_values as $branch_name => $branch) {
    if ($branch_name == 'submit' || $branch_name == 'default_branch') {
      next;
    }
    if ($branch_name == 'new') {
      if ($branch['branch_name'] != '') {
        db_query("INSERT INTO {api_branch} (branch_name, title, directory) VALUES ('%s', '%s', '%s')", $branch['branch_name'], $branch['title'], $branch['directory']);
      }
    }
    else {
      if ($branch['branch_name'] == '') {
        db_query("DELETE FROM {api_branch} WHERE branch_name = '%s'", $branch_name);
        $result = db_query("SELECT did FROM {api_documentation} WHERE branch_name = '%s'", $branch_name);
        while ($object = db_fetch_object($result)) {
          db_query('DELETE FROM {api_documentation} WHERE did = %d', $object->did);
          db_query('DELETE FROM {api_file} WHERE did = %d', $object->did);
          db_query('DELETE FROM {api_function} WHERE did = %d', $object->did);
          db_query('DELETE FROM {api_reference} WHERE from_did = %d OR to_did = %d', $object->did, $object->did);
        }
      }
      else {
        db_query("UPDATE {api_branch} SET branch_name = '%s', title = '%s', directory = '%s' WHERE branch_name = '%s'", $branch['branch_name'], $branch['title'], $branch['directory'], $branch_name);
        db_query("UPDATE {api_documentation} SET branch_name = '%s' WHERE branch_name = '%s'", $branch['branch_name'], $branch_name);
      }
    }
  }
  
  //save the variable for default branch
  if($form_values['default_branch']){
    variable_set('api_default_branch', $form_values['default_branch']);
  }

  drupal_set_message(t('Changes saved.'));
}

function api_php_manual_index_submit($form_id, $form_values) {
  include_once(drupal_get_path('module', 'api') .'/parser.inc');
  variable_set('api_php_funcsummary', $form_values['api_php_funcsummary']);
  db_query("DELETE FROM {api_documentation} WHERE branch_name = 'php'");
  api_parse_php_manual($form_values['api_php_funcsummary']);
  drupal_set_message(t('Manual pages scanned.'));
}

function api_reindex_submit($form_id, $form_values) {
  db_query("UPDATE {api_file} SET modified = 52");
  drupal_set_message(t('All files have been tagged for reindexing. The index will be rebuilt during the next few runs of %cron.', array('%cron' => '<a href="cron.php">cron.php</a>')));
}

/**
 * Implementation of hook_cron().
 */
function api_cron() {
  include_once(drupal_get_path('module', 'api') .'/parser.inc');

  $files_scanned = 0;

  db_query("UPDATE {api_file} SET found = 0");

  $branches = db_query('SELECT branch_name, directory FROM {api_branch}');
  while ($branch = db_fetch_object($branches)) {
    $files = api_scan_directories($branch->directory);
    foreach ($files as $path => $file_name) {
      if ($files_scanned >= variable_get('api_files_per_cron', 10)) {
        break;
      }

      $modified = 0;
      $result = db_query("SELECT f.did, f.modified FROM {api_documentation} d INNER JOIN {api_file} f ON d.did = f.did WHERE d.object_name = '%s' AND d.branch_name = '%s' AND d.object_type = 'file'", $file_name, $branch->branch_name);
      if ($file = db_fetch_object($result)) {
        $modified = $file->modified;
        db_query("UPDATE {api_file} SET found = 1 WHERE did = %d", $file->did);
      }

      if (filemtime($path) > $modified) {
        if (api_parse_file($path, $branch->branch_name, $file_name)) {
          $files_scanned++;
        }
      }
    }
  }

  // Remove outdated files.
  if ($files_scanned == 0) {
    $result = db_query("SELECT ad.file_name, ad.branch_name FROM {api_file} af LEFT JOIN {api_documentation} ad ON ad.did = af.did WHERE af.found = 0");
    while ($file = db_fetch_object($result)) {
      print ('Removing '. $file->file_name .'...<br />');
      $doc_result = db_query("SELECT ad.did FROM {api_documentation} ad WHERE ad.file_name = '%s' AND ad.branch_name = '%s'", $file->file_name, $file->branch_name);
      while ($doc = db_fetch_object($doc_result)) {
        db_query("DELETE FROM {api_documentation} WHERE did = %d", $doc->did);
        db_query("DELETE FROM {api_file} WHERE did = %d", $doc->did);
        db_query("DELETE FROM {api_function} WHERE did = %d", $doc->did);
        db_query("DELETE FROM {api_reference} WHERE from_did = %d OR to_did = %d", $doc->did, $doc->did);
      }
    }
  }

  // Ensure that reference updates persist until the next cron run.
  api_reference('save');
}

/**
 * Turn function names into links.
 */
function api_link_code($code, $branch_name) {
  $code = preg_replace('!(<span class="php-function-or-constant">)([a-zA-Z0-9_]+)(</span>)!e', '"$1". api_link_name("$2", "'. $branch_name .'") ."$3"', $code);

  return $code;
}

/**
 * Turn function and file names into links.
 */
function api_link_documentation($documentation, $branch_name) {
  // We do the replacement in two phases to avoid linking documentation within title attributes.
  $documentation = preg_replace('!([a-zA-Z0-9_]+)(\()!', '@@@$1@@@$2', $documentation);
  $documentation = preg_replace('%(?<!")[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+(?!")%', '@@@$0@@@', $documentation);
  $documentation = preg_replace('!@@@([^@]*)@@@!e', 'api_link_name("$1", "'. $branch_name .'")', $documentation);

  return $documentation;
}

/**
 * Link an object name to its documentation.
 */
function api_link_name($name, $branch_name) {
  static $local_objects = array();
  static $php_functions = array();

  if (count($local_objects) == 0) {
    $result = db_query("SELECT object_name, title, object_type, summary FROM {api_documentation} WHERE branch_name = '%s'", $branch_name);
    while ($object = db_fetch_object($result)) {
      $local_objects[$object->object_type][$object->title] = $object;
    }
  }

  if (count($php_functions) == 0) {
    $result = db_query("SELECT object_name, summary FROM {api_documentation} WHERE branch_name = 'php' AND object_type = 'function'");
    while ($function = db_fetch_object($result)) {
      $php_functions[$function->object_name] = $function->summary;
    }
  }

  if (array_key_exists($name, $local_objects['function'])) {
    return l($name, 'api/'. $branch_name .'/function/'. $local_objects['function'][$name]->object_name, array('title' => $local_objects['function'][$name]->summary, 'class' => 'local'));
  }
  else if (array_key_exists($name, $local_objects['file'])) {
    return l($name, 'api/'. $branch_name .'/file/'. $local_objects['file'][$name]->object_name, array('title' => $local_objects['file'][$name]->summary, 'class' => 'local'));
  }
  else if (array_key_exists($name, $local_objects['constant'])) {
    return l($name, 'api/'. $branch_name .'/constant/'. $local_objects['constant'][$name]->object_name, array('title' => $local_objects['constant'][$name]->summary, 'class' => 'local'));
  }
  else if (array_key_exists($name, $php_functions)) {
    return '<a href="http://php.net/'. $name .'" title="'. $php_functions[$name] .'" class="php-manual">'. $name .'</a>';
  }
  else {
    return $name;
  }
}

/**
 * Find all the files in the directories specified for a branch.
 */
function api_scan_directories($directories) {
  $directory_array = explode(':', $directories);

  if (count($directory_array) > 1) {
    $directories_components = array();
    foreach ($directory_array as $directory) {
      $directory_components = array();
      $parts = explode('/', $directory);
      foreach ($parts as $part) {
        if (strlen($part)) {
          array_unshift($directory_components, reset($directory_components) .'/'. $part);
        }
      }
      $directories_components[] = $directory_components;
    }

    $common_ancestor_components = call_user_func_array('array_intersect', $directories_components);
    $common_ancestor = reset($common_ancestor_components);
  }
  else {
    $common_ancestor = $directories;
  }

  $source_files = array();
  foreach ($directory_array as $directory) {
    $files = file_scan_directory($directory, '.*');
    foreach ($files as $path => $file) {
      if (strpos($path, '/.') !== FALSE) {
        continue;
      }
      $file_name = substr($path, strlen($common_ancestor) + 1);
      
      $source_files[$path] = $file_name;
    }
  }
  return $source_files;
}
