<?php

/**
 * @file
 * Breakpoints
 * @todo: provide button to reload breakpoints from theme
 */

define('BREAKPOINTS_SOURCE_TYPE_THEME', 'theme');
define('BREAKPOINTS_SOURCE_TYPE_MODULE', 'module');
define('BREAKPOINTS_SOURCE_TYPE_CUSTOM', 'custom');
define('BREAKPOINTS_GROUP', 'group');

/**
 * Implements hook_permission().
 */
function breakpoints_permission() {
  return array(
    'administer breakpoints' => array(
      'title' => t('Administer Breakpoints'),
      'description' => t('Administer all breakpoints'),
    ),
  );
}

/**
 * Implements hook_ctools_plugin_directory().
 */
function breakpoints_ctools_plugin_directory($module, $plugin) {
  if ($module == 'ctools' && $plugin == 'export_ui') {
    return 'plugins/' . $plugin;
  }
}

/**
 * Implements hook_ctools_plugin_api().
 */
function breakpoints_ctools_plugin_api($owner, $api) {
  return array('version' => 1);
}

/**
 * Implements hook_enable().
 * Import breakpoints from all enabled themes.
 */
function breakpoints_enable() {
  $themes = list_themes();
  foreach ($themes as $theme_key => $theme) {
    if (!$theme->status) {
      unset($themes[$theme_key]);
    }
  }
  breakpoints_themes_enabled(array_keys($themes));
}

/**
 * Implements hook_themes_enabled();
 * Import breakpoints from all new enabled themes.
 * Do not use breakpoints_breakpoints_group_reload_from_theme as is clears the cache.
 */
function breakpoints_themes_enabled($theme_list) {
  $themes = list_themes();
  foreach ($theme_list as $theme_key) {
    if (isset($themes[$theme_key]->info['breakpoints'])) {
      $weight = 0;
      $theme_settings = $themes[$theme_key]->info['breakpoints'];
      $multipliers = isset($themes[$theme_key]->info['multipliers']) ? $themes[$theme_key]->info['multipliers'] : array();
      $settings = breakpoints_settings();
      $current_multipliers = drupal_map_assoc($settings->multipliers);
      // Build a group for each theme
      $breakpoint_group = breakpoints_breakpoint_group_empty_object();
      $breakpoint_group->machine_name = $theme_key;
      $breakpoint_group->name = $themes[$theme_key]->info['name'];
      $breakpoint_group->type = BREAKPOINTS_SOURCE_TYPE_THEME;
      foreach ($theme_settings as $name => $media_query) {
        $breakpoint = breakpoints_breakpoint_load($name, $theme_key, 'theme');
        if (!$breakpoint) {
          $breakpoint = breakpoints_breakpoint_empty_object();
          $breakpoint->name = $name;
          $breakpoint->source = $theme_key;
          $breakpoint->source_type = 'theme';
          $breakpoint->theme = '';
          $breakpoint->status = TRUE;
          $breakpoint->weight = $weight++;
          $breakpoint->machine_name = breakpoints_breakpoint_config_name($breakpoint);
        }
        $breakpoint->breakpoint = $media_query;
        $breakpoint->multipliers = isset($multipliers[$name]) ? drupal_map_assoc($multipliers[$name]) : array();
        $current_multipliers += drupal_map_assoc($breakpoint->multipliers);
        breakpoints_breakpoint_save($breakpoint);
        $breakpoint_group->breakpoints[] = $breakpoint->machine_name;
      }
      breakpoints_settings_save($current_multipliers);
      breakpoints_breakpoint_group_save($breakpoint_group);
      $message = t('The breakpoints from theme %theme are imported and !grouplink.', array(
        '%theme' => check_plain($themes[$theme_key]->info['name']),
        '!grouplink' => l(t('a new group is created'), 'admin/config/media/breakpoints/groups/' . $theme_key),
      ));
      drupal_set_message($message, 'status');
    }
  }
  menu_rebuild();
}

