<?php

/**
 * @file
 * Handles Skinr UI functionality allowing users to apply skins to their site.
 */

/**
 * Implements hook_hook_info().
 */
function skinr_ui_hook_info() {
  $hooks = array(
    'skinr_ui_element_options',
    'skinr_ui_element_title',
  );
  $hooks = array_fill_keys($hooks, array(
    'group' => 'skinr',
  ));
  return $hooks;
}

/**
 * Implements hook_permission().
 */
function skinr_ui_permission() {
  return array(
    'administer skinr' => array(
      'title' => t('Administer Skinr'),
    ),
    'edit skin settings' => array(
      'title' => t('Edit skin settings.'),
    ),
    'edit advanced skin settings' => array(
      'title' => t('Edit advanced skin settings'),
      'description' => t('Edit advanced skin settings, such as custom CSS classes.'),
    ),
  );
}

/**
 * Determine whether the user has a given privilege.
 *
 * @param $string
 *   The permission, such as "administer nodes", being checked for.
 * @param $account
 *   (optional) The account to check, if not given use currently logged in user.
 *
 * @return
 *   Boolean TRUE if the current user has the requested permission.
 *
 * @see user_access()
 */
function skinr_ui_access($string, $account = NULL) {
  return user_access($string, $account) || user_access('administer skinr', $account);
}

/**
 * Implements hook_menu().
 */
function skinr_ui_menu() {
  $items['admin/structure/skinr'] = array(
    'title' => 'Skinr',
    'description' => 'Manage your skin configurations and import/export skin configurations.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('skinr_ui_list'),
    'access arguments' => array('administer skinr'),
    'file' => 'skinr_ui.admin.inc',
  );
  $items['admin/structure/skinr/list'] = array(
    'title' => 'List',
    'description' => t('Manage skinr configurations.'),
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );

  // Themes.
  $default_theme = variable_get('theme_default', 'bartik');
  $items['admin/structure/skinr/library'] = array(
    'title' => 'Library',
    'description' => 'Manage what skins are available when configuring the way your site looks.',
    'page callback' => 'skinr_ui_admin_library',
    'page arguments' => array($default_theme),
    'access arguments' => array('administer skinr'),
    'file' => 'skinr_ui.admin.inc',
    'type' => MENU_LOCAL_TASK,
  );
  foreach (list_themes() as $key => $theme) {
    $items['admin/structure/skinr/library/list/' . $key] = array(
      'title' => check_plain($theme->info['name']),
      'page arguments' => array($key),
      'type' => $key == $default_theme ? MENU_DEFAULT_LOCAL_TASK : MENU_LOCAL_TASK,
      'weight' => $key == $default_theme ? -10 : 0,
      'access callback' => '_skinr_ui_themes_access',
      'access arguments' => array($theme),
      'file' => 'skinr_ui.admin.inc',
    );
  }

  // Import & Export.
  $items['admin/structure/skinr/import'] = array(
    'title' => 'Import',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('skinr_ui_import_form'),
    'type' => MENU_LOCAL_TASK,
    'access arguments' => array('administer skinr'),
    'weight' => 2,
    'description' => t('Import skin configurations.'),
    'file' => 'skinr_ui.admin.inc',
  );
  $items['admin/structure/skinr/export'] = array(
    'title' => 'Export',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('skinr_ui_export_form'),
    'type' => MENU_LOCAL_TASK,
    'access arguments' => array('administer skinr'),
    'weight' => 3,
    'description' => t('Export skin configurations.'),
    'file' => 'skinr_ui.admin.inc',
  );

  // Add skin settings for an element.
  $items['admin/structure/skinr/add'] = array(
    'title' => 'Add skin',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('skinr_ui_add'),
    'type' => MENU_LOCAL_ACTION,
    'access arguments' => array('edit skin settings'),
    'file' => 'skinr_ui.admin.inc',
  );

  // Configure skin settings for an element.
  $items['admin/structure/skinr/edit/%/%'] = array(
    'title' => 'Edit skin',
    'title callback' => 'skinr_ui_edit_title',
    'title arguments' => array(4, 5),
    'page callback' => 'skinr_ui_edit',
    'page arguments' => array(4, 5), // module, element
    'type' => MENU_CALLBACK,
    'context' => MENU_CONTEXT_PAGE | MENU_CONTEXT_INLINE,
    'access arguments' => array('edit skin settings'),
  );
  $items['admin/structure/skinr/edit/%/%/configure'] = array(
    'title' => 'Edit skin',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'context' => MENU_CONTEXT_INLINE,
  );

  // Enable a skin configuration.
  $items['admin/structure/skinr/skin/%skinr_skin/enable'] = array(
    'title' => 'Enable skin',
    'page callback' => 'skinr_ui_skin_status_set',
    'page arguments' => array(4, TRUE),
    'type' => MENU_CALLBACK,
    'access arguments' => array('administer skinr'),
    'file' => 'skinr_ui.admin.inc',
  );

  // Disable a skin configuration.
  $items['admin/structure/skinr/skin/%skinr_skin/disable'] = array(
    'title' => 'Disable skin',
    'page callback' => 'skinr_ui_skin_status_set',
    'page arguments' => array(4, FALSE),
    'type' => MENU_CALLBACK,
    'access arguments' => array('administer skinr'),
    'file' => 'skinr_ui.admin.inc',
  );

  // Delete a skin configuration.
  $items['admin/structure/skinr/skin/%skinr_skin/delete'] = array(
    'title' => 'Delete skin',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('skinr_ui_delete_confirm', 4),
    'type' => MENU_CALLBACK,
    'access arguments' => array('administer skinr'),
    'file' => 'skinr_ui.admin.inc',
  );

  return $items;
}

