<?php
/**
 * @file
 * Slick carousel integration, the last carousel you'll ever need.
 */

/**
 * Implements hook_theme().
 */
function slick_theme($existing, $type, $theme, $path) {
  $base = array(
    'file' => 'slick.theme.inc',
    'path' => $path . '/templates',
  );

  $themes = array();
  foreach (array('slick', 'item', 'grid', 'media', 'wrapper') as $item) {
    $key = $item == 'slick' ? $item : "slick_$item";
    $themes[$key] = $base + array(
      'render element' => 'element',
    );
    if ($item != 'wrapper') {
      $themes[$key]['template'] = strtr($key, '_', '-');
    }
  }

  $themes['slick_image_lazy'] = $base + array(
    'variables' => array(
      'item'        => NULL,
      'lazy'        => FALSE,
      'image_style' => NULL,
      'url'         => NULL,
      'attributes'  => array(),
      'options'     => array(),
    ),
  );

  return $themes;
}

/**
 * Implements hook_ctools_plugin_api().
 */
function slick_ctools_plugin_api($owner, $api) {
  if ($owner == 'slick' && $api == 'slick_default_preset') {
    return array('version' => 1);
  }
}

/**
 * Implements hook_hook_info().
 */
function slick_hook_info() {
  $hooks['slick_arrows_info'] = array('group' => 'slick');
  $hooks['slick_dots_info']   = array('group' => 'slick');
  $hooks['slick_skins_info']  = array('group' => 'slick');
  return $hooks;
}

/**
 * Implements hook_library().
 */
function slick_library() {
  $library = libraries_get_path('slick');
  $path = drupal_get_path('module', 'slick');
  $info = system_get_info('module', 'slick');
  $components = $path . '/css/components';
  $common = array(
    'website' => 'http://drupal.org/project/slick',
    'version' => !empty($info['version']) ? $info['version'] : '7.x-2.x',
  );

  $libraries['slick'] = array(
    'title' => 'Slick',
    'website' => 'http://kenwheeler.github.io/slick/',
    'version' => '1.x',
    'js' => array(
      $library . '/slick/slick.min.js'  => array(),
    ),
    'css' => array(
      $library . '/slick/slick.css' => array(),
    ),
  );

  $libraries['slick.colorbox'] = $common + array(
    'title' => 'Slick colorbox',
    'js' => array(
      $path . '/js/slick.colorbox.min.js' => array('group' => JS_DEFAULT, 'weight' => 1),
    ),
    'css' => array(
      $components . '/slick.colorbox.css' => array(),
      $components . '/slick.lightbox.css' => array(),
    ),
  );

  $libraries['slick.photobox'] = $common + array(
    'title' => 'Slick photobox',
    'js' => array(
      $path . '/js/slick.photobox.min.js' => array('group' => JS_DEFAULT, 'weight' => 1),
    ),
    'css' => array($components . '/slick.lightbox.css' => array()),
    'dependencies' => array(array('photobox', 'photobox')),
  );

  $libraries['slick.media'] = $common + array(
    'title' => 'Slick media',
    'js' => array(
      $path . '/js/slick.media.min.js' => array('group' => JS_DEFAULT, 'weight' => -1),
    ),
    'css' => array($components . '/slick.media.css' => array()),
  );

  return $libraries;
}

/**
 * Returns the cacheable supported JS and CSS assets for the given slick.
 *
 * @param array $attach
 *   An array of conditions to load the relevant assets, empty means basic.
 * @param array $settings
 *   An array of settings to check for the supported features.
 *
 * @return array
 *   The cacheable array formatted for the '#attached' property.
 */