/**
 * Implements hook_themes_disabled();
 * Remove breakpoints from all disabled themes.
 */
function breakpoints_themes_disabled($theme_list) {
  $themes = list_themes();
  foreach ($theme_list as $theme_key) {
    $breakpoints = breakpoints_breakpoint_load_all_theme($theme_key);
    foreach ($breakpoints as $breakpoint) {
      breakpoints_breakpoint_delete($breakpoint, $theme_key);
    }
    breakpoints_breakpoint_group_delete_by_name($theme_key);
  }
  menu_rebuild();
}

/**
 * Implements hook_menu().
 */
function breakpoints_menu() {
  $items = array();

  // @todo: link to all breakpoints and a list of all groups
  // cf theme settings page
  $items['admin/config/media/breakpoints'] = array(
    'title' => 'Breakpoints',
    'description' => 'Manage breakpoints',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('breakpoints_admin_breakpoints'),
    'access arguments' => array('administer breakpoints'),
    'file' => 'breakpoints.admin.inc',
  );

  $items['admin/config/media/breakpoints/create_style'] = array(
    'title' => 'Add responsive style',
    'description' => 'Add a responsive image style',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('breakpoints_add_style_form'),
    'access arguments' => array('administer breakpoints'),
    'file' => 'breakpoints.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 30,
  );

  $items['admin/config/media/breakpoints/multipliers'] = array(
    'title' => 'Multipliers',
    'description' => 'Manage multipliers',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('breakpoints_multipliers_form'),
    'access arguments' => array('administer breakpoints'),
    'file' => 'breakpoints.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 20,
  );

  $items['admin/config/media/breakpoints/settings'] = array(
    'title' => 'settings',
    'description' => 'Manage breakpoint settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('breakpoints_admin_settings_form'),
    'access arguments' => array('administer breakpoints'),
    'file' => 'breakpoints.admin.inc',
    'type' => MENU_LOCAL_TASK,
    'weight' => 30,
  );

  $items['admin/config/media/breakpoints/multipliers/%/delete'] = array(
    'title' => '',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('breakpoints_admin_multiplier_delete_form', 5),
    'type' => MENU_CALLBACK,
    'access arguments' => array('administer breakpoints'),
    'file' => 'breakpoints.admin.inc',
    'weight' => 15,
  );

  $items['admin/config/media/breakpoints/groups'] = array(
    'title' => 'Groups',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => 10,
  );

  $items['admin/config/media/breakpoints/groups/global'] = array(
    'title' => 'All breakpoints',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -1,
  );

  $items['admin/config/media/breakpoints/groups/add'] = array(
    'title' => 'Add a new group',
    'page arguments' => array('breakpoints_admin_breakpoint_group_edit_form'),
    'type' => MENU_LOCAL_TASK,
    'access arguments' => array('administer breakpoints'),
    'file' => 'breakpoints.admin.inc',
    'weight' => 99,
  );

  $items['admin/config/media/breakpoints/groups/import'] = array(
    'title' => 'Import a new group',
    'page arguments' => array('breakpoints_admin_breakpoint_group_import_form'),
    'type' => MENU_LOCAL_TASK,
    'access arguments' => array('administer breakpoints'),
    'file' => 'breakpoints.admin.inc',
    'weight' => 99,
  );

  $items['admin/config/media/breakpoints/groups/import-breakpoint'] = array(
    'title' => 'Import a new breakpoint',
    'page arguments' => array('breakpoints_admin_breakpoint_import_form'),
    'type' => MENU_LOCAL_TASK,
    'access arguments' => array('administer breakpoints'),
    'file' => 'breakpoints.admin.inc',
    'weight' => 99,
  );

  $items['admin/config/media/breakpoints/%/%'] = array(
    'title' => '',
    'page callback' => 'breakpoints_admin_breakpoint_actions_page',
    'page arguments' => array('', 4, 5),
    'type' => MENU_CALLBACK,
    'access arguments' => array('administer breakpoints'),
    'file' => 'breakpoints.admin.inc',
    'weight' => 15,
  );

  $breakpoint_groups = breakpoints_breakpoint_group_load_all();
  foreach ($breakpoint_groups as $breakpoint_group_name => $breakpoint_group) {
    if (!empty($breakpoint_group->machine_name)) {
      $items['admin/config/media/breakpoints/groups/' . $breakpoint_group->machine_name] = array(
        'title' => $breakpoint_group->name,
        'page arguments' => array('breakpoints_admin_breakpoints', $breakpoint_group->machine_name),
        'type' => MENU_LOCAL_TASK,
        'access arguments' => array('administer breakpoints'),
        'file' => 'breakpoints.admin.inc',
        'weight' => 15,
      );
      $items['admin/config/media/breakpoints/groups/' . $breakpoint_group->machine_name . '/edit'] = array(
        'title' => 'Edit ' . $breakpoint_group->name,
        'page arguments' => array('breakpoints_admin_breakpoint_group_edit_form', $breakpoint_group->machine_name),
        'type' => MENU_CALLBACK,
        'access arguments' => array('administer breakpoints'),
        'file' => 'breakpoints.admin.inc',
        'weight' => 15,
      );
      $items['admin/config/media/breakpoints/groups/' . $breakpoint_group->machine_name . '/delete'] = array(
        'title' => 'Delete ' . $breakpoint_group->name,
        'page arguments' => array('breakpoints_admin_breakpoint_group_delete_form', $breakpoint_group->machine_name),
        'type' => MENU_CALLBACK,
        'access arguments' => array('administer breakpoints'),
        'file' => 'breakpoints.admin.inc',
        'weight' => 15,
      );

      $items['admin/config/media/breakpoints/groups/' . $breakpoint_group->machine_name . '/export'] = array(
        'title' => 'Export ' . $breakpoint_group->name,
        'page arguments' => array('breakpoints_admin_breakpoint_group_export_form', $breakpoint_group->machine_name),
        'type' => MENU_LOCAL_ACTION,
        'access arguments' => array('administer breakpoints'),
        'file' => 'breakpoints.admin.inc',
        'weight' => 15,
      );

      $items['admin/config/media/breakpoints/groups/' . $breakpoint_group->machine_name . '/duplicate'] = array(
        'title' => 'Duplicate ' . $breakpoint_group->name,
        'page arguments' => array('breakpoints_admin_breakpoint_group_duplicate_form', $breakpoint_group->machine_name),
        'type' => MENU_CALLBACK,
        'access arguments' => array('administer breakpoints'),
        'file' => 'breakpoints.admin.inc',
        'weight' => 15,
      );

      $items['admin/config/media/breakpoints/groups/' . $breakpoint_group->machine_name . '/%/%'] = array(
        'title' => '',
        'page arguments' => array('breakpoints_admin_breakpoint_actions_form', $breakpoint_group->machine_name, 6, 7),
        'type' => MENU_CALLBACK,
        'access arguments' => array('administer breakpoints'),
        'file' => 'breakpoints.admin.inc',
        'weight' => 15,
      );
    }
  }

  return $items;
}

/**
 * Load general settings.
 */
function breakpoints_settings() {
  $config = new StdClass;
  $config->multipliers = variable_get('breakpoints_multipliers', array('1x', '1.5x', '2x'));
  return $config;
}

/**
 * Save general settings.
 */
function breakpoints_settings_save($multipliers) {
  variable_set('breakpoints_multipliers', $multipliers);
}

/**
 * Sort breakpoints by weight.
 */
function _breakpoints_sort_by_weight($a, $b) {
  if (isset($a->weight) && isset($b->weight)) {
    if ($a->weight == $b->weight) {
      if (isset($a->source_type) && $a->source_type == BREAKPOINTS_SOURCE_TYPE_CUSTOM) {
        return -1;
      }
      if (isset($b->source_type) && $b->source_type == BREAKPOINTS_SOURCE_TYPE_CUSTOM) {
        return 1;
      }
      return 0;
    }
    return ($a->weight < $b->weight) ? -1 : 1;
  }
  return 0;
}

/**
 * Sort breakpoints by weight.
 */
function _breakpoints_sort_by_weight_array($a, $b) {
  return _breakpoints_sort_by_weight((object)$a, (object)$b);
}

/**
 * Construct config name.
 */
function breakpoints_breakpoint_config_name($breakpoints_breakpoint) {
  if (is_string($breakpoints_breakpoint)) {
    return $breakpoints_breakpoint;
  }
  else {
    return drupal_strtolower('breakpoints'
      . '.' . $breakpoints_breakpoint->source_type
      . '.' . $breakpoints_breakpoint->source
      . '.' . $breakpoints_breakpoint->name);
  }
}

