//
// Requires these to be loaded:
//  ui-dynamics.jsp
//  formValidatorUtil.js
//
//
// See: http://svn.etraveli.net/trac/wiki/Documentation/WebSystem/JavaScriptFormValidation
//


var d_debug_local = false;     // Set to false not to see this in RuntimeEnvironment.DEV
var d_debug_level_local = 1;  // (0-3) 0 == not much, 3 alot.


// Holds the directives.
var ValidationEntry = function (uiKey, validationFunction) {
  this.uiKey = uiKey;
  this.validationFunction = validationFunction;
  this.namePrefix = arguments.length > 2 ? arguments[2] : '';
  this.matches = function(elementName) {
    if (this.namePrefix) {
      return elementName.indexOf(this.namePrefix) == 0;
    }
    return true;
  };
};
/*

 Air.Search.EarlyReturnTripDate
 Air.Search.EarlyTripToDate
 Air.Search.EnterDestination


 Air.Search.ReturnTripDateBeforeTripToDate
 Air.Search.StillSearching

 */

var VALIDATION_REGISTRY = {
  // Payment
  'town'             : new ValidationEntry('Javascript.Validation.InvalidCity', v_isTown),
  'zipCode'          : new ValidationEntry('Javascript.Validation.InvalidZipCode', v_isZipCode),
  'country'          : new ValidationEntry('Javascript.Validation.InvalidCountry', v_isCountry),
  'address'          : new ValidationEntry('Javascript.Validation.InvalidAddress', v_isAddress),
  'chkTravelCond'    : new ValidationEntry('Javascript.Validation.InvalidTravelConditions', v_isTravelCond),

  // Below required if in form.
  'personalId'       : new ValidationEntry('Javascript.Validation.InvalidPersonalId', v_required),
  'province'         : new ValidationEntry('Javascript.Validation.InvalidProvince', v_required),
  'nationality'      : new ValidationEntry('Javascript.Validation.InvalidCountry', v_isCountry),

  //  'cardNumber'       : new ValidationEntry('Javascript.CC.InvalidCardNum', v_validateCreditCard),
  //  'cvcCode'          : new ValidationEntry('Javascript.Validation.InvalidCVVCode', v_validateCVC),
  //  'cardType'         : new ValidationEntry('Javascript.Validation.InvalidCardType', v_required),


  'paymentMethod'         : new ValidationEntry('Javascript.Validation.InvalidPaymentMethod', v_validatePaymentMethod),

  // Kalle Kula / Payment
  'title'            : new ValidationEntry('Javascript.Validation.InvalidTitle', v_isTitle),
  'firstName'        : new ValidationEntry('Javascript.Util.FirstName', v_paxFirstName),
  'lastName'         : new ValidationEntry('Javascript.Util.LastName', v_paxLastName),
  'phone'            : new ValidationEntry('Javascript.Util.Phone', v_isPhoneFax),
  'cellPhone'        : new ValidationEntry('Javascript.Validation.InvalidMobile', v_isPhoneFax),
  'email'            : new ValidationEntry('Javascript.Util.Email', v_isEmail),
  'emailVerify'      : new ValidationEntry('Javascript.Util.Emai2', v_isEmailVerify),
  'birthDay'         : new ValidationEntry('Input.Validation.ChildAgesRequired', v_childBirthDay),
  'selectedTraveller': new ValidationEntry('Javascript.Validation.TravellerSelectionRequired', v_travellerSelected),

  'orderNumber'      : new ValidationEntry('OrderNumber', v_orderNumber),




  // SearchFormsN
  // Air/Combo
  'fromCity'         : new ValidationEntry('Javascript.Validation.InvalidOrigin', v_isCityWithCode),
  'toCity'           : new ValidationEntry('Javascript.Validation.InvalidDestination', v_isCityWithCode),
  'children'         : new ValidationEntry('Javascript.Validation.InvalidChildAge', v_validatePaxAndChildAges),
  'depDate'          : new ValidationEntry('Air.Search.InvalidTripToDate', v_validateAirDates),

  // Hotel/Combo
  'cityName'         : new ValidationEntry('Javascript.Validation.InvalidDestination', v_isCityWithId),
  'checkIn'          : new ValidationEntry('Hotel.Search.InvalidCheckInDate', v_validateHotelDates),

  // Combo
  'numberOfRooms'    : new ValidationEntry('Javascript.Validation.InvalidChildAge', v_validateComboNumPassengers),

  // name must start with 'air.', 'air.shit' or 'air.apa[12].shit' will match
  'shit'      : new ValidationEntry('Javascript.Util.Phone', v_isEmailVerify, 'air.'),

  // Car
  'pickupCityId'          : new ValidationEntry('Car.PickupStation.NotSelected', v_validateCarPickupCity),
  'pickupDate'            : new ValidationEntry('Car.Search.InvalidPickupDate', v_validateCarDates),

  // Combined
  'returnCityId'          : new ValidationEntry('Combined.Car.ReturnStation.NotSelected', v_validateCombinedCarReturnCity),
  'returnDate'            : new ValidationEntry('Combined.Car.Search.InvalidReturnDate', v_validateCombinedCarDate),
  'selectedDriverIndex'   : new ValidationEntry('Combined.Traveller.NoDriverSelected', v_validateDriverSelected),

  // For last comma...
  'non-existing-field' : new ValidationEntry('*', v_required)
};


/**
 * Holds data about ONE NOT-validated field / or message which means the form is NOT validated.
 *
 * If the first argument is a String, it is used instead of errorKeyOrMessage,
 * in which case errorKeyOrMessage is not read.
 *
 * @param field                The field which was validated
 * @param errorKeyOrMessage    The KEY of the UI-text, or the resolved UI text to display to the user.
 */
var ValidationError = function(field, errorKeyOrMessage) {
  this.field = field;
  this.errorKeyOrMessage = errorKeyOrMessage;
  if (typeof field == 'string') {
    this.errorKeyOrMessage = field;
  }
};

/**
 * Collector of errors. Instantiated when validateIBEForm is called, and used to collect:
 *
 *   - Errors
 *   - Debug messages
 *
 * And has methods to add those and know if there are any so far.
 *
 * @param form The form to be validated
 */
var ValidationErrors = function(form) {
  this.form = form;
  this.errors = new Array();
  this.debugMessages = new Array();
  this.firstElementWithError;
  this.setFirstElementWithError = function(elem) {
    if (!this.firstElementWithError) this.firstElementWithError = elem;
  };

  this.getFirstFormField = function(explicitNameOrSuffix, prefix) {
    return this.getFormFields(explicitNameOrSuffix, prefix, true);
  };

  this.getFormFields = function(explicitNameOrSuffix, prefix, returnFirst) {
    var elems = [];
    for (var i = 0; i < this.form.elements.length; i++) {
      var elem = this.form.elements[i];
      if (!elem.name || !elem.type) continue;
      if (!prefix && explicitNameOrSuffix == elem.name) {
        this.debug('getFirstFormField(' + explicitNameOrSuffix + ', ' + prefix + ') -> ' + elem.name, 3);
        return elem;
      } else {
        var fieldNameTuple = h_resolveFieldNameParts(elem);
        var fieldNamePrefix = fieldNameTuple[0];
        var fieldNameSuffix = fieldNameTuple[1];
        if (explicitNameOrSuffix == fieldNameSuffix) {
          if (!prefix || prefix == fieldNamePrefix) {
            this.debug('   1 ------ getFirstFormField(' + explicitNameOrSuffix + ', ' + prefix +
                       ') -> (' + fieldNameSuffix + ', ' + fieldNamePrefix + ')', 3);

            if (!returnFirst) {
              elems[elems.length] = elem;
            } else {
              return elem;
            }
          }
        }
      }
    }

    if (!returnFirst && elems && elems.length > 0) {
      return elems;
    }

    return null;
  };

  this.addError = function(field, errorKeyOrMessage) {
    var errs = this.errors;
    for (var i = 0; i < errs.length; i++) {
      var validationErr = errs[i];
      if (validationErr.errorKeyOrMessage == errorKeyOrMessage) {
        return;
      }
    }
    errs[errs.length] = new ValidationError(field, errorKeyOrMessage);
  };
  this.debug = function(msg) {
    if (d_debug_local && IBE.debug) {
      if (arguments.length <= 1 || d_debug_level_local >= arguments[1])
      {
        this.debugMessages[this.debugMessages.length] = msg;
      }
    }
  };

  this.hasErrors = function() {
    return this.errors.length > 0;
  };
  this.numErrors = function() {
    return this.errors.length;
  };
  this.createMessage = function() {
    var message = '';
    if (this.hasErrors()) {
      message += UiText.get('Javascript.ValidationHeader') + ': \n\n';
      for (var i = 0; i < this.errors.length; i++) {
        var err = this.errors[i];
        message += (' - ' + UiText.get(err.errorKeyOrMessage) + '' +

                    ((d_debug_local && IBE.debug) ?
                     '\n    -   (Debug: for field: ' +
                     (err.field ?
                      err.field.name :
                      '[no-element]') + ', ' + err.errorKeyOrMessage + ')' : '') +

                    '\n');
      }
    }
    if (d_debug_local && IBE.debug) {
      if (this.debugMessages.length > 0) {
        message += '\n\n';
        message += '-**---D-E-B-U-G---**---------------**-\n';
        for (var i = 0; i < this.debugMessages.length; i++) {
          message += ((1 + i) + '. ' + this.debugMessages[i] + '\n');
        }
        message += '-**---------------**---G-U-B-E-D---**-\n';
      }
    }
    return message;
  };
};


