xFormValidatorFactory = {
  init : function(validators, callBack) {
    this.initDone   = true;
    this.validators = {};
    this.setCallBack(callBack);
    for (var i = 0; i < validators.length; i++) {
      this.addValidator(validators[i]);
    }
  },
  addValidator : function(validator) {
    var validateableDataTypes = validator.getValidateableDataTypes();
    for (var i = 0; i < validateableDataTypes.length; i++) {
      this.validators[validateableDataTypes[i]] = validator;
    }
  },
  setLogBuffer : function(logBuffer) {
    this.logBuffer = logBuffer;
  },
  createValidator : function(type) {
    if (this.validators[type]) {
      return new this.validators[type](this.logBuffer);
    } else {
      throw new xException('cant find suitable validator for type ' + type);
    }
  },
  setCallBack : function(callBack) {
    this.callBack = callBack;
  },
  getCallBack : function() {
    return this.callBack;
  },
  handleCallBack : function(context, element, validated, run) {
    try {
      this.callBack(context, element, validated, run);
    } catch (e) {
    }
  }
}
//--------------------------------------------------------------------------------------------------
function xGroupValidator(logBuffer) {
  this.logBuffer = logBuffer;
}
xGroupValidator.getValidateableDataTypes = function() {
  return new Array('form', 'group', 'set');
}
xGroupValidator.prototype.validate = function(context, element, run) {
  var result      = true;
  var subElements = element.getSubElements();

  for (var i = 0; i < subElements.length; i++) {
    var subElement  = subElements[i];
    var restriction = new xFormElementRestriction(subElement);

    if (subElement.isValidateable() && restriction.isEnabled()) {
      var validator = xFormValidatorFactory.createValidator(subElement.getDataType());
      var temporaryResult = validator.validate(restriction, subElement, run);
      xFormValidatorFactory.handleCallBack(restriction, subElement, temporaryResult, run);
      result = temporaryResult && result;
    }
  }
  if (element.getCustomProperty('postCheck', 'form')) {
    try {
      eval('result = ' + element.getCustomProperty('postCheck', 'form') + "(context, element, result) && result");
    } catch(e){
    }
  }
  return result;
}
//--------------------------------------------------------------------------------------------------
function xStringValidator(logBuffer) {
  this.logBuffer = logBuffer;
}
xStringValidator.getValidateableDataTypes = function() {
  return new Array('text', 'string');
}
xStringValidator.prototype.validate = function(context, element, run) {
  var value = element.getValue();

  var result = !(context.rmin > 0 && value.length < context.rmin ||
                 context.rmax > 0 && value.length > context.rmax);

  if (context.rfilter && !(value.match(context.rfilter))) {
    if ('no' == context.rrequired && value.length == 0) {
      result = true;
    } else {
      result = false;
    }
  }

  return result;
}
//--------------------------------------------------------------------------------------------------
function xMultisetValidator(logBuffer) {
  this.logBuffer = logBuffer;
}
xMultisetValidator.getValidateableDataTypes = function() {
  return new Array('multiset');
}
xMultisetValidator.prototype.validate = function(context, element, run) {
  var value = element.getValue();
  if (isArray(value)) {
    value = value.length;
  } else {
    return false;
  }

  var result = !(context.rmin > 0 && value < context.rmin ||
                 context.rmax > 0 && value > context.rmax);

  if (!result) {
    this.logBuffer.addEvent(
      element,
      context,
      sprintf('  cannot validate field "%s", %d is not in range (%s, %s)', element, value, context.rmin, context.rmax ? context.rmax : '*'),
      this.logBuffer.MSG_ERROR
    );
  }
  return result;
}
//--------------------------------------------------------------------------------------------------
function xNumericValidator(logBuffer) {
  this.logBuffer = logBuffer;
}
xNumericValidator.getValidateableDataTypes = function() {
  return new Array('number', 'integer', 'float');
}
xNumericValidator.prototype.validate = function(context, element, run) {
  var value = parseFloat(element.getValue());

  var result = !(isNaN(value) ||
                 context.rmin > 0 && value < context.rmin ||
                 context.rmax > 0 && value > context.rmax);

  if (!result) {
    this.logBuffer.addEvent(
      element,
      context,
      sprintf('  cannot validate field "%s"', element),
      this.logBuffer.MSG_ERROR
    );
  }
  return result;
}
//--------------------------------------------------------------------------------------------------
function xDateTimeValidator(logBuffer) {
  this.logBuffer  = logBuffer;
  this.reDate     = new RegExp("^(\\d{2})\\.(\\d{2})\\.(\\d{4})$");
  this.reDateTime = new RegExp("^(\\d{2})\\.(\\d{2})\\.(\\d{4}) (\\d{2}):(\\d{2})$");
  this.reTime     = new RegExp("^(\\d{2}):(\\d{2})$");
}
xDateTimeValidator.getValidateableDataTypes = function() {
  return new Array('date', 'time', 'datetime');
}
xDateTimeValidator.prototype.checkDate = function(dd, mm, yyyy) {
  mm -= 0;
  dd -= 0;
  yyyy -=0;
  var validDate = new Date(yyyy, mm - 1, dd);
  var result = true;

  if (dd != validDate.getDate()) {
    result = false;
  }
  if (mm != validDate.getMonth() + 1) {
    result = false;
  }
  if (yyyy != validDate.getFullYear()) {
    result = false;
  }
  return result;
}
xDateTimeValidator.prototype.checkTime = function(hh, mm) {
  var result = true;
  if (hh < 0 || hh > 23) {
    result = false;
  }
  
  if (mm < 0 || mm > 59) {
    result = false;
  }

  return result;
}
xDateTimeValidator.prototype.validate = function(context, element, run) {
  var value  = element.getValue();
  var data   = new Array();
  var result = true;


  switch (element.getDataType()) {
    case 'date':
      if (data = value.match(this.reDate)) {
        result = this.checkDate(data[1], data[2], data[3]);
      } else {
        result = false;
      }
      break;
    case 'datetime':
      if (data = value.match(this.reDateTime)) {
        result = this.checkDate(data[1], data[2], data[3]);
        result = this.checkTime(data[4], data[5]) & result;
      } else {
        result = false;
      }
      break;
    case 'time':
      if (data = value.match(this.reTime)) {
        result = this.checkDate(data[1], data[2]);
      } else {
        result = false;
      }
      break;
  }
  if (!String(value).length &&
      'yes' != context.rrequired) {
    result = true;
  }
/*
  var result = !(isNaN(value) ||
                 context.rmin > 0 && value < context.rmin ||
                 context.rmax > 0 && value > context.rmax);
*/
  if (!result) {
    this.logBuffer.addEvent(
      element,
      context,
      sprintf('  cannot validate field "%s"', element),
      this.logBuffer.MSG_ERROR
    );
  }
  return result;
}
//--------------------------------------------------------------------------------------------------
function xExampleValidator(logBuffer) {
  this.logBuffer = logBuffer;
}
xExampleValidator.getValidateableDataTypes = function() {
  return new Array('testtype1', 'testtype2');
}
xExampleValidator.prototype.validate = function(context, element, run) {
  log(sprintf('    test validator triggered!\n    checking element "%s" value: %s', element.getName(), element.getValue()));
  return true;
}
//--------------------------------------------------------------------------------------------------
function xFunctionValidator(logBuffer) {
  this.logBuffer = logBuffer;
}
xFunctionValidator.getValidateableDataTypes = function() {
  return new Array('function');
}
xFunctionValidator.prototype.validate = function(context, element, run) {
  var result;
  try {
    log(sprintf('    validing field "%s" using function %s', element, context.rcustom))
    eval('result = ' + context.rcustom + '(element.getValue());');
  } catch(e) {
    log(sprintf('    failed to validate field "%s" using function <i>%s</i>', element, context.rcustom))
    result = false;
  }
  return result;
}
function testFunctionValidation(value) {
  return true;
}
//--------------------------------------------------------------------------------------------------
xFormValidatorFactory.init(
  new Array(
    xStringValidator,
    xNumericValidator,
    xGroupValidator,
    xFunctionValidator,
    xDateTimeValidator,
    xMultisetValidator)
);
xFormValidatorFactory.addValidator(xExampleValidator);