<?php

/**
 * @file
 * Provide a "Multimedia asset" field.
 */

/**
 * Implements hook_field_info().
 */
function mediafield_field_info() {
  return array(
    'media' => array(
      'label' => t('Multimedia asset'),
      'description' => t('This field stores a reference to a multimedia asset.'),
      'settings' => array(),
      'instance_settings' => array(
        'file_extensions' => variable_get('file_entity_default_allowed_extensions', 'jpg jpeg gif png txt doc docx xls xlsx pdf ppt pptx pps ppsx odt ods odp mp3 mov mp4 m4a m4v mpeg avi ogg oga ogv weba webp webm'),
      ),
      'default_widget' => 'media_generic',
      'default_formatter' => 'media',
      'property_type' => 'field_item_file',
      'property_callbacks' => array('entity_metadata_field_file_callback'),
    ),
  );
}

/**
 * Implements hook_field_widget_info_alter().
 *
 * Alter the media file selector so it is available for media fields.
 */
function mediafield_field_widget_info_alter(&$info) {
  $info['media_generic']['field types'][] = 'media';
}

/**
 * Implements hook_field_instance_settings_form().
 */
function mediafield_field_instance_settings_form($field, $instance) {
  $settings = $instance['settings'];

  // Make the extension list a little more human-friendly by comma-separation.
  $extensions = str_replace(' ', ', ', $settings['file_extensions']);
  $form['file_extensions'] = array(
    '#type' => 'textfield',
    '#title' => t('Allowed file extensions for uploaded files'),
    '#default_value' => $extensions,
    '#description' => t('Separate extensions with a space or comma and do not include the leading dot.'),
    '#element_validate' => array('_file_generic_settings_extensions'),
    // By making this field required, we prevent a potential security issue
    // that would allow files of any type to be uploaded.
    '#required' => TRUE,
    '#maxlength' => 255,
  );

  return $form;
}

/**
 * Implements hook_field_is_empty().
 */
function mediafield_field_is_empty($item, $field) {
  return empty($item['fid']);
}

/**
 * Implements hook_field_formatter_info().
 */
function mediafield_field_formatter_info() {
  $formatters = array(
    'media' => array(
      'label' => t('Media'),
      'field types' => array('media'),
      'settings' => array('file_view_mode' => 'default'),
    ),
  );

  return $formatters;
}

/**
 * Implements hook_field_formatter_settings_form().
 */
function mediafield_field_formatter_settings_form($field, $instance, $view_mode, $form, &$form_state) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

  $element = array();

  if ($display['type'] == 'media') {
    $entity_info = entity_get_info('file');
    $options = array('default' => t('Default'));
    foreach ($entity_info['view modes'] as $file_view_mode => $file_view_mode_info) {
      $options[$file_view_mode] = $file_view_mode_info['label'];
    }
    $element['file_view_mode'] = array(
      '#title' => t('File view mode'),
      '#type' => 'select',
      '#default_value' => $settings['file_view_mode'],
      '#options' => $options,
    );
  }

  return $element;
}

/**
 * Implements hook_field_formatter_settings_summary().
 */
function mediafield_field_formatter_settings_summary($field, $instance, $view_mode) {
  $display = $instance['display'][$view_mode];
  $settings = $display['settings'];

  $summary = '';

  if ($display['type'] == 'media') {
    $entity_info = entity_get_info('file');
    $file_view_mode_label = isset($entity_info['view modes'][$settings['file_view_mode']]) ? $entity_info['view modes'][$settings['file_view_mode']]['label'] : t('Default');
    $summary = t('File view mode: @view_mode', array('@view_mode' => $file_view_mode_label));
  }

  return $summary;
}

/**
 * Implements hook_field_formatter_view().
 */
function mediafield_field_formatter_view($entity_type, $entity, $field, $instance, $langcode, $items, $display) {
  $element = array();

  $files = array();
  foreach ($items as $delta => $item) {
    if (!empty($item['file'])) {
      $files[$item['fid']] = $item['file'];
    }
  }

  if (!empty($files)) {
    $output = file_view_multiple($files, $display['settings']['file_view_mode'], 0, $langcode);
    // Remove the first level from the output array.
    $element = reset($output);
  }

  return $element;
}

/**
 * Implements hook_field_prepare_view().
 */
function mediafield_field_prepare_view($entity_type, $entities, $field, $instances, $langcode, &$items) {
  // Collect all file IDs that need loading.
  $fids = array();
  foreach ($entities as $id => $entity) {
    // Load the files from the files table.
    foreach ($items[$id] as $delta => $item) {
      if (!empty($item['fid'])) {
        $fids[] = $item['fid'];
      }
    }
  }

  // Load the file entities.
  $files = file_load_multiple($fids);

  // Add the loaded file entities to the field item array.
  foreach ($entities as $id => $entity) {
    foreach ($items[$id] as $delta => $item) {
      // If the file does not exist, mark the entire item as empty.
      if (empty($files[$item['fid']])) {
        unset($items[$id][$delta]);
      }
      else {
        $items[$id][$delta]['file'] = $files[$item['fid']];
      }
    }
  }
}