/**
 * Menu item access callback - only admin or enabled themes can be accessed.
 */
function _skinr_ui_themes_access($theme) {
  return user_access('administer skinr') && drupal_theme_access($theme);
}


/**
 * Implements hook_theme().
 */
function skinr_ui_theme() {
  return array(
    'skinr_ui_admin_library_fieldset' => array(
      'render element' => 'form',
      'file' => 'skinr_ui.admin.inc',
    ),
    'skinr_ui_admin_library_summary' => array(
      'variables' => array('name' => NULL, 'description' => NULL),
      'file' => 'skinr_ui.admin.inc',
    ),
  );
}

/**
 * Implements hook_help().
 */
function skinr_ui_help($path, $arg) {
  if (module_exists('advanced_help')) {
    $advanced_help = '<p>' . t('Visit the <a href="@skinr-help">help page</a> for full documentation.', array('@skinr-help' => url('admin/advanced_help/skinr'))). '</p>';
  }
  else {
    $advanced_help = '<p>' . t('Please download and enable the <a href="http://drupal.org/project/advanced_help">Advanced Help</a> module for full Skinr documentation.') . '</p>';
  }
  switch ($path) {
    case 'admin/structure/skinr':
      return  '<p>' . t('Below is a list of all skin configurations in use on this site.') . '</p>' . $advanced_help;
    case 'admin/structure/skinr/import':
      return  '<p>' . t('To import skin configurations, paste exported code and click the "Import" button.') . '</p>';
    case 'admin/structure/skinr/export':
      return  '<p>' . t('To export skin configurations, ensure the correct theme is selected and click the "Export" button.') . '</p>';
    case 'admin/structure/skinr/edit/%/%':
      // @todo Make this help text more relevant.
      $theme_hooks = skinr_theme_hooks($arg[4], $arg[5]);
      return  '<p>' . t('Manage which skins you want to apply to the hooks <strong>!hooks</strong>.', array('!hooks' => implode(', ', $theme_hooks))) . '</p>';
  }
}

/**
 * Menu title callback; sets the title for a skins configuration form page.
 *
 * @param $module
 *   The module that we're editing settings of.
 * @param $element
 *   The element we're editing settings of.
 */
function skinr_ui_edit_title($module, $element) {
  return t('Skin settings for !module type !element', array('!module' => $module, '!element' => $element));
}

/**
 * Menu callback; prepares some variables and displays a Skinr edit form.
 *
 * @param $module
 *   The module that we're editing settings of.
 * @param $element
 *   The element of the object we're editing settings of.
 */
