<?php

/**
 * Implements hook_hook_info().
 */
function media_internet_hook_info() {
  $hooks = array(
    'media_internet_providers',
  );

  return array_fill_keys($hooks, array('group' => 'media'));
}

/**
 * Implements hook_menu().
 */
function media_internet_menu() {
  $items['file/add/web'] = array(
    'title' => 'Web',
    'description' => 'Add internet files to your media library.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('media_internet_add_upload'),
    'access callback' => 'media_internet_access',
    'type' => MENU_LOCAL_TASK,
    'file' => 'file_entity.pages.inc',
    'file path' => drupal_get_path('module', 'file_entity'),
  );

  return $items;
}

/**
 * Access callback for the media_internet media browser plugin.
 */
function media_internet_access($account = NULL) {
  return user_access('administer files', $account) || user_access('add media from remote sources', $account);
}

/**
 * Implement hook_permission().
 */
function media_internet_permission() {
  return array(
    'add media from remote sources' => array(
      'title' => t('Add media from remote services'),
      'description' => t('Add media from remote sources such as other websites, YouTube, etc'),
    ),
  );
}

/**
 * Implements hook_theme().
 */
function media_internet_theme() {
  return array(
    // media_internet.pages.inc.
    'media_internet_embed_help' => array(
      'variables' => array('description' => NULL, 'supported_providers' => NULL),
    ),
  );
}

/**
 * Gets the list of providers.
 *
 * A "Provider" is a bit of meta-data like a title and a logo and a class which
 * can handle saving remote files.  Each provider is able to parse an embed code or URL
 * and store it as a file object in file_managed.
 */
function media_internet_get_providers() {
  $providers = &drupal_static(__FUNCTION__);

  if (!isset($providers)) {
    $cid = 'media:internet:providers';
    if ($cache = cache_get($cid)) {
      $providers = $cache->data;
    }
    else {
      $providers = array();
      foreach (module_implements('media_internet_providers') as $module) {
        foreach (module_invoke($module, 'media_internet_providers') as $key => $provider) {
          // Store the module here too for convinience.
          $providers[$key] = $provider;
          $providers[$key]['module'] = $module;
          if (!isset($providers[$key]['weight'])) {
            $providers[$key]['weight'] = 0;
          }
        }
      }
      uasort($providers, 'drupal_sort_weight');
      cache_set($cid, $providers);
    }
  }

  return $providers;
}

/**
 * Finds the appropriate provider for a given URL or embed_string
 *
 * Each provider has a claim() method which it uses to tell media_internet
 * that it should handle this input.  We cycle through all providers to find
 * the right one.
 *
 * @todo: Make this into a normal hook or something because we have to instantiate
 * each class to test and that's not right.
 */
function media_internet_get_provider($embed_string) {
  foreach (media_internet_get_providers() as $class_name => $nothing) {
    $p = new $class_name($embed_string);
    if ($p->claim($embed_string)) {
      return $p;
    }
  }
  throw new MediaInternetNoHandlerException(t('Unable to handle the provided embed string or URL.'));
}

/**
 * Returns HTML for help text based on supported internet media providers.
 *
 * @param $variables
 *   An associative array containing:
 *   - description: The normal description for this field, specified by the
 *     user.
 *   - supported_providers: A string of supported providers.
 *
 * @ingroup themeable
 */
function theme_media_internet_embed_help($variables) {
  $description = $variables['description'];
  $supported_providers = $variables['supported_providers'];

  $descriptions = array();

  if (strlen($description)) {
    $descriptions[] = $description;
  }
  if (!empty($supported_providers)) {
    $descriptions[] = t('Supported internet media providers: !providers.', array('!providers' => '<strong>' . $supported_providers . '</strong>'));
  }

  return implode('<br />', $descriptions);
}

/**
 * Implements hook_forms().
 */
function media_internet_forms($form_id, $args) {
  $forms = array();

  // Create a copy of the upload wizard form for internet media.
  if ($form_id == 'media_internet_add_upload') {
    $forms[$form_id] = array(
      'callback' => 'file_entity_add_upload',
    );
  }

  return $forms;
}

/**
 * Implements hook_form_FORM_ID_alter().
 */