/**
 * Load a single breakpoint.
 */
function breakpoints_breakpoint_load($name, $source, $source_type) {
  $key = drupal_strtolower('breakpoints.' . $source_type . '.' . $source . '.' . $name);
  return breakpoints_breakpoint_load_by_fullkey($key);
}

/**
 * Load a single breakpoint using the full config key.
 */
function breakpoints_breakpoint_load_by_fullkey($machine_name = NULL) {
  // Use Ctools export API to fetch all presets from the DB as well as code.
  ctools_include('export');
  if ($machine_name) {
    $breakpoints = ctools_export_load_object('breakpoints', 'names', array($machine_name));
    $breakpoint = isset($breakpoints[$machine_name]) ? $breakpoints[$machine_name] : FALSE;
    return $breakpoint;
  }
  else {
    $breakpoints = ctools_export_load_object('breakpoints');
    return $breakpoints;
  }
}

/**
 * Load all breakpoints.
 */
function breakpoints_breakpoint_load_all($theme_key = '') {
  $breakpoints_user = breakpoints_breakpoint_load_all_custom();
  $breakpoints_module = breakpoints_breakpoint_load_all_module();
  $breakpoints_theme = breakpoints_breakpoint_load_all_theme($theme_key);
  $breakpoints = array_merge($breakpoints_theme, $breakpoints_module, $breakpoints_user);
  uasort($breakpoints, '_breakpoints_sort_by_weight');
  return $breakpoints;
}

/**
 * Load all enabled breakpoints.
 */
function breakpoints_breakpoint_load_all_active($theme_key = '') {
  $breakpoints = breakpoints_breakpoint_load_all($theme_key);
  $enabled = array();
  if (!empty($breakpoints)) {
    foreach ($breakpoints as $breakpoint_name => $breakpoint) {
      if ($breakpoint->status) {
        $enabled[$breakpoint_name] = $breakpoint;
      }
    }
  }
  return $enabled;
}

/**
 * Load all breakpoints by source type.
 */
function _breakpoints_breakpoint_load_all_by_type($source_type, $source = '') {
  $breakpoints = breakpoints_breakpoint_load_by_fullkey();
  foreach ($breakpoints as $machine_name => $breakpoint) {
    if ($breakpoint->source_type != $source_type) {
      unset($breakpoints[$machine_name]);
      continue;
    }
    if ($source != '' && $breakpoint->source != $source) {
      unset($breakpoints[$machine_name]);
    }
  }
  return $breakpoints;
}

/**
 * Load all custom breakpoints.
 */
function breakpoints_breakpoint_load_all_custom() {
  $breakpoints = _breakpoints_breakpoint_load_all_by_type(BREAKPOINTS_SOURCE_TYPE_CUSTOM);
  return $breakpoints;
}

/**
 * Load all user defined breakpoints.
 */
function breakpoints_breakpoint_load_all_module() {
  return _breakpoints_breakpoint_load_all_by_type(BREAKPOINTS_SOURCE_TYPE_MODULE);
}

/**
 * Load all breakpoints from the theme.
 */