/**
 * Implements hook_field_validate().
 *
 * Possible error codes:
 * - 'media_remote_file_type_not_allowed': The remote file is not an allowed
 *   file type.
 */
function mediafield_field_validate($obj_type, $object, $field, $instance, $langcode, $items, &$errors) {
  $allowed_types = array_keys(array_filter($instance['widget']['settings']['allowed_types']));

    // @TODO: merge in stuff from media_uri_value
  foreach ($items as $delta => $item) {
    if (empty($item['fid'])) {
      return TRUE;
      //@TODO: make support for submiting with just a URI here?
    }

    $file = file_load($item['fid']);

    // Only validate allowed types if the file is remote and not local.
    if (!file_entity_file_is_local($file)) {
      if (!in_array($file->type, $allowed_types)) {
        $errors[$field['field_name']][$langcode][$delta][] = array(
          'error' => 'media_remote_file_type_not_allowed',
          'message' => t('%name: Only remote files with the following types are allowed: %types-allowed.', array('%name' => t($instance['label']), '%types-allowed' => !empty($allowed_types) ? implode(', ', $allowed_types) : t('no file types selected'))),
        );
      }
    }
  }
}

/**
 * Implements_hook_field_widget_error().
 */
function mediafield_field_widget_error($element, $error, $form, &$form_state) {
  form_error($element['fid'], $error['message']);
}

/**
 * @todo The following hook_field_(insert|update|delete|delete_revision)
 *   implementations are nearly identical to the File module implementations of
 *   the same field hooks. The only differences are:
 *   - We pass 'media' rather than 'file' as the module argument to the
 *     file_usage_(add|delete)() functions.
 *   - We do not delete the file / media entity when its usage count goes to 0.
 *   We should submit a core patch to File module to make it flexible with
 *   respect to the above, so that we can reuse its implementation rather than
 *   duplicating it.
 */

/**
 * Implements hook_field_insert().
 */
function mediafield_field_insert($entity_type, $entity, $field, $instance, $langcode, &$items) {
  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);

  // Add a new usage of each uploaded file.
  foreach ($items as $item) {
    $file = (object) $item;
    file_usage_add($file, 'mediafield', $entity_type, $id);
  }
}

/**
 * Implements hook_field_update().
 *
 * Checks for files that have been removed from the object.
 */
function mediafield_field_update($entity_type, $entity, $field, $instance, $langcode, &$items) {
  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);

  // On new revisions, all files are considered to be a new usage and no
  // deletion of previous file usages are necessary.
  if (!empty($entity->revision)) {
    foreach ($items as $item) {
      $file = (object) $item;
      file_usage_add($file, 'mediafield', $entity_type, $id);
    }
    return;
  }

  // Build a display of the current FIDs.
  $current_fids = array();
  foreach ($items as $item) {
    $current_fids[] = $item['fid'];
  }

  // Compare the original field values with the ones that are being saved.
  $original_fids = array();
  if (!empty($entity->original->{$field['field_name']}[$langcode])) {
    foreach ($entity->original->{$field['field_name']}[$langcode] as $original_item) {
      $original_fids[] = $original_item['fid'];
      if (isset($original_item['fid']) && !in_array($original_item['fid'], $current_fids)) {
        // Decrement the file usage count by 1.
        $file = (object) $original_item;
        file_usage_delete($file, 'mediafield', $entity_type, $id, 1);
      }
    }
  }

  // Add new usage entries for newly added files.
  foreach ($items as $item) {
    if (!in_array($item['fid'], $original_fids)) {
      $file = (object) $item;
      file_usage_add($file, 'mediafield', $entity_type, $id);
    }
  }
}

/**
 * Implements hook_field_delete().
 */
function mediafield_field_delete($entity_type, $entity, $field, $instance, $langcode, &$items) {
  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);

  // Delete all file usages within this entity.
  foreach ($items as $delta => $item) {
    $file = (object) $item;
    file_usage_delete($file, 'mediafield', $entity_type, $id, 0);
  }
}

/**
 * Implements hook_field_delete_revision().
 */
function mediafield_field_delete_revision($entity_type, $entity, $field, $instance, $langcode, &$items) {
  list($id, $vid, $bundle) = entity_extract_ids($entity_type, $entity);
  foreach ($items as $delta => $item) {
    // @TODO: Not sure if this is correct
    $file = (object)$item;
    if (file_usage_delete($file, 'mediafield', $entity_type, $id, 1)) {
      $items[$delta] = NULL;
    }
  }
}

/**
 * Implements hook_views_api().
 */
function mediafield_views_api() {
  return array(
    'api' => 3,
  );
}
