<?php
// $Id: profile_locality.module,v 1.1 2010/12/12 09:05:41 bacteriaman Exp $

/**
 * @file
 * The profile_locality.module file, which produces the primary functionality.
 *
 */

define('DEFAULT_COUNTRY', 'US'); // 2-letter capitalized iso abbreviation for default country

/**
 * Implementation of hook_menu().
 */
function profile_locality_menu() {
  $items = array();

  $items['admin/user/profile_locality'] = array(
    'title' => 'Profile Locality',
    'page callback' => 'profile_locality_admin',
    'access arguments' => array('administer site configuration'),
    'description' => 'Configure your Profile Locality preferences and field assignments.',
    'type' => MENU_NORMAL_ITEM,
  );
  $items['postal/autocomplete'] = array(
    'title' => 'Postal autocomplete',
    'page callback' => 'profile_locality_postal_autocomplete',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );

  return $items;
}

/**
 * Implementation of hook_form_alter().
 */
function profile_locality_form_alter(&$form, &$form_state, $form_id) {
  switch ($form_id) {
    case 'user_register':
    case 'user_profile_form':
      if (module_exists('profile')) {
        $category = variable_get('profile_locality_category', NULL);
        $country = variable_get('profile_locality_country', NULL);
        $postal = variable_get('profile_locality_postal_code', NULL);

        if (isset($form[$category][$country])) {
          if (isset($form_state['post']['profile_country'])) {
            // currently selected country value when validation fails
            $default_value = $form_state['post']['profile_country'];
          }
          elseif ($form[$category][$country]['#default_value']) {
            $default_value = $form[$category][$country]['#default_value'];
          }
          else {
            $default_value = variable_get('profile_locality_default_country', DEFAULT_COUNTRY);
          }
          // load profile country list selection field options
          $form[$category][$country]['#options'] = profile_locality_countries();
          $form[$category][$country]['#default_value'] = $default_value;
        }

        if (isset($form[$category][$postal])) {
          // switch postal textfield to autocomplete by adding #autocomplete_path attribute
          $form[$category][$postal]['#autocomplete_path'] = 'postal/autocomplete/'. $default_value;
          if ($form[$category][$postal]['#required']) {
            // disable #required attribute for postal code field to suppress default validation error
            $form[$category][$postal]['#required'] = FALSE;
            // define custom #mandatory attribute as alternative to default #required attribute
            $form[$category][$postal]['#mandatory'] = TRUE;
          }

          $form['#validate'][] = 'profile_locality_validate_handler';
        }

        // define and add javascript string variables
        drupal_add_js("var countryId = ". drupal_to_js(form_clean_id('#edit-'. $country)) .";", 'inline');
        drupal_add_js("var postalId = ". drupal_to_js(form_clean_id('#edit-'. $postal)) .";", 'inline');
        // javascript formatted array string of unique country iso codes
        // drupal_to_js formats array as quote-comma-quote comma-delimited string w/ opening/closing brackets, exp. [ "US","CA" ]
        drupal_add_js("var codes = ". variable_get('profile_locality_iso_codes', drupal_to_js(array())) .";", 'inline');

        drupal_add_js(drupal_get_path('module', 'profile_locality') .'/profile_locality.js');
      }
      break;
  }
}

/**
 * Custom validate handler to postal code value (if applicable).
 */
function profile_locality_validate_handler($form, &$form_state) {
  $category = variable_get('profile_locality_category', NULL);
  $country = variable_get('profile_locality_country', NULL);
  $postal = variable_get('profile_locality_postal_code', NULL);

  // retrieve record for selected country iso and postal code
  // query will return existing iso and postal code (if valid)
  $locality = db_fetch_object(db_query("SELECT DISTINCT pc1.iso, pc2.code FROM {profile_locality_postal_code} pc1 LEFT JOIN (SELECT code, iso FROM {profile_locality_postal_code} WHERE code = '%s') pc2 ON pc2.iso = pc1.iso WHERE pc1.iso = '%s'", $form_state['values'][$postal], $form_state['values'][$country]));
  if (empty($locality->code) && $locality->iso == $form_state['values'][$country] && $form_state['values'][$postal]) {
    // country iso is valid, but postal code entry does not exist in database
    form_set_error($postal, t('The %postal you entered (%code) is not valid for %country.', array('%postal' => $form[$category][$postal]['#title'], '%code' => $form_state['values'][$postal], '%country' => $form[$category][$country]['#options'][$form_state['values'][$country]])));
  }
  elseif (empty($locality->iso)) {
    // clear existing value (if any) because postal codes for selected country do not exist in database
    form_set_value($form[$category][$postal], NULL, $form_state);
  }

  // use custom #mandatory attribute when postal code field is required
  // insure postal codes exist for the selected iso country code
  if ($form[$category][$postal]['#mandatory'] && in_array($form_state['values'][$country], profile_locality_iso_codes())) {
    // custom validation when postal field is required
    if (empty($form_state['values'][$postal])) {
      form_set_error($postal, t('%postal field is required.', array('%postal' => $form[$category][$postal]['#title'])));
    }
  }
}