function breakpoints_breakpoint_load_all_theme($theme_key = '') {
  return _breakpoints_breakpoint_load_all_by_type(BREAKPOINTS_SOURCE_TYPE_THEME, $theme_key);
}

/**
 * Empty breakpoint object.
 */
function breakpoints_breakpoint_empty_object() {
  return (object)breakpoints_breakpoint_empty_array();
}

/**
 * Empty breakpoint array.
 */
function breakpoints_breakpoint_empty_array() {
  $config = breakpoints_settings();
  return array(
    'name' => '',
    'machine_name' => '',
    'breakpoint' => '',
    'source' => '',
    'source_type' => '',
    'status' => TRUE,
    'weight' => 0,
    'multipliers' => array(),
  );
}

/**
 * Save a single breakpoint.
 */
function breakpoints_breakpoint_save(&$breakpoint) {
  if (!isset($breakpoint->machine_name) || empty($breakpoint->machine_name)) {
    $breakpoint->machine_name = breakpoints_breakpoint_config_name($breakpoint);
  }
  $update = (isset($breakpoint->id) && is_numeric($breakpoint->id)) ? array('id') : array();
  // Remove unused multipliers.
  $breakpoint->multipliers = array_filter($breakpoint->multipliers);
  if(is_null($breakpoint->multipliers)){
    $breakpoint->multipliers = array();
  }
  // Add the '1x' multiplier.
  $breakpoint->multipliers = array_merge($breakpoint->multipliers, array('1x' => '1x'));
  $result = drupal_write_record('breakpoints', $breakpoint, $update);
  // Reset CTools cache.
  ctools_export_load_object_reset('breakpoints');
  return $result;
}

/**
 * Delete a single breakpoint.
 */
function breakpoints_breakpoint_delete($breakpoint) {
  $name = breakpoints_breakpoint_config_name($breakpoint);
  return breakpoints_breakpoint_delete_by_fullkey($name);
}

/**
 * Delete a single breakpoint.
 */
function breakpoints_breakpoint_delete_by_fullkey($key) {
  if (!empty($key)) {
    $sql = "DELETE FROM {breakpoints} where machine_name = :key";
    db_query($sql, array(':key' => $key));
  }
  // Clear the Ctools export API cache.
  ctools_include('export');
  ctools_export_load_object_reset('breakpoints');
}

/**
 * Toggle status of a single breakpoint.
 */
function breakpoints_breakpoint_toggle_status($machine_name) {
  $breakpoint = breakpoints_breakpoint_load_by_fullkey($machine_name);
  if ($breakpoint) {
    $breakpoint->status = !$breakpoint->status;
    breakpoints_breakpoint_save($breakpoint);
  }
}

/**
 * Check if a breakpoint name already exists.
 */
function breakpoints_breakpoint_name_exists($machine_name) {
  $breakpoints = breakpoints_breakpoint_load_all_custom();
  $fullkey = 'custom.user.' . $machine_name;
  return array_key_exists($fullkey, $breakpoints);
}

/**
 * Check if a breakpoint machine name already exists.
 */
function breakpoints_breakpoint_machine_name_exists($machine_name) {
  // Just try to load the breakpoint object, we profit from ctool's cache mechanism,
  // better that doing a query to the db every time this function is called.
  return (bool)breakpoints_breakpoint_load_by_fullkey($machine_name);
}

/**
 * Empty breakpoint group object.
 */
function breakpoints_breakpoint_group_empty_object() {
  return (object)breakpoints_breakpoint_group_empty_array();
}

/**
 * Empty breakpoint group array.
 */
function breakpoints_breakpoint_group_empty_array() {
  return array(
    'machine_name' => '',
    'name' => '',
    'breakpoints' => array(),
    'type' => 'custom',
  );
}

/**
 * Check if a group name already exists.
 */
function breakpoints_breakpoint_group_name_exists($machine_name) {
  // Check for reserved words.
  if ($machine_name == 'global' || $machine_name == 'add') {
    return TRUE;
  }
  // Check if group name is used before.
  $group_check = breakpoints_breakpoint_group_load($machine_name);
  if ($group_check && isset($group_check->machine_name) && !empty($group_check->machine_name)) {
    return TRUE;
  }
  return FALSE;
}

/**
 * Load all breakpoint groups.
 */
function breakpoints_breakpoint_group_load_all() {
  return breakpoints_breakpoint_group_load();
}

/**
 * Load a single breakpoint group.
 */
function breakpoints_breakpoint_group_load($name = NULL) {
  // Use Ctools export API to fetch all presets from the DB as well as code.
  ctools_include('export');
  if ($name) {
    $groups = ctools_export_load_object('breakpoint_group', 'names', array($name));
    $group = isset($groups[$name]) ? $groups[$name] : FALSE;
    return $group;
  }
  else {
    $groups = ctools_export_load_object('breakpoint_group');
    return $groups;
  }
}

/**
 * Validate a single breakpoint group.
 */
function breakpoints_breakpoint_group_validate($group) {
  if (!is_object($group)) {
    return FALSE;
  }
  foreach (array('machine_name', 'name', 'breakpoints', 'type') as $property) {
    if (!property_exists($group, $property)) {
      return FALSE;
    }
  }
  return TRUE;
}

/**
 * Validate a single breakpoint.
 */
function breakpoints_breakpoint_validate($breakpoint) {
  if (!is_object($breakpoint)) {
    return FALSE;
  }
  foreach (array_keys(breakpoints_breakpoint_empty_array()) as $property) {
    if (!property_exists($breakpoint, $property)) {
      return FALSE;
    }
  }
  return TRUE;
}

/**
 * Save a single breakpoint group.
 */
function breakpoints_breakpoint_group_save(&$breakpoint_group) {
  $update = (isset($breakpoint_group->id) && is_numeric($breakpoint_group->id)) ? array('id') : array();
  $result = drupal_write_record('breakpoint_group', $breakpoint_group, $update);
  // rebuild menu if we add a new group
  if (empty ($update)) {
    menu_rebuild();
  }
  // Reset CTools cache.
  ctools_export_load_object_reset('breakpoint_group');
  return $result;
}

/**
 * Delete a single breakpoint group.
 */
function breakpoints_breakpoint_group_delete($breakpoint_group) {
  $name = $breakpoint_group->machine_name;
  return breakpoints_breakpoint_group_delete_by_fullkey($name);
}

/**
 * Delete a single breakpoint group by fullkey.
 */
function breakpoints_breakpoint_group_delete_by_name($machine_name) {
  $name = $machine_name;
  return breakpoints_breakpoint_group_delete_by_fullkey($name);
}

/**
 * Delete a single breakpoint group by fullkey.
 */
function breakpoints_breakpoint_group_delete_by_fullkey($key) {
  if (!empty($key)) {
    $sql = "DELETE FROM {breakpoint_group} where machine_name = :key";
    db_query($sql, array(':key' => $key));
  }
  // Clear the Ctools export API cache.
  ctools_include('export');
  ctools_export_load_object_reset('breakpoint_group');
  menu_rebuild();
}

/**
 * Implements hook_theme().
 */
function breakpoints_theme() {
  return array(
    'breakpoints_admin_breakpoints_table' => array(
      'render element' => 'form',
      'theme_key' => NULL,
    ),
    'breakpoints_multipliers_table_form' => array(
      'render element' => 'form',
      'theme_key' => NULL,
    )
  );
}

/**
 * Export callback.
 * @see breakpoints_schema()
 */
function breakpoint_group_export_breakpoints($object, $field, $value, $indent) {
  ctools_include('export');
  $export = array();
  if (isset($object->breakpoints)) {
    foreach ($object->breakpoints as $breakpoint) {
      if (!is_array($breakpoint) && !is_object($breakpoint)) {
        $breakpoint = breakpoints_breakpoint_load_by_fullkey($breakpoint);
      }
      $export[] = $breakpoint->machine_name;
    }
  }
  return ctools_var_export($export, $indent);
}

/**
 * Reload the breakpoints as defined by the group.
 */
function breakpoints_breakpoints_group_reload(&$group, $force = TRUE) {
  switch ($group->type) {
    case BREAKPOINTS_SOURCE_TYPE_THEME:
      // delete all breakpoints defined by this theme.
      $breakpoints = breakpoints_breakpoint_load_all_theme($group->machine_name);
      foreach ($breakpoints as $breakpoint) {
        breakpoints_breakpoint_delete($breakpoint);
      }

      // Reload all breakpoints from theme.info.
      $reloaded_group = breakpoints_breakpoints_group_reload_from_theme($group->machine_name);

      // Reset the breakpoints for this group.
      if ($force) {
        $group->breakpoints = $reloaded_group->breakpoints;
        breakpoints_breakpoint_group_save($group);
      }
      break;
  }
}

function breakpoints_breakpoints_group_reload_from_theme($theme_key) {
  // Clear caches so theme.info is fresh.
  system_rebuild_theme_data();
  drupal_theme_rebuild();

  $themes = list_themes();
  if (isset($themes[$theme_key]->info['breakpoints'])) {
    $weight = 0;
    $theme_settings = $themes[$theme_key]->info['breakpoints'];
    $multipliers = isset($themes[$theme_key]->info['multipliers']) ? $themes[$theme_key]->info['multipliers'] : array();
    $settings = breakpoints_settings();
    $current_multipliers = drupal_map_assoc($settings->multipliers);
    // Build a group for each theme
    $breakpoint_group = breakpoints_breakpoint_group_empty_object();
    $breakpoint_group->machine_name = $theme_key;
    $breakpoint_group->name = $themes[$theme_key]->info['name'];
    $breakpoint_group->type = BREAKPOINTS_SOURCE_TYPE_THEME;
    foreach ($theme_settings as $name => $media_query) {
      $breakpoint = breakpoints_breakpoint_load($name, $theme_key, 'theme');
      if (!$breakpoint) {
        $breakpoint = breakpoints_breakpoint_empty_object();
        $breakpoint->name = $name;
        $breakpoint->source = $theme_key;
        $breakpoint->source_type = 'theme';
        $breakpoint->theme = '';
        $breakpoint->status = TRUE;
        $breakpoint->weight = $weight++;
        $breakpoint->machine_name = breakpoints_breakpoint_config_name($breakpoint);
      }
      $breakpoint->breakpoint = $media_query;
      $breakpoint->multipliers = isset($multipliers[$name]) ? drupal_map_assoc($multipliers[$name]) : array();
      $current_multipliers += drupal_map_assoc($breakpoint->multipliers);
      breakpoints_breakpoint_save($breakpoint);
      $breakpoint_group->breakpoints[] = $breakpoint->machine_name;
    }
    breakpoints_settings_save($current_multipliers);
    return $breakpoint_group;
  }
}

/**
 * Revert the breakpoints of a group.
 */
function breakpoints_breakpoints_group_revert(&$group) {
  breakpoints_breakpoints_group_reload($group);
  $group->overridden = 0;
  breakpoints_breakpoint_group_save($group);
}

/**
 * Duplicate a group.
 */
function breakpoints_breakpoints_group_duplicate($group, $new_name, $new_machine_name) {
  $new_group = breakpoints_breakpoint_group_empty_object();
  $new_group->machine_name = $new_machine_name;
  $new_group->name = $new_name;
  $new_group->type = BREAKPOINTS_SOURCE_TYPE_CUSTOM;
  $new_group->breakpoints = $group->breakpoints;
  breakpoints_breakpoint_group_save($new_group);
  return $new_group;
}

/**
 * Override the breakpoints of a group.
 */