function skinr_ui_edit($module, $element) {
  // Set defaults.
  $defaults = array();
  $themes = list_themes();
  foreach ($themes as $theme) {
    if (!$theme->status) {
      continue;
    }

    $params = array(
      'theme' => $theme->name,
      'module' => $module,
      'element' => $element,
    );
    // Don't nest the call to skinr_skin_get_sids() in skinr_skin_load_multiple().
    // If the prior functions returns no results, the second function will load
    // ALL skins.
    if ($sids = skinr_skin_get_sids($params)) {
      $skins = skinr_skin_load_multiple($sids);
      foreach ($skins as $skin) {
        $defaults[$theme->name][$skin->skin] = $skin->options;
      }
    }
  }
  return drupal_get_form('skinr_ui_form', array('module' => $module, 'element' => $element), $defaults);
}

/**
 * Form builder for the skins configuration form.
 *
 * @param $module
 *   The module that we're editing settings of.
 * @param $element
 *   The element of the object we're editing settings of.
 * @param $defaults
 *   An array of default values keyed by theme name.
 *
 * @ingroup forms
 */
function skinr_ui_form($form, &$form_state, $arguments, $defaults) {
  // Set arguments.
  $module = $arguments['module'];
  $element = $arguments['element'];

  // Set default values.
  $form_state['values']['skinr_settings'] = $defaults;

  // Build form.
  $form = array(
    '#attributes' => array('class' => 'skinr-form'),
  );

  $form['skinr']['module'] = array(
    '#type' => 'hidden',
    '#value' => $module,
  );
  $form['skinr']['element'] = array(
    '#type' => 'hidden',
    '#value' => $element,
  );

  $form['actions'] = array('#type' => 'actions');
  $form['actions']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Save'),
    '#weight' => 50,
  );

  return $form;
}

/**
 * Implements hook_form_alter().
 */
