<?php
/**
 * @file
 * Code for the Select number widget module.
 */

/**
 * Implements hook_field_widget_info().
 */
function select_number_widget_field_widget_info() {
  $widgets['select_number_widget'] = array(
    'label' => t('Select'),
    'description' => t('Select widget.'),
    'field types' => array('number_integer', 'number_decimal', 'commerce_price'),
    'settings' => array(
      'select_widget' => array(
        'enabled' => FALSE,
        'start' => NULL,
        'end' => NULL,
        'step' => NULL,
      ),
    ),
  );

  return $widgets;
}

/**
 * Implements hook_field_widget_settings_form().
 */
function select_number_widget_field_widget_settings_form($field, $instance) {
  $widget = $instance['widget'];
  $settings = $widget['settings'];

  $form['select_widget'] = array(
    '#type' => 'fieldset',
    '#title' => t('Select widget settings'),
    '#weight' => 10,
  );
  $form['select_widget'] += _select_number_widget_field_widget_settings_form($settings['select_widget'], TRUE);

  return $form;
}

/**
 * Implements hook_field_widget_form().
 */
function select_number_widget_field_widget_form(&$form, &$form_state, $field, $instance, $langcode, $items, $delta, $element) {
  if (!empty($items) && !empty($items[$delta])) {
    $default_value = $items[$delta]['value'];
  }
  else {
    $default_value = $instance['default_value'];
  }

  return _select_number_widget_field_widget_form($field, $instance, $element, $default_value);
}


/**
 * FAPI validation of an individual element.
 */
function select_number_widget_validate(&$element, &$form_state) {
  // Required validation
  if ($element['#required'] && $element['#value'] == '_none') {
    form_error($element, t('!name field is required.', array('!name' => $element['#title'])));
  }

  // Needed for number field validation.
  if ($element['#value'] == '_none') {
    $element['#value'] = NULL;
  }

  // Transpose selections from field => delta to delta => field, turning
  // multiple selected options into multiple parent elements.
  // Took from options widgets.
  $items = _options_form_to_storage($element);
  form_set_value($element, $items, $form_state);
}

/**
 * Implementation for hook_field_widget_info_alter.
 * Add new settings to the numeric widget types.
 */
function select_number_widget_field_widget_info_alter(&$info) {
  $widgets = array('commerce_price_simple', 'commerce_price_full', 'number');
  foreach ($info as $widget_name => $widget) {
    if (in_array($widget_name, $widgets)) {
      $info[$widget_name]['settings'] += array(
        'select_widget' => array(
          'enabled' => FALSE,
          'start' => NULL,
          'end' => NULL,
          'step' => NULL,
        ),
      );
    }
  }
}

/**
 * Implementation for hook_form_FORM_ID_alter.
 * Add the new settings form to instance widget form.
 */
function select_number_widget_form_field_ui_widget_type_form_alter(&$form, &$form_state, $form_id) {
  $field = field_info_field($form['#field_name']);
  // Altering only for locked fields that deal with numbers.
  $field_types = array('number_integer', 'commerce_price');
  if ($field['locked'] && in_array($field['type'], $field_types)) {
    $instance = field_info_instance($form['#entity_type'], $form['#field_name'], $form['#bundle']);
    $widget_settings = $instance['widget']['settings'];
    $form['basic']['select_widget'] = array(
      '#type' => 'checkbox',
      '#title' => t('Enable select'),
      '#default_value' => $widget_settings['select_widget']['enabled'],
      '#description' => t("Determines whether to use a select input form using the specified range and the step increment."),
    );
    $form['basic']['select_settings'] = array(
      '#type' => 'container',
      '#states' => array(
        // Hide the settings when the select checkbox is disabled.
        'invisible' => array(
         ':input[name="select_widget"]' => array('checked' => FALSE),
        ),
      ),
    );
    $form['basic']['select_settings'] += _select_number_widget_field_widget_settings_form($widget_settings['select_widget']);
    $form['#validate'][] = 'select_number_widget_form_field_ui_widget_type_form_validate';
    $form['#submit'][] = 'select_number_widget_form_field_ui_widget_type_form_submit';
  }
}

/**
 * Validation function for the altered field widget form.
 */
function select_number_widget_form_field_ui_widget_type_form_validate($form, &$form_state) {
  $form_values = $form_state['values'];

  $bundle = $form['#bundle'];
  $entity_type = $form['#entity_type'];
  $field_name = $form['#field_name'];
  // Retrieve the stored instance settings to merge with the incoming values.
  $instance = field_read_instance($entity_type, $field_name, $bundle);

  // Step validation, less than start - end difference
  if ($form_values['step'] > (max($form_values['start'], $form_values['end']) - min($form_values['start'], $form_values['end']))) {
    form_error($form['basic']['select_settings']['step'], t('Step exceeds the specified range, start to end.'));
  }

  // Requires start and end values if the select widget is enbled (checked).
  if (!empty($form_values['select_widget'])) {
    if (empty($form_values['start'])) {
      form_error($form['basic']['select_settings']['start'], t('If select widget is enabled the start value for the range is required.'));
    }
    if (empty($form_values['end'])) {
      form_error($form['basic']['select_settings']['end'], t('If select widget is enabled the end value for the range is required.'));
    }
  }

  // Validation against numbers minumum and maximum values of the instance
  if (!empty($instance['settings']['min'])) {
    if ($form_values['start'] < $instance['settings']['min']) {
      form_error($form['basic']['select_settings']['start'], t('The start value should be greater than field instance minimum.'));
    }
    if ($form_values['end'] < $instance['settings']['min']) {
      form_error($form['basic']['select_settings']['start'], t('The end value should be greater than field instance minimum.'));
    }
  }
  if (!empty($instance['settings']['max'])) {
    if ($form_values['start'] > $instance['settings']['max']) {
      form_error($form['basic']['select_settings']['start'], t('The start value should be less than field instance maximum.'));
    }
    if ($form_values['end'] > $instance['settings']['max']) {
      form_error($form['basic']['select_settings']['start'], t('The end value should be less than field instance maximum.'));
    }
  }
}

/**
 * Submit function for the altered field widget form.
 * Add the settings to the instance widget settings.
 */
function select_number_widget_form_field_ui_widget_type_form_submit($form, &$form_state) {
  $form_values = $form_state['values'];
  $bundle = $form['#bundle'];
  $entity_type = $form['#entity_type'];
  $field_name = $form['#field_name'];

  // Retrieve the stored instance settings to merge with the incoming values.
  $instance = field_read_instance($entity_type, $field_name, $bundle);

  $instance['widget']['settings']['select_widget']['enabled'] = $form_values['select_widget'];
  $instance['widget']['settings']['select_widget']['start'] = $form_values['start'];
  $instance['widget']['settings']['select_widget']['end'] = $form_values['end'];
  $instance['widget']['settings']['select_widget']['step'] = $form_values['step'];
  try {
    field_update_instance($instance);
  }
  catch (Exception $e) {
    drupal_set_message(t('There was a problem changing the widget for field %label.', array('%label' => $instance['label'])), 'error');
  }
}

/**
 * Implementation for hook_field_widget_form_alter.
 * Change the textfield input with select input,
 * having the specified range with specified step as options.
 */
