
package de.ivu.elect.jee.presentation.dynamicform;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.Validator;
import javax.faces.validator.ValidatorException;

import org.primefaces.model.DualListModel;

import de.ivu.elect.jee.faces.FacesComponentUtil;
import de.ivu.elect.jee.faces.FacesMessageUtil;

public class PickListTargetSizeRangeValidator implements Validator {

  public static final String MAX_SIZE_SUMMARY_KEY = "component.pick.list.target.size.range.validator.MAXIMUM";

  public static final String MIN_SIZE_SUMMARY_KEY = "component.pick.list.target.size.range.validator.MINIMUM";

  public static final String FIXED_SIZE_SUMMARY_KEY = "component.pick.list.target.size.range.validator.SIZE";

  public static final String RANGE_SUMMARY_KEY = "component.pick.list.target.size.range.validator.RANGE";

  public static final String DETAIL_KEY_SUFFIX = "_detail";

  public static final String MIN_SIZE_ATTRIBUTE_NAME = "minTargetSize";

  public static final String MAX_SIZE_ATTRIBUTE_NAME = "maxTargetSize";

  public PickListTargetSizeRangeValidator() {
    super();
  }

  @Override
  public void validate(FacesContext ctx, UIComponent component, Object value) throws ValidatorException {
    if (value instanceof DualListModel) {
      validateSize(ctx, component, ((DualListModel< ? >) value).getTarget().size(), getMinSize(component), getMaxSize(component));
    }
  }

  private void validateSize(FacesContext ctx, UIComponent component, long size, Long minSize, Long maxSize) {
    if (minSize != null) {
      if (maxSize != null) {
        if (minSize.equals(maxSize)) {
          validateFixedSize(ctx, component, size, minSize);
        } else {
          validateRange(ctx, component, size, minSize, maxSize);
        }
      }

      validateMinSize(ctx, component, size, minSize);
    }

    if (maxSize != null) {
      validateMaxSize(ctx, component, size, maxSize);
    }
  }

  private void validateMinSize(FacesContext ctx, UIComponent component, long size, long minSize) {
    if (size < minSize) {
      throwValidatorException(ctx, component, MIN_SIZE_SUMMARY_KEY, minSize);
    }
  }

  private void validateMaxSize(FacesContext ctx, UIComponent component, long size, long maxSize) {
    if (size > maxSize) {
      throwValidatorException(ctx, component, MAX_SIZE_SUMMARY_KEY, maxSize);
    }
  }

  private void validateFixedSize(FacesContext ctx, UIComponent component, long size, long fixedSize) {
    if (size != fixedSize) {
      throwValidatorException(ctx, component, FIXED_SIZE_SUMMARY_KEY, fixedSize);
    }
  }

  private void validateRange(FacesContext ctx, UIComponent component, long size, long minSize, long maxSize) {
    if (size < minSize || size > maxSize) {
      throwValidatorException(ctx, component, RANGE_SUMMARY_KEY, minSize, maxSize);
    }
  }

  protected Long getMinSize(UIComponent component) {
    return getLongAttributeOrDefault(component, MIN_SIZE_ATTRIBUTE_NAME);
  }

  protected Long getMaxSize(UIComponent component) {
    return getLongAttributeOrDefault(component, MAX_SIZE_ATTRIBUTE_NAME);
  }

  private Long getLongAttributeOrDefault(UIComponent component, String attributeName) {
    Object result = FacesComponentUtil.getAttributeFromDynamicFormControlOrNormal(component, attributeName);

    if (result instanceof Number) {
      return ((Number) result).longValue();
    }

    return null;
  }

  protected void throwValidatorException(FacesContext ctx, UIComponent component, String summaryKey, long size) {
    throw new ValidatorException(FacesMessageUtil.createErrorMessage(ctx, summaryKey, summaryKey + DETAIL_KEY_SUFFIX,
        FacesComponentUtil.getLocalizedLabelOrId(component), size));
  }

  protected void throwValidatorException(FacesContext ctx, UIComponent component, String summaryKey, long minSize, long maxSize) {
    throw new ValidatorException(FacesMessageUtil.createErrorMessage(ctx, summaryKey, summaryKey + DETAIL_KEY_SUFFIX,
        FacesComponentUtil.getLocalizedLabelOrId(component), minSize, maxSize));
  }
}