function skinr_ui_form_alter(&$form, $form_state, $form_id) {
  // Fix for update script.
  if ($form_id == 'update_script_selection_form') {
    return;
  }

  // Ensure module and element values are set.
  if (empty($form['skinr']['module']['#value']) || empty($form['skinr']['element']['#value'])) {
    return;
  }

  // Check for access.
  if (!skinr_ui_access('edit skin settings')) {
    // Deny access.
    return;
  }

  $module = $form['skinr']['module']['#value'];
  $element = $form['skinr']['element']['#value'];

  $groups = skinr_get_group_info();
  $skin_infos = skinr_get_skin_info();

  // Apply overridden status to skins.
  foreach ($skin_infos as $skin_name => $skin_info) {
    $skin_infos[$skin_name]['status'] = skinr_skin_info_status_get($skin_infos[$skin_name]);
  }

  // Invoke hook_skinr_theme_hooks() and hook_skinr_theme_hooks_alter().
  $theme_hooks = skinr_theme_hooks($module, $element);

  $form['skinr_settings'] = array(
    '#tree' => TRUE,
    '#type' => 'container',
    // Move settings to top.
    '#weight' => 0,
  );


  $themes = list_themes();
  ksort($themes);
  // Get current theme, but make sure it's not the admin theme when we're editing with AJAX.
  $current_theme = skinr_current_theme(TRUE);

  foreach ($themes as $theme) {
    if (!$theme->status) {
      continue;
    }

    // If this hook is a region, and the region does not exist for this
    // theme, don't bother outputting any of the settings.
    if (strpos($theme_hooks[0], 'region') === 0) {
      // Strip the region__ part off the region name.
      $region = substr($theme_hooks[0], 8);

      $regions = system_region_list($theme->name, REGIONS_VISIBLE);
      if (!isset($regions[$region])) {
        continue;
      }
    }

    // Load defaults for theme sub-form.
    $defaults = isset($form_state['values']['skinr_settings']) ? $form_state['values']['skinr_settings'] : array();

    // Build theme sub-form.
    $form['skinr_settings'][$theme->name] = array(
      '#type' => 'fieldset',
      '#title' => $theme->info['name'] . ($theme->name == $current_theme ? ' (' . t('enabled + default') . ')' : ''),
      '#collapsible' => TRUE,
      '#collapsed' => $theme->name == $current_theme ? FALSE : TRUE,
    );
    if ($theme->name == $current_theme) {
      // Current theme goes at the top.
      $form['skinr_settings'][$theme->name]['#attributes'] = array('class' => array('skinr-ui-current-theme'));
      $form['skinr_settings'][$theme->name]['#weight'] = -10;

      // Use vertical tabs.
      $form['skinr_settings'][$theme->name]['groups'] = array(
        '#type' => 'vertical_tabs',
      );
    }

    // Create individual widgets for each skin.
    foreach ($skin_infos as $skin_name => $skin_info) {
      // Check if this skin is disabled.
      if (empty($skin_info['status'][$theme->name])) {
        continue;
      }

      // Check if this skin applies to this hook.
      if (!is_array($skin_info['theme hooks']) || (!in_array('*', $skin_info['theme hooks']) && !_skinr_is_featured($theme_hooks, $skin_info['theme hooks']))) {
        continue;
      }

      // Create widget.
      $field = array();
      if (!empty($skin_info['form callback'])) {
        // Process custom form callbacks.

        // Load include file.
        if (!empty($skin_info['source']['filename'])) {
          skinr_load_include($skin_info['source']['path'] . '/' . $skin_info['source']['filename']);
        }

        // Execute form callback.
        if (function_exists($skin_info['form callback'])) {
          $context = array(
            'theme' => $theme->name,
            'skin_name' => $skin_name,
            'skin_info' => $skin_info,
          );
          $field = $skin_info['form callback']($form, $form_state, $context);
        }
      }
      else {
        switch ($skin_info['type']) {
          case 'checkboxes':
            $field = array(
              '#type' => 'checkboxes',
              '#multiple' => TRUE,
              '#title' => t($skin_info['title']),
              '#options' => skinr_ui_info_options_to_form_options($skin_info['options']),
              '#default_value' => isset($defaults[$theme->name][$skin_name]) ? $defaults[$theme->name][$skin_name] : array(),
              '#description' => t($skin_info['description']),
              '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
            );
            break;
          case 'radios':
            $field = array(
              '#type' => 'radios',
              '#title' => t($skin_info['title']),
              '#options' => array_merge(array('' => '&lt;none&gt;'), skinr_ui_info_options_to_form_options($skin_info['options'])),
              '#default_value' => isset($defaults[$theme->name][$skin_name]) ? $defaults[$theme->name][$skin_name] : '',
              '#description' => t($skin_info['description']),
              '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
            );
            break;
          case 'select':
            $field = array(
              '#type' => 'select',
              '#title' => t($skin_info['title']),
              '#options' => array_merge(array('' => '<none>'), skinr_ui_info_options_to_form_options($skin_info['options'])),
              '#default_value' => isset($defaults[$theme->name][$skin_name]) ? $defaults[$theme->name][$skin_name] : '',
              '#description' => t($skin_info['description']),
              '#weight' => isset($skin_info['weight']) ? $skin_info['weight'] : NULL,
            );
            break;
          default:
            // Raise an error.
            drupal_set_message(t("Widget %name's type is invalid.", array('%name' => $skin_name)), 'error', FALSE);
            break;
        }
      }
      if (empty($skin_info['group']) || empty($groups[$skin_info['group']])) {
        $form['skinr_settings'][$theme->name][$skin_name] = $field;
      }
      else {
        if (!empty($field) && !isset($form['skinr_settings'][$theme->name]['groups'][$skin_info['group']])) {
          $group = $groups[$skin_info['group']];
          $form['skinr_settings'][$theme->name]['groups'][$skin_info['group']] = array(
            '#type' => 'fieldset',
            '#title' => t($group['title']),
            '#description' => t($group['description']),
            '#weight' => isset($group['weight']) ? $group['weight'] : NULL,
          );
        }
        $form['skinr_settings'][$theme->name]['groups'][$skin_info['group']][$skin_name] = $field;
      }
    }

    // Check for access.
    if (skinr_ui_access('edit advanced skin settings')) {
      $skin_name = '_additional';
      $form['skinr_settings'][$theme->name]['groups']['_additional'] = array(
        '#type' => 'fieldset',
        '#title' => t('Advanced'),
        '#weight' => 50,
      );
      $form['skinr_settings'][$theme->name]['groups']['_additional']['_additional'] = array(
        '#type' => 'textfield',
        '#title' => t('CSS classes'),
        '#size' => 40,
        '#description' => t('To add CSS classes manually, enter classes separated by a single space i.e. <code>first-class second-class</code>'),
        '#default_value' => isset($defaults[$theme->name][$skin_name]) ? $defaults[$theme->name][$skin_name] : '',
      );
    }
  }

  // Only add validation handler once.
  if (!isset($form['#validate']) || !in_array('skinr_ui_form_validate', $form['#validate'])) {
    $form['#validate'][] = 'skinr_ui_form_validate';
  }

  // Only add submit handler once.
  if (!isset($form['#submit']) || !in_array('skinr_ui_form_submit', $form['#submit'])) {
    $form['#submit'][] = 'skinr_ui_form_submit';
  }
}