function select_number_widget_field_widget_form_alter(&$element, &$form_state, $context) {
  $field = $context['field'];
  $instance = $context['instance'];
  $widget_settings = $instance['widget']['settings'];
  if (isset($widget_settings['select_widget']) && !empty($widget_settings['select_widget']['enabled'])) {
    if (empty($widget_settings['select_widget']['step'])) {
      $widget_settings['select_widget']['step'] = 1;
    }
    foreach(range($widget_settings['select_widget']['start'], $widget_settings['select_widget']['end'], $widget_settings['select_widget']['step']) as $number) {
      $options[$number] = $number;
    }
    $select_form = array(
      '#type' => 'select',
      '#options' => $options,
    );
    if ($field['type'] == 'commerce_price') {
      $select_form['#default_value'] = intval($element['amount']['#default_value']);
      $element['amount'] = array_merge($element['amount'], $select_form);
      unset($element['amount']['#size']);
    }
    elseif ($field['type'] == 'number_integer') {
      $select_form['#default_value'] = $element['value']['#default_value'];
      $element['value'] = $select_form;
      unset($element['value']['#size']);
    }
  }
}

/**
 * Implements hook_field_widget_settings_form().
 */
function _select_number_widget_field_widget_settings_form($select_number_widget_settings, $required = FALSE) {
  $form = array();
  $form['start'] = array(
    '#type' => 'textfield',
    '#title' => t('Start'),
    '#default_value' => isset($select_number_widget_settings['start']) ? $select_number_widget_settings['start'] : NULL,
    '#description' => t('First value of the sequence.'),
    '#element_validate' => array('element_validate_number'),
    '#required' => $required,
  );
  $form['end'] = array(
    '#type' => 'textfield',
    '#title' => t('Maximum'),
    '#default_value' => isset($select_number_widget_settings['end']) ? $select_number_widget_settings['end'] : NULL,
    '#description' => t('The sequence is ended upon reaching the end value.'),
    '#element_validate' => array('element_validate_number'),
    '#required' => $required,
  );
  $form['step'] = array(
    '#type' => 'textfield',
    '#title' => t('Step'),
    '#default_value' => isset($select_number_widget_settings['step']) ? $select_number_widget_settings['step'] : NULL,
    '#description' => t('If a step value is given, it will be used as the increment between elements in the sequence. step should be given as a positive number. If not specified, step will default to 1.'),
  );
  $form['#validate'][] = 'select_number_widget_form_field_ui_widget_type_form_validate';
  $form['#submit'][] = 'select_number_widget_form_field_ui_widget_type_form_submit';

  return $form;
}

/**
 * Helper function for building the widget form.
 */
function _select_number_widget_field_widget_form($field, $instance, $element, $default_value = NULL) {
  $select_number_widget_settings = $instance['widget']['settings']['select_widget'];
  if (empty($select_number_widget_settings['step'])) {
    $select_number_widget_settings['step'] = 1;
  }
  foreach(range($select_number_widget_settings['start'], $select_number_widget_settings['end'], $select_number_widget_settings['step']) as $number) {
    $options[$number] = $number;
  }
  $options = array('_none' => t('- Select a value -')) + $options;
  if (!($number_type = str_replace('number_', '', $field['type']))) {
    $number_type = 'float';
  }
  $element += array(
    '#type' => 'select',
    '#options' => $options,
    '#number_type' => str_replace('number_', '', $field['type']),
    '#properties' => array(
      'empty_option' => 'option_select',
    ),
    '#value_key' => key($field['columns']),
  );
  if (!empty($default_value)) {
    $element['#default_value'] = $default_value;
  }
  // Add prefix and suffix.
  if (!empty($instance['settings']['prefix'])) {
    $prefixes = explode('|', $instance['settings']['prefix']);
    $element['#field_prefix'] = field_filter_xss(array_pop($prefixes));
  }
  if (!empty($instance['settings']['suffix'])) {
    $suffixes = explode('|', $instance['settings']['suffix']);
    $element['#field_suffix'] = field_filter_xss(array_pop($suffixes));
  }

  $element['#element_validate'] = array('select_number_widget_validate', 'number_field_widget_validate');

  if ($field['type'] == 'commerce_price') {
    return array('amount' => $element);
  }
  elseif (in_array($field['type'], array('number_integer', 'number_decimal'))) {
    return array('value' => $element);
  }
}
