<?php
/**
 * @file
 * Image optimize functionalities
 */

/**
 * Include additional files.
 */
$dirname = dirname(__FILE__);
$includes = file_scan_directory($dirname . '/includes', '/.inc$/');
foreach (module_list() as $module) {
  $file = $dirname . '/includes/' . $module . '.inc';
  if (isset($includes[$file])) {
    require_once $file;
  }
}

/**
 * Implements hook_init().
 *
 * Abstract layer to all methods implemented by base toolkit.
 */
function imageapi_optimize_init() {
  $cache = cache_get('imageapi_optimize:methods');
  if (!$cache || (empty($cache->data) && variable_get('imageapi_optimize_toolkit', '') != '')) {
    $methods = _imageapi_optimize_get_methods();
  }
  else {
    $methods = $cache->data;
  }

  foreach ($methods as $method) {
    eval('function image_imageapi_optimize_' . $method . '($image) {
      $params = array_slice(func_get_args(), 1);
      return _imageapi_optimize_invoke("' . $method . '", $image, $params);
    }');
  }
}

/**
 * Implements hook_theme().
 */
function imageapi_optimize_theme() {
  $items = array();

  $items['imageapi_optimize_services_internal_form'] = array(
    'render element' => 'form',
    'file' => 'services/internal.inc',
  );

  return $items;
}

/**
 * Implements hook_menu().
 */
function imageapi_optimize_menu() {
  $items['admin/config/media/image-toolkit/imageapi_optimize/%'] = array(
    'title callback' => 'imageapi_optimize_internal_binary_settings_title',
    'title arguments' => array(5),
    'page callback' => 'drupal_get_form',
    'page arguments' => array('imageapi_optimize_internal_binary_settings', 5),
    'access arguments' => array('administer site configuration'),
    'file' => 'services/internal.inc',
  );

  return $items;
}

/**
 * Implements hook_image_toolkits().
 */
function imageapi_optimize_image_toolkits() {
  return array(
    'imageapi_optimize' => array(
      'title' => t('ImageAPI Optimize'),
      'available' => TRUE,
    ),
  );
}

/**
 * Settings form callback for the image toolkit.
 */
function image_imageapi_optimize_settings() {
  // Get all defined services and binaries info definitions.
  $info = imageapi_optimize_info(TRUE);

  $form['imageapi_optimize_base_toolkit'] = array(
    '#type' => 'fieldset',
    '#title' => t('ImageAPI Optimize Base Toolkit'),
  );

  // Base toolkit settings.
  $toolkits = image_get_available_toolkits();
  unset($toolkits['imageapi_optimize']);
  $base_toolkit = variable_get('imageapi_optimize_toolkit', 'gd');

  $form['imageapi_optimize_base_toolkit']['imageapi_optimize_toolkit'] = array(
    '#type' => 'radios',
    '#title' => t('Base toolkit'),
    '#default_value' => $base_toolkit,
    '#options' => $toolkits,
    '#element_validate' => array('imageapi_optimize_toolkit_element_validate'),
  );

  $function = 'image_' . $base_toolkit . '_settings';
  if (function_exists($function)) {
    $return = $function();
    if (is_array($return)) {
      $form['imageapi_optimize_base_toolkit']['imageapi_optimize_toolkit_settings'] = array(
        '#type' => 'fieldset',
        '#title' => t('@toolkit Settings', array('@toolkit' => $toolkits[$base_toolkit])),
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
      ) + $return;
    }
  }

  // Services (3rd party and internal) settings.
  $form['imageapi_optimize_service'] = array(
    '#type' => 'fieldset',
    '#title' => t('ImageAPI Optimize Service'),
  );

  $form['imageapi_optimize_service']['imageapi_optimize_service'] = array(
    '#type' => 'radios',
    '#title' => t('Choose which optimization service to use'),
    '#default_value' => variable_get('imageapi_optimize_service', 'internal'),
    '#options' => array(),
  );

  uasort($info['services'], 'drupal_sort_weight');
  foreach ($info['services'] as $name => $service) {
    // Append this service to the available services.
    $form['imageapi_optimize_service']['imageapi_optimize_service']['#options'][$name] = $service['title'];
    if (isset($service['url'])) {
      $form['imageapi_optimize_service']['imageapi_optimize_service']['#options'][$name] = l($service['title'], $service['url']);
    }

    // Build service form if required.
    if (isset($service['callbacks']['form'])) {
      // Include services file if defined and available.
      if (isset($service['file'])) {
        include_once $service['file'];
      }

      $return = $service['callbacks']['form']($info);
      if (is_array($return)) {
        $form['imageapi_optimize_service']['imageapi_optimize_' . $name] = array(
          '#type' => 'fieldset',
          '#title' => t('@service Settings', array('@service' => $service['title'])),
          '#collapsible' => TRUE,
          '#collapsed' => TRUE,
          '#states' => array(
            'visible' => array(
              ':input[name="imageapi_optimize_service"]' => array('value' => $name),
            ),
          ),
        ) + $return;
      }
    }
  }

  // Reloads methods because user may change toolkit
  $form['#submit'][] = '_imageapi_optimize_get_methods';

  return $form;
}