function breakpoints_breakpoints_group_override($group) {
  foreach ($group->breakpoints as $key => $breakpoint) {
    $breakpoint = breakpoints_breakpoint_load_by_fullkey($breakpoint);
    $old_breakpoint = clone $breakpoint;
    if ($breakpoint->source_type == BREAKPOINTS_SOURCE_TYPE_THEME && $breakpoint->source == $group->machine_name) {
      unset($breakpoint->id);
      $breakpoint->machine_name = 'custom.' . $breakpoint->source . '.' . str_replace('-', '_', drupal_clean_css_identifier($breakpoint->name));
      $breakpoint->source_type = BREAKPOINTS_SOURCE_TYPE_CUSTOM;

      // make sure it doesn't already exists.
      if (breakpoints_breakpoint_load_by_fullkey($breakpoint->machine_name) === FALSE) {
        breakpoints_breakpoint_save($breakpoint);
      }

      // Add to the group and delete old breakpoint.
      $group->breakpoints[$key] = $breakpoint->machine_name;
      breakpoints_breakpoint_delete($old_breakpoint, $group->machine_name);
    }
  }
  $group->overridden = 1;
  breakpoints_breakpoint_group_save($group);
}

/**
 * Export breakpoints ready for theme.info inclusion.
 */
function breakpoints_breakpoints_group_exporttotheme(&$group) {
  $export = array();
  foreach ($group->breakpoints as $breakpoint_name) {
    $breakpoint = breakpoints_breakpoint_load_by_fullkey($breakpoint_name);
    if ($breakpoint && $breakpoint->status) {
      $export[$breakpoint->name] = $breakpoint->breakpoint;
    }
  }
  return $export;
}

/**
 * array_filter callback.
 */
function _breakpoints_filter_styles($var) {
  static $exists = NULL;
  if (is_null($exists)) {
    $exists = module_exists('resp_img') && defined('RESP_IMG_STYLE_PREFIX');
  }
  if (!$exists) {
    return TRUE;
  }
  return strpos(is_array($var) ? $var['name'] : $var, RESP_IMG_STYLE_PREFIX) !== 0;
}

/**
 * Implements hook_permission().
 */
function breakpoints_form_system_theme_settings_alter(&$form, &$form_state) {
  if (isset($form_state['build_info']['args'][0])) {
    $form['actions']['rescan_breakpoints'] = array(
      '#type' => 'submit',
      '#value' => t('Scan this theme for breakpoints'),
      '#submit' => array('breakpoints_form_system_theme_settings_alter_submit'),
    );
  }
}

function breakpoints_form_system_theme_settings_alter_submit(&$form, &$form_state) {
  $theme = $form_state['build_info']['args'][0];
  $group = breakpoints_breakpoint_group_load($theme);
  if ($group) {
    breakpoints_breakpoints_group_reload($group);
  }
  else {
    breakpoints_themes_enabled(array($theme));
  }
}

/**
 * Implements hook_features_export().
 */
function breakpoint_group_features_export($data, &$export, $module_name = '') {
  features_include();
  $pipe = ctools_component_features_export('breakpoint_group', $data, $export, $module_name);
  foreach ($data as $group_name) {
    $group = breakpoints_breakpoint_group_load($group_name);
    foreach ($group->breakpoints as $breakpoint_name) {
      $breakpoint = breakpoints_breakpoint_load_by_fullkey($breakpoint_name);
      if ($breakpoint->source_type != BREAKPOINTS_SOURCE_TYPE_THEME) {
        $pipe['breakpoints'][] = $breakpoint_name;
      }
    }
  }
  return $pipe;
}

/**
 * Implements hook_flush_caches().
 */
function breakpoints_flush_caches() {
  $themes = list_themes();
  foreach ($themes as $theme_key => $theme) {
    if ($theme->status) {
      $group = breakpoints_breakpoint_group_load($theme_key);
      if ($group) {
        breakpoints_breakpoints_group_reload($group, FALSE);
      }
      else {
        breakpoints_themes_enabled(array($theme_key));
      }
    }
  }
}