function media_internet_form_file_entity_add_upload_alter(&$form, &$form_state, $form_id) {
  $step = $form['#step'];
  $options = $form['#options'];

  // Swap the upload field for an embed field when on the first step of the web
  // tab.
  if ($form_id == 'media_internet_add_upload' && $step == 1) {
    unset($form['upload']);

    $form['embed_code'] = array(
      '#type' => 'textfield',
      '#title' => t('File URL'),
      '#description' => t('Enter a URL to a file.'),
      '#attributes' => array('class' => array('media-add-from-url')),
      // There is no standard specifying a maximum length for a URL. Internet
      // Explorer supports up to 2083 (http://support.microsoft.com/kb/208427)
      // so we assume publicly available media URLs are within this limit.
      '#maxlength' => 2083,
      '#required' => TRUE,
      '#default_value' => isset($form_state['storage']['embed_code']) ? $form_state['storage']['embed_code'] : NULL,
    );

    // Create an array to hold potential Internet media providers.
    $providers = array();

    // Determine if there are any visible providers.
    foreach (media_internet_get_providers() as $key => $provider) {
      if (empty($provider['hidden']) || $provider['hidden'] != TRUE) {
        $providers[] = check_plain($provider['title']);
      }
    }

    $form['#providers'] = $providers;

    // Notify the user of any available providers.
    if ($providers) {
      // If any providers are enabled it is assumed that some kind of embed is supported.
      $form['embed_code']['#title'] = t('File URL or media resource');
      $form['embed_code']['#description'] = t('Enter a URL to a file or media resource. Many media providers also support identifying media via the embed code used to embed the media into external websites.');

      $form['embed_code']['#description'] = theme('media_internet_embed_help', array('description' => $form['embed_code']['#description'], 'supported_providers' => implode(' ', $providers)));
    }

    $form['#validators'] = array();

    if (!empty($options['types'])) {
      $form['#validators']['media_file_validate_types'] = array($options['types']);
    }

    // Add validation and submission handlers to the form and ensure that they
    // run first.
    array_unshift($form['#validate'], 'media_internet_add_validate');
    array_unshift($form['#submit'], 'media_internet_add_submit');
  }
}

/**
 * Allow stream wrappers to have their chance at validation.
 *
 * Any module that implements hook_media_parse will have an
 * opportunity to validate this.
 *
 * @see media_parse_to_uri()
 */
function media_internet_add_validate($form, &$form_state) {
  // Supporting providers can now claim this input. It might be a URL, but it
  // might be an embed code as well.
  $embed_code = $form_state['values']['embed_code'];

  try {
    $provider = media_internet_get_provider($embed_code);
    $provider->validate();
  }
  catch (MediaInternetNoHandlerException $e) {
    form_set_error('embed_code', $e->getMessage());
    return;
  }
  catch (MediaInternetValidationException $e) {
    form_set_error('embed_code', $e->getMessage());
    return;
  }

  $validators = $form['#validators'];
  $file = $provider->getFileObject();

  if ($validators) {
    try {
      $file = $provider->getFileObject();
    }
    catch (Exception $e) {
      form_set_error('embed_code', $e->getMessage());
      return;
    }

    // Check for errors. @see media_add_upload_validate calls file_save_upload().
    // this code is ripped from file_save_upload because we just want the validation part.
    // Call the validation functions specified by this function's caller.
    $errors = file_validate($file, $validators);

    if (!empty($errors)) {
      $message = t('%url could not be added.', array('%url' => $embed_code));
      if (count($errors) > 1) {
        $message .= theme('item_list', array('items' => $errors));
      }
      else {
        $message .= ' ' . array_pop($errors);
      }
      form_set_error('embed_code', $message);
      return FALSE;
    }
  }

  // @TODO: Validate that if we have no $uri that this is a valid file to
  // save. For instance, we may only be interested in images, and it would
  // be helpful to let the user know they passed the HTML page containing
  // the image accidentally. That would also save us from saving the file
  // in the submit step.

  // This is kinda a hack of the same.

  // This should use the file_validate routines that the upload form users.
  // We need to fix the media_parse_to_file routine to allow for a validation.
}

/**
 * Upload a file from a URL.
 *
 * This will copy a file from a remote location and store it locally.
 *
 * @see media_parse_to_uri()
 * @see media_parse_to_file()
 */
function media_internet_add_submit($form, &$form_state) {
  $embed_code = $form_state['values']['embed_code'];

  try {
    // Save the remote file
    $provider = media_internet_get_provider($embed_code);
    // Providers decide if they need to save locally or somewhere else.
    // This method returns a file object
    $file = $provider->save();
  }
  catch (Exception $e) {
    form_set_error('embed_code', $e->getMessage());
    return;
  }

  if (!$file->fid) {
    form_set_error('embed_code', t('The file %file could not be saved. An unknown error has occurred.', array('%file' => $embed_code)));
    return;
  }
  else {
    $form_state['storage']['upload'] = $file->fid;
  }
}