/**
 * Form validation handler for skinr_ui_form_alter().
 */
function skinr_ui_form_validate($form, &$form_state) {
  $module = $form_state['values']['module'];
  $element = $form_state['values']['element'];

  $error = FALSE;
  if (isset($form_state['values']['skinr_settings'])) {
    foreach ($form_state['values']['skinr_settings'] as $theme_name => $theme) {
      if (isset($theme['groups']['_additional']['_additional'])) {
        // Validate additional classes field.
        if (preg_match('/[^a-zA-Z0-9\-\_\s]/', $theme['groups']['_additional']['_additional'])) {
          form_set_error('skinr_settings][' . $theme_name . '][groups][_additional][_additional', t('Additional classes for Skinr may only contain alphanumeric characters, spaces, - and _.'));
          $error = TRUE;
        }
      }
    }
  }

  if (!$error) {
    $groups = skinr_get_group_info();
    // Add hard-coded additional classes group.
    $groups['_additional'] = array(
      'title' => 'Additional',
      'description' => 'Additional custom classes.',
      'weight' => 0,
    );

    if (!empty($form_state['values']['skinr_settings'])) {
      foreach ($form_state['values']['skinr_settings'] as $theme_name => $theme) {
        // Unset active tab variables.
        foreach ($theme['groups'] as $skin_name => $options) {
          if (strpos($skin_name, '__groups__active_tab') !== FALSE) {
            unset($form_state['values']['skinr_settings'][$theme_name]['groups'][$skin_name]);
            continue;
          }
        }
        // Undo any grouping to ease processing on submit.
        foreach ($groups as $group_name => $group) {
          if (!empty($theme['groups'][$group_name]) && is_array($theme['groups'][$group_name])) {
            $group_values = $theme['groups'][$group_name];
            unset($form_state['values']['skinr_settings'][$theme_name]['groups'][$group_name]);
            $form_state['values']['skinr_settings'][$theme_name] = array_merge($form_state['values']['skinr_settings'][$theme_name], $group_values);
          }
        }
        unset($form_state['values']['skinr_settings'][$theme_name]['groups']);
      }
    }
  }
}

/**
 * Form submission handler for skinr_ui_form_alter().
 */
function skinr_ui_form_submit($form, &$form_state) {
  $current_theme = skinr_current_theme(TRUE);

  $module = $form_state['values']['module'];
  $element = $form_state['values']['element'];

  if (!empty($form_state['values']['skinr_settings'])) {
    foreach ($form_state['values']['skinr_settings'] as $theme_name => $theme) {
      // Process widgets.
      if (!empty($theme) && is_array($theme)) {
        foreach ($theme as $skin_name => $options) {
          if ($skin_name == '_additional' && !user_access('edit advanced skin settings')) {
            // This user doesn't have access to alter these options.
            continue;
          }

          // Ensure options is an array.
          if (!is_array($options)) {
            $options = $skin_name == '_additional' ? explode(' ', $options) : array($options);
          }
          // Sanitize options.
          $options = _skinr_array_strip_empty($options);

          // Find existing skin.
          $params = array(
            'theme' => $theme_name,
            'module' => $module,
            'element' => $element,
            'skin' => $skin_name,
          );
          $sids = skinr_skin_get_sids($params);

          unset($skin);
          if (!empty($sids)) {
            $sid = reset($sids);
            $skin = skinr_skin_load($sid);
          }

          if (empty($options)) {
            if (!empty($skin)) {
              // Delete this skin configuration.
              skinr_skin_delete($skin->sid);
            }
            continue;
          }

          if (empty($skin)) {
            // It doesn't exist, so create a new skin.
            $skin = new stdClass();
            $skin->theme = $theme_name;
            $skin->module = $module;
            $skin->element = $element;
            $skin->skin = $skin_name;
          }
          $skin->options = $options;
          $skin->status = 1;

          // Save skin.
          if (!skinr_skin_save($skin)) {
            drupal_set_message(t("Skinr settings for %skin weren't saved due to an error.", array('%skin' => $skin_name)), 'error');
          }
        }
      }
    }
  }
}

/**
 * Implements hook_preprocess().
 */
function skinr_ui_preprocess(&$variables, $hook) {
  $original_hook = $hook;
  $theme_registry = theme_get_registry();
  if (isset($theme_registry[$hook]['original hook'])) {
    $original_hook = $theme_registry[$hook]['original hook'];
  }

  $contextual_links = array();
  $counter = 0;
  $array_elements = skinr_invoke_all('skinr_elements', $variables, $original_hook, 'contextual_links');
  foreach ($array_elements as $module => $elements) {
    foreach ($elements as $element) {
      $contextual_links['skinr-' .  $module . '-' . $counter++] = array(
        'admin/structure/skinr/edit', array($module, $element),
      );
    }
  }
  if (!empty($contextual_links)) {
    skinr_ui_contextual_links($variables, $original_hook, $contextual_links);
  }
  return;
}

/**
 * Set contextual menu items for skinr.
 *
 * @param $variables
 *   The $variables parameter from a preprocess function.
 * @param $hook
 *   The $hook parameter from a preprocess function.
 * @param $contextual_links
 *   An array of contextual links data as returned from Skinr's contextual
 *   links handler.
 */
function skinr_ui_contextual_links(&$variables, $hook, $contextual_links) {
  $hooks = theme_get_registry();

  // Determine the primary theme function argument.
  if (!empty($hooks[$hook]['variables'])) {
    $keys = array_keys($hooks[$hook]['variables']);
    $key = $keys[0];
  }
  elseif (!empty($hooks[$hook]['render element'])) {
    $key = $hooks[$hook]['render element'];
  }

  if (!empty($key) && isset($variables[$key])) {
    $element = &$variables[$key];
  }

  if (isset($element) && is_array($element)) {
    foreach ($contextual_links as $key => $contextual_link) {
      $element['#contextual_links'][$key] = $contextual_link;
    }
  }
}

/**
 * Helper function to determine whether one of a set of hooks exists in a list
 * of required theme hooks.
 *
 * @param $theme_hooks
 *   An array of theme hooks available to this element.
 * @param $allowed_hooks
 *   An array of allowed theme hooks.
 *
 * @return
 *   TRUE if an overlap is found, FALSE otherwise.
 *
 * @todo Rename function to be more descriptive.
 */
function _skinr_is_featured($theme_hooks, $allowed_hooks) {
  foreach ($theme_hooks as $theme_hook) {
    if (in_array($theme_hook, $allowed_hooks)) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Helper function to retrieve a unique id for each skinr class. Used by AJAX.
 *
 * @return
 *   A unique ID number.
 *
 * @todo Evaluate the usefulness of this function. Should it go into
 *   a UI front-end specific file?
 */
function _skinr_ui_ajax_id() {
  static $skinr_id = 0;
  return ++$skinr_id;
}

/**
 * Helper function to convert an array of options, as specified in the .info
 * file, into an array usable by Form API.
 *
 * @param $options
 *   An array containing at least the 'class' and 'label' keys.
 *
 * @return
 *   A Form API compatible array of options.
 *
 * @todo Rename function to be more descriptive.
 */
function skinr_ui_info_options_to_form_options($options) {
  $form_options = array();
  foreach ($options as $option_name => $option) {
    $form_options[$option_name] = t($option['title']);
  }
  return $form_options;
}