/**
 *
 * @param           form The form to validate.
 *
 * @optionalParam   breakAfterFirstError    if true, script will break after first error. Default is false.
 *
 * @optionalParam   addFuncs                Array Of JavaScript Functions To Call After Standard Checks
 *                                          Will not be executed id there are errors.
 *
 *                  The spec of hose functions are:
 *
 *                  function(inpitErrors){
 *                    // Any errors must be added by calling:
 *                    validationErrors.addError(element-with-error-for-debug || null, uiKeyOrResolvedMessage);
 *
 *                    return true; // If to continuewith next function
 *                    return false; // If to break
 *                  }
 * @optionalParam   additionalValidators    Map Of {elemSuffix : ValidationEntry,elemSuffix2 : ValidationEntry}
 *                                          Will overload any pre-registered validator if already registered
 *                                          in central VALIDATION_REGISTRY.
 *
 * @return true If the form was validated, false otherwise.
 *              If false is returned a javascript alert warned the user of the indata which must be corrected.
 */
function validateIBEForm(form, breakAfterFirstError, addFuncs, additionalValidators) {
  var formId = form.id;
  var formAction = form.action;
  var formElements = form.elements;

  var validationErrors = new ValidationErrors(form);
  var alreadyFoundErrorForRule = new Array();
  var i;

  for (i = 0; i < formElements.length; i++) {
    var elem = formElements[i];
    if (!elem.name || !elem.type) continue;
    // if (elem.type == 'hidden') continue;
    // h_elemReset(elem);

    var fieldNameTuple = h_resolveFieldNameParts(elem);
    var fieldNamePrefix = fieldNameTuple[0];
    var fieldNameSuffix = fieldNameTuple[1];

    validationErrors.debug('Checking ' + elem.name + '/' + fieldNamePrefix + '/' + fieldNameSuffix);
    // Only check same suffix Once.

    var validator = VALIDATION_REGISTRY[fieldNameSuffix];
    if (additionalValidators) {
      var olValidator = additionalValidators[fieldNameSuffix];
      if (olValidator && olValidator.uiKey) {
        validator = olValidator;
        validationErrors.debug('Found Overloaded Validator ' + elem.name + '::' + validator.uiKey, 3);
      }
    }

    if (validator && validator.validationFunction) {
      validationErrors.debug('Found Validator ' + elem.name + '::' + validator.uiKey, 3);
      if (!validator.matches(elem.name)) {
        validationErrors.debug('  ...but its not matching ' + elem.name + '::' + validator.namePrefix, 3);
        continue;
      }
      if (!validator.validationFunction(elem, validationErrors, fieldNamePrefix)) {
        validationErrors.setFirstElementWithError(elem);
        // h_elemError(elem);
        validationErrors.debug('Adding Error ' + elem.name + '::' + validator.uiKey, 2);
        validationErrors.debug('    - alreadyFoundErrorForRule[' + fieldNameSuffix + ']: ' + alreadyFoundErrorForRule[fieldNameSuffix], 3);

        if (alreadyFoundErrorForRule[fieldNameSuffix]) continue;
        validationErrors.addError(elem, validator.uiKey);
        alreadyFoundErrorForRule[fieldNameSuffix] = true;
      }
      if (breakAfterFirstError && validationErrors.hasErrors()) {
        validationErrors.debug('<<<<<< Breaking, found error #1 >>>>>>', 1);
        break;
      }
    }
  }

  // Call Any Additional functions.
  if (!validationErrors.hasErrors() && addFuncs && addFuncs.length > 0) {
    for (i = 0; i < addFuncs.length; i++) {
      var func = addFuncs[i];
      validationErrors.debug('Additional Function:\n' + func, 3);
      if (typeof func == 'function') {
        if (!func(validationErrors)) {
          validationErrors.debug('<<<<<< Breaking since function returned != true >>>>>>', 2);
          break;
        }
      }
      if (breakAfterFirstError && validationErrors.hasErrors()) {
        validationErrors.debug('<<<<<< Breaking, found error #2 >>>>>>', 2);
        break;
      }
    }
  }

  if (validationErrors.hasErrors()) {
    validationErrors.debug('NumErrors ' + validationErrors.numErrors());
    if (validationErrors.firstElementWithError) {
      h_elemFocus(validationErrors.firstElementWithError);
    }
    alert(validationErrors.createMessage());
    return false;
  } else {
    if (d_debug_level_local > 0 && validationErrors.debugMessages.length > 0) {
      if (!confirm('OK! but debug:' + validationErrors.createMessage())) return false;
    }
    return true;
  }
}