function slick_attach(array $attach, $settings = array()) {
  $path  = drupal_get_path('module', 'slick');
  $skins = slick_skins();
  $load  = array();
  $load['css'] = $load['js'] = array();
  $attach += array(
    'attach_slick_css'  => variable_get('slick_css', TRUE),
    'attach_module_css' => variable_get('slick_module_css', TRUE),
    'attach_module_js'  => TRUE,
  );
  drupal_alter('slick_attach_info', $attach, $settings);

  if ($easing = libraries_get_path('easing')) {
    $load['js'] += array($easing . '/jquery.easing.min.js' => array('group' => JS_LIBRARY, 'weight' => -6));
  }

  $load['library'][] = array('slick', 'slick');
  if ($attach['attach_module_js']) {
    $load['js'] += array($path . '/js/slick.load.min.js' => array('weight' => 0));
  }

  $components = array('colorbox', 'photobox', 'media');
  foreach ($components as $component) {
    if (isset($attach['attach_' . $component]) && $attach['attach_' . $component]) {
      $load['library'][] = array('slick', 'slick.' . $component);
    }
  }

  if (isset($attach['attach_skin']) && $skin = $attach['attach_skin']) {
    if (isset($skins[$skin]['css'])) {
      if ($attach['attach_slick_css']) {
        $load['css'] += array(libraries_get_path('slick') . '/slick/slick-theme.css' => array('weight' => -100));
      }
      if ($attach['attach_module_css']) {
        $load['css'] += array($path . '/css/theme/slick.theme.css' => array('weight' => -99));
      }
      if (is_array($skins[$skin]['css'])) {
        $load['css'] += $skins[$skin]['css'];
      }
    }
    if (isset($skins[$skin]['js']) && is_array($skins[$skin]['js'])) {
      $load['js'] += $skins[$skin]['js'];
    }
  }

  $navs = array('thumbnail', 'arrows', 'dots');
  foreach ($navs as $nav) {
    if (isset($attach['attach_skin_' . $nav]) && $skin = $attach['attach_skin_' . $nav]) {
      $nav_skins = $nav == 'arrows' ? slick_arrows() : ($nav == 'dots' ? slick_dots() : $skins);
      if (isset($nav_skins[$skin]['css']) && is_array($nav_skins[$skin]['css'])) {
        $load['css'] += $nav_skins[$skin]['css'];
      }
    }
  }

  // Attach default JS settings to allow responsive displays have a lookup,
  // excluding wasted+/trouble options, e.g.: PHP string vs JS object.
  $defaults = slick_get_options();
  $excludes = explode(' ', 'mobileFirst appendArrows appendDots asNavFor prevArrow nextArrow cssEaseBezier cssEaseOverride respondTo');
  $load['js'][] = array(
    'data' => array('slick' => array_diff_key($defaults, drupal_map_assoc($excludes))),
    'type' => 'setting',
  );

  drupal_alter('slick_attach_load_info', $load, $attach, $skins, $settings);
  return $load;
}

/**
 * Returns an array of options available for a Slick instance.
 */
function slick_get_options() {
  $options = &drupal_static(__FUNCTION__);
  if (!isset($options)) {
    $options = array(
      'mobileFirst'      => FALSE,
      'asNavFor'         => '',
      'accessibility'    => TRUE,
      'adaptiveHeight'   => FALSE,
      'autoplay'         => FALSE,
      'autoplaySpeed'    => 3000,
      'pauseOnHover'     => TRUE,
      'pauseOnDotsHover' => FALSE,
      'arrows'           => TRUE,
      'appendArrows'     => '$(element)',
      'prevArrow'        => '<button type="button" data-role="none" class="slick-prev" aria-label="previous">Previous</button>',
      'nextArrow'        => '<button type="button" data-role="none" class="slick-next" aria-label="next">Next</button>',
      'centerMode'       => FALSE,
      'centerPadding'    => '50px',
      'dots'             => FALSE,
      'dotsClass'        => 'slick-dots',
      'appendDots'       => '$(element)',
      'draggable'        => TRUE,
      'fade'             => FALSE,
      'focusOnSelect'    => FALSE,
      'infinite'         => TRUE,
      'initialSlide'     => 0,
      'lazyLoad'         => 'ondemand',
      'respondTo'        => 'window',
      'rtl'              => FALSE,
      'rows'             => 1,
      'slidesPerRow'     => 1,
      'slide'            => '',
      'slidesToShow'     => 1,
      'slidesToScroll'   => 1,
      'speed'            => 500,
      'swipe'            => TRUE,
      'swipeToSlide'     => FALSE,
      'edgeFriction'     => 0.35,
      'touchMove'        => TRUE,
      'touchThreshold'   => 5,
      'useCSS'           => TRUE,
      'cssEase'          => 'ease',
      'cssEaseBezier'    => '',
      'cssEaseOverride'  => '',
      'useTransform'     => FALSE,
      'easing'           => 'linear',
      'variableWidth'    => FALSE,
      'vertical'         => FALSE,
      'verticalSwiping'  => FALSE,
      'waitForAnimate'   => TRUE,
      'mousewheel'       => FALSE,
    );
    drupal_alter('slick_options_info', $options);
  }
  return $options;
}

/**
 * Returns an array of skins registered via hook_slick_skins_info().
 */
