package com.atlassian.stash.internal.web.util;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Collections2;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;

import javax.annotation.Nullable;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

public class FilteredErrors implements Errors {
    
    private final Errors delegate;
    private final Predicate<String> ignoreFieldPredicate;

    public FilteredErrors(Errors delegate, String... ignoredFieldNames) {
        this(delegate, Predicates.in(Sets.newHashSet(ignoredFieldNames)));
    }

    public FilteredErrors(Errors delegate, Predicate<String> ignoreFieldPredicate) {
        this.delegate = delegate;
        this.ignoreFieldPredicate = ignoreFieldPredicate;
    }

    @Override
    public String getObjectName() {
        return delegate.getObjectName();
    }

    @Override
    public void setNestedPath(String nestedPath) {
        delegate.setNestedPath(nestedPath);
    }

    @Override
    public String getNestedPath() {
        return delegate.getNestedPath();
    }

    @Override
    public void pushNestedPath(String subPath) {
        delegate.pushNestedPath(subPath);
    }

    @Override
    public void popNestedPath() throws IllegalStateException {
        delegate.popNestedPath();
    }

    @Override
    public void reject(String errorCode) {
        delegate.reject(errorCode);
    }

    @Override
    public void reject(String errorCode, String defaultMessage) {
        delegate.reject(errorCode, defaultMessage);
    }

    @Override
    public void reject(String errorCode, Object[] errorArgs, String defaultMessage) {
        delegate.reject(errorCode, errorArgs, defaultMessage);
    }

    @Override
    public void rejectValue(String field, String errorCode) {
        delegate.rejectValue(field, errorCode);
    }

    @Override
    public void rejectValue(String field, String errorCode, String defaultMessage) {
        delegate.rejectValue(field, errorCode, defaultMessage);
    }

    @Override
    public void rejectValue(String field, String errorCode, Object[] errorArgs, String defaultMessage) {
        delegate.rejectValue(field, errorCode, errorArgs, defaultMessage);
    }

    @Override
    public void addAllErrors(Errors errors) {
        this.delegate.addAllErrors(errors);
    }

    @Override
    public boolean hasErrors() {
        return hasGlobalErrors() || hasFieldErrors();
    }

    @Override
    public int getErrorCount() {
        return delegate.getErrorCount();
    }

    @Override
    public List<ObjectError> getAllErrors() {
        return delegate.getAllErrors();
    }

    @Override
    public boolean hasGlobalErrors() {
        return delegate.hasGlobalErrors();
    }

    @Override
    public int getGlobalErrorCount() {
        return delegate.getGlobalErrorCount();
    }

    @Override
    public List<ObjectError> getGlobalErrors() {
        return delegate.getGlobalErrors();
    }

    @Override
    public ObjectError getGlobalError() {
        return delegate.getGlobalError();
    }

    @Override
    public boolean hasFieldErrors() {
        return getFieldErrorCount() != 0;
    }

    @Override
    public int getFieldErrorCount() {
        return getFieldErrors().size();
    }

    @Override
    public List<FieldError> getFieldErrors() {
        return Lists.newArrayList(Collections2.filter(delegate.getFieldErrors(), new Predicate<FieldError>() {
            public boolean apply(@Nullable FieldError input) {
                return input != null && !ignoreFieldPredicate.apply(input.getField());
            }
        }));
    }

    @Override
    public FieldError getFieldError() {
        final Iterator<FieldError> iterator = getFieldErrors().iterator();
        return iterator.hasNext() ? iterator.next() : null;
    }

    @Override
    public boolean hasFieldErrors(String field) {
        return !ignoreFieldPredicate.apply(field) && delegate.hasFieldErrors(field);
    }

    @Override
    public int getFieldErrorCount(String field) {
        return ignoreFieldPredicate.apply(field) ? 0 : delegate.getFieldErrorCount(field);
    }

    @Override
    public List<FieldError> getFieldErrors(String field) {
        return ignoreFieldPredicate.apply(field) ? Collections.<FieldError>emptyList() : delegate.getFieldErrors(field);
    }

    @Override
    public FieldError getFieldError(String field) {
        return ignoreFieldPredicate.apply(field) ? null : delegate.getFieldError(field);
    }

    @Override
    public Object getFieldValue(String field) {
        return delegate.getFieldValue(field);
    }

    @Override
    public Class getFieldType(String field) {
        return delegate.getFieldType(field);
    }
}