/**
 * Validation callback for Base toolkit field.
 */
function imageapi_optimize_toolkit_element_validate($element) {
  if ($element['#value'] == 'imageapi_optimize') {
    form_set_error($element['#name'], t('You must select a different toolkit from ImageAPI Optimize itself.'));
  }
}

/**
 * Implements image_HOOK_load().
 */
function image_imageapi_optimize_load($image) {
  return _imageapi_optimize_invoke('load', $image);
}

/**
 * Implements image_HOOK_save().
 */
function image_imageapi_optimize_save($image, $dst) {
  if (_imageapi_optimize_invoke('save', $image, array($dst))) {
    return _imageapi_optimize_optimize($image, $dst);
  }
  return FALSE;
}
/**
 * Helper. Based on image_toolkit_invoke()
 */
function _imageapi_optimize_invoke($method, $image, array $params = array()) {
  $function = 'image_' . variable_get('imageapi_optimize_toolkit', '') . '_' . $method;
  if (function_exists($function)) {
    array_unshift($params, $image);

    return call_user_func_array($function, $params);
  }
  return FALSE;
}

/**
 * Optimizes image with external commands
 */
function _imageapi_optimize_optimize($image, $dst) {
  $info = imageapi_optimize_info();
  $service = variable_get('imageapi_optimize_service', 'internal');

  if (isset($info['services'][$service])) {
    $service = $info['services'][$service];
    if (isset($service['file'])) {
      include_once $service['file'];
    }

    $service['callbacks']['process']($image, $dst);
  }

  // If optimize service fails, there is no problem. Original image is saved.
  return TRUE;
}

/**
 * Load all defined services and binaries info definitions.
 */
function imageapi_optimize_info($reset = FALSE) {
  $cache = cache_get('imageapi_optimize:info');

  if (!$cache || $reset) {
    $info = module_invoke_all('imageapi_optimize_info');
    drupal_alter('imageapi_optimize_info', $info);

    // Process and validate all defined info.
    foreach ($info as $type => &$types) {
      foreach ($types as $name => &$data) {
        // Set defaults.
        $data += array(
          'callbacks' => array(),
          'weight' => 0,
        );
        $data['callbacks'] += array(
          'process' => 'imageapi_optimize_' . $type . '_' . $name,
        );

        // Add form callback if undefined yet available.
        if (!isset($data['callbacks']['form'])) {
          $function = 'imageapi_optimize_' . $type . '_' . $name . '_form';
          if (function_exists($function)) {
            $data['callbacks']['form'] = $function;
          }
        }

        // Set user defined binaries weight if available.
        if ('binaries' == $type) {
          $settings = variable_get('imageapi_optimize_' . $name, array());
          $data['weight'] = isset($settings['weight']) ? $settings['weight'] : $data['weight'];

          if (empty($data['settings'])) {
            $data['settings'] = array();
          }
        }

        // Validate info.
        $error = '';
        if (!isset($data['title'])) {
          $error .= t('Title not set.') . '<br />';
        }

        foreach ($data['callbacks'] as $callback) {
          if (!function_exists($callback)) {
            $error .= t('Function !callback doesn\'t exist.', array('!callback' => $callback)) . '<br />';
          }
        }

        if (isset($data['file']) && !file_exists($data['file'])) {
          $error .= t('File !file doesn\'t exist.', array('!file' => $file)) . '<br />';
        }

        if (!empty($error)) {
          unset($info[$type][$name]);
          watchdog('imageapi_optimize', $errors);
        }
      }

      // Sort by weight.
      uasort($info[$type], 'drupal_sort_weight');
    }

    // Cache the info to reduce future load times.
    cache_set('imageapi_optimize:info', $info);
    return $info;
  }

  return $cache->data;
}

/**
 * Gets all implemented methods by ImageAPI and contrib modules.
 * This function takes a dozens of miliseconds CPU times.
 */
function _imageapi_optimize_get_methods() {
  // The list of toolkits might not loaded yet. We call this function to get
  // toolkits in separate .inc files eventually included.
  image_get_available_toolkits();

  $funcs = get_defined_functions();
  $methods = array();
  $prefix = 'image_' . variable_get('imageapi_optimize_toolkit', '') . '_';

  foreach ($funcs['user'] as $func) {
    if (strpos($func, $prefix) === 0) {
      $method = substr($func, strlen($prefix));
      if (!in_array($method, array('load', 'save', 'settings'))) {
        $methods[] = $method;
      }
    }
  }

  cache_set('imageapi_optimize:methods', $methods);
  watchdog('imageapi', 'Refresh ImageAPI methods');
  return $methods;
}