function slick_skins() {
  $skins = &drupal_static(__FUNCTION__, NULL);
  if (!isset($skins)) {
    $skins = module_invoke_all('slick_skins_info');
    drupal_alter('slick_skins_info', $skins);
  }
  return $skins;
}

/**
 * Returns an array of arrow skins registered via hook_slick_arrows_info().
 */
function slick_arrows() {
  $arrows = &drupal_static(__FUNCTION__, NULL);
  if (!isset($arrows)) {
    $arrows = module_invoke_all('slick_arrows_info');
    drupal_alter('slick_arrows_info', $arrows);
  }
  return $arrows;
}

/**
 * Returns an array of dot skins registered via hook_slick_dots_info().
 */
function slick_dots() {
  $dots = &drupal_static(__FUNCTION__, NULL);
  if (!isset($dots)) {
    $dots = module_invoke_all('slick_dots_info');
    drupal_alter('slick_dots_info', $dots);
  }
  return $dots;
}

/**
 * Fetches the given optionset object identified by $id.
 *
 * @param string $id
 *   The optionset ID.
 *
 * @return object
 *   Returns the optionset, or else default, if no optionset found.
 */
function slick_optionset_load($id) {
  ctools_include('export');
  $optionset = ctools_export_crud_load('slick_optionset', $id);

  // Ensures deleted optionset while being used doesn't screw up.
  if (!isset($optionset->name)) {
    $optionset = ctools_export_crud_load('slick_optionset', 'default');
    watchdog('slick', 'Fallback to default optionset.', array(), WATCHDOG_WARNING);
  }
  return $optionset;
}

/**
 * Creates a new optionset object without saving it to the database.
 *
 * @param array $values
 *   The values to build the optionset if provided.
 *
 * @return object
 *   Returns the programmatically created optionset object.
 */
function slick_optionset_create($values = array()) {
  ctools_include('export');
  $optionset = ctools_export_crud_new('slick_optionset');

  $optionset->options = $optionset->options['settings'] = array();
  $optionset->breakpoints = 0;
  foreach (array('name', 'label', 'skin', 'breakpoints', 'options') as $key) {
    if (isset($values[$key])) {
      $optionset->{$key} = $values[$key];
    }
  }

  $defaults['general']  = array('goodies' => array());
  $defaults['settings'] = slick_get_options();
  $optionset->options   = $optionset->options + $defaults;
  return $optionset;
}

/**
 * Returns a cacheable renderable array of a simple slick instance.
 *
 * @param array $items
 *   An array of slick contents: text, image or media.
 * @param array $options
 *   An array of key:value pairs of custom JS options to override $optionset.
 * @param array $settings
 *   An array of key:value pairs of HTML/layout related settings.
 * @param array $attach
 *   An array of assets to load using slick_attach($attach), or regular array.
 * @param string $id
 *   An optional ID for the instance.
 * @param object $optionset
 *   The cached optionset object to avoid possible multiple invocations.
 *
 * @return array
 *   The cacheable renderable array of a slick instance, or empty array.
 */
function slick_build($items = array(), $options = array(), $settings = array(), $attach = array(), $id = NULL, $optionset = NULL) {
  if (empty($items)) {
    return array();
  }

  $slick_id = &drupal_static('slick_id', 0);
  $fallback = isset($settings['attributes']['id']) ? $settings['attributes']['id'] : 'slick-' . ++$slick_id;
  $id       = $settings['attributes']['id'] = empty($id) ? $fallback : $id;
  $display  = empty($settings['current_display']) ? 'main' : $settings['current_display'];

  $slick = array(
    '#theme'     => 'slick',
    '#items'     => $items,
    '#options'   => $options,
    '#optionset' => $optionset,
    '#settings'  => $settings,
  );

  if ($display != 'thumbnail') {
    $custom = isset($attach['css']) || isset($attach['js']) || isset($attach['library']);
    $slick['#attached'] = $custom ? $attach : slick_attach($attach, $settings);
  }

  // Cache the render array if so configured.
  if (!empty($settings['cache'])) {
    $cid    = empty($settings['cid']) ? $id . ':custom' : $id . ':' . $settings['cid'];
    $cache  = cache_get($cid, 'cache');
    $active = $settings['cache'] == 'persistent' ? TRUE : (!$cache ? TRUE : REQUEST_TIME < $cache->expire);

    if (!$cache || $active) {
      $slick['#cache'] = array(
        'cid'    => $cid,
        'expire' => $settings['cache'] == 'persistent' ? CACHE_TEMPORARY : REQUEST_TIME + $settings['cache'],
      );
    }
  }
  return $slick;
}