/**
 * Implementation of hook_profile_alter().
 */
function profile_locality_profile_alter(&$account) {
  $category = variable_get('profile_locality_category', NULL);
  $country = variable_get('profile_locality_country', NULL);
  if ($country) {
    $account->content[$category][$country]['#value'] = profile_locality_get_country($account->content[$category][$country]['#value']);
  }
}

/**
 * Return array of iso country codes.
 *
 * @return array
 */
function profile_locality_iso_codes() {
  $codes = array();
  $result = db_query("SELECT DISTINCT(iso) FROM {profile_locality_postal_code}");
  while ($code = db_fetch_object($result)) {
    $codes[] = $code->iso;
  }

  return $codes;
}

/**
 * Retrieve list of countries.
 *
 * @return array
 */
function profile_locality_countries() {
  $countries = array();
  $result = db_query("SELECT iso, name FROM {profile_locality_country}");
  while ($country = db_fetch_object($result)) {
    $countries[$country->iso] = $country->name;
  }

  return $countries;
}

/**
 * Retrieve full country name.
 *
 * @param string
 * @return string
 */
function profile_locality_get_country($iso) {
  $countries = profile_locality_countries();

  return $countries[$iso];
}

/**
 * Custom callback function for Profile Locality admin form.
 */
function profile_locality_admin() {
  $selections = array();
  $textfields = array();
  $result = db_query("SELECT name, type, category FROM {profile_fields} WHERE type = 'selection' OR type = 'textfield'");
  while ($field = db_fetch_object($result)) {
    if ($field->type == 'selection') {
      $selections[$field->name] = $field->name .' ('. $field->category .')';
    }
    else {
      $textfields[$field->name] = $field->name .' ('. $field->category .')';
    }
  }

  if (count($selections) && count($textfields)) {
    return drupal_get_form('profile_locality_admin_form', $selections, $textfields);
  }
  else {
    drupal_set_message(t('You must first create a Country list selection field and Postal single-line textfield on the !profile setup page.', array('!profile' => l('profile', 'admin/user/profile'))), 'error');
    return FALSE;
  }
}

/**
 * Custom function for Profile Locality admin form.
 *
 * @param array
 * @param array
 * @param array
 * @return array
 */
function profile_locality_admin_form($form_state, $selections, $textfields) {
  $form['profile_locality_default_country'] = array(
    '#type' => 'select',
    '#default_value' => variable_get('profile_locality_default_country', DEFAULT_COUNTRY),
    '#options' => profile_locality_countries(),
    '#description' => t('Choose the default Country selection.'),
  );
  $form['profile_locality_country'] = array(
    '#type' => 'select',
    '#default_value' => variable_get('profile_locality_country', NULL),
    '#options' => $selections,
    '#description' => t('Choose a profile list selection field to use for Country.'),
  );
  $form['profile_locality_postal_code'] = array(
    '#type' => 'select',
    '#default_value' => variable_get('profile_locality_postal_code', NULL),
    '#options' => $textfields,
    '#description' => t('Choose a profile single-line textfield to use for Postal Code.'),
  );
  $form['profile_locality_iso_codes'] = array(
    '#type' => 'hidden',
    '#value' => drupal_to_js(profile_locality_iso_codes())
  );

  return system_settings_form($form);
}

/**
 * Profile Locality admin form validation function.
 */
function profile_locality_admin_form_validate($form, &$form_state) {
  $result = db_query("
    SELECT pf1.name, pf2.name, pf1.category FROM {profile_fields} pf1, {profile_fields} pf2
    WHERE pf1.name = '%s' AND pf2.name = '%s' AND pf1.category = pf2.category",
    $form_state['values']['profile_locality_country'], $form_state['values']['profile_locality_postal_code']);

  if ($profile = db_fetch_object($result)) {
    // country and postal fields belong to the same profile category
    // manually save the profile category because it's not part of the form
    variable_set('profile_locality_category', $profile->category);
  }
  else {
    form_set_error('profile_locality_postal_code', t('Country and Postal Code fields must belong to the same profile category.'));
  }
}

/**
 * Callback to allow autocomplete of postal codes
 * 
 * @param string
 * @param string
 */
function profile_locality_postal_autocomplete($field, $string) {
  $matches = array();
  $result = db_query_range("SELECT code FROM {profile_locality_postal_code} WHERE LOWER(iso) = '%s' AND LOWER(code) LIKE LOWER('%s%%')", $field, $string, 0, 10);
  while ($data = db_fetch_object($result)) {
    $matches[$data->code] = check_plain($data->code);
  }

  drupal_json($matches);
}