/**
 * Copyright (c) Istituto Nazionale di Fisica Nucleare, 2006-2014.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.italiangrid.voms.ac.impl;

import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.List;

import org.bouncycastle.asn1.x509.AttributeCertificate;
import org.italiangrid.voms.VOMSAttribute;
import org.italiangrid.voms.ac.VOMSACLookupStrategy;
import org.italiangrid.voms.ac.VOMSACValidationStrategy;
import org.italiangrid.voms.ac.VOMSACValidator;
import org.italiangrid.voms.ac.VOMSValidationResult;
import org.italiangrid.voms.ac.ValidationResultListener;
import org.italiangrid.voms.asn1.VOMSACUtils;
import org.italiangrid.voms.store.UpdatingVOMSTrustStore;
import org.italiangrid.voms.store.VOMSTrustStore;
import org.italiangrid.voms.store.VOMSTrustStores;
import org.italiangrid.voms.util.CertificateValidatorBuilder;
import org.italiangrid.voms.util.NullListener;

import eu.emi.security.authn.x509.X509CertChainValidatorExt;

/**
 * The default implementation of the VOMS validator.
 * 
 * @author andreaceccanti
 *
 */
public class DefaultVOMSValidator extends DefaultVOMSACParser implements
  VOMSACValidator {

  public static final String DEFAULT_TRUST_ANCHORS_DIR = "/etc/grid-security/certificates";

  private final VOMSACValidationStrategy validationStrategy;
  private final VOMSTrustStore trustStore;
  private ValidationResultListener validationResultListener;
  private final Object listenerLock = new Object();

  public static class Builder {

    private VOMSACValidationStrategy validationStrategy;
    private VOMSTrustStore trustStore;
    private ValidationResultListener validationResultListener;

    private X509CertChainValidatorExt certChainValidator;
    private VOMSACLookupStrategy acLookupStrategy;

    public Builder() {

    }

    public Builder validationStrategy(VOMSACValidationStrategy s) {

      this.validationStrategy = s;
      return this;
    }

    public Builder trustStore(VOMSTrustStore ts) {

      this.trustStore = ts;
      return this;
    }

    public Builder validationListener(ValidationResultListener l) {

      this.validationResultListener = l;
      return this;
    }

    public Builder certChainValidator(X509CertChainValidatorExt v) {

      this.certChainValidator = v;
      return this;
    }

    public Builder acLookupStrategy(VOMSACLookupStrategy ls) {

      this.acLookupStrategy = ls;
      return this;
    }

    private void sanityChecks() {

      if (validationStrategy == null) {
        if (trustStore == null)
          trustStore = VOMSTrustStores.newTrustStore();

        if (certChainValidator == null)
          certChainValidator = new CertificateValidatorBuilder()
            .trustAnchorsDir(DEFAULT_TRUST_ANCHORS_DIR).build();

        validationStrategy = new DefaultVOMSValidationStrategy(trustStore,
          certChainValidator);
      }

      if (validationResultListener == null) {

        validationResultListener = NullListener.INSTANCE;

      }

      if (acLookupStrategy == null){
        
        acLookupStrategy = new LeafACLookupStrategy();
      
      }
        
    }

    public DefaultVOMSValidator build() {

      sanityChecks();
      return new DefaultVOMSValidator(this);
    }
  }

  private DefaultVOMSValidator(Builder b) {

    super(b.acLookupStrategy);

    this.validationStrategy = b.validationStrategy;
    this.trustStore = b.trustStore;
    this.validationResultListener = b.validationResultListener;
  }

  public List<VOMSValidationResult> validateWithResult(
    X509Certificate[] validatedChain) {

    return internalValidate(validatedChain);
  }

  protected List<VOMSValidationResult> internalValidate(
    X509Certificate[] validatedChain) {

    List<VOMSAttribute> parsedAttrs = parse(validatedChain);
    List<VOMSValidationResult> results = new ArrayList<VOMSValidationResult>();

    for (VOMSAttribute a : parsedAttrs) {

      VOMSValidationResult result = validationStrategy.validateAC(a,
        validatedChain);

      synchronized (listenerLock) {
        validationResultListener.notifyValidationResult(result);
      }

      results.add(result);

    }

    return results;
  }

  public List<VOMSAttribute> validate(X509Certificate[] validatedChain) {

    List<VOMSAttribute> validAttributes = new ArrayList<VOMSAttribute>();

    for (VOMSValidationResult result : internalValidate(validatedChain)) {

      if (result.isValid()) {
        validAttributes.add(result.getAttributes());
      }
    }

    return validAttributes;
  }

  public void shutdown() {

    if (trustStore instanceof UpdatingVOMSTrustStore) {
      ((UpdatingVOMSTrustStore) trustStore).cancel();
    }
  }

  public List<AttributeCertificate> validateACs(List<AttributeCertificate> acs) {

    List<AttributeCertificate> validatedAcs = new ArrayList<AttributeCertificate>();

    for (AttributeCertificate ac : acs) {

      VOMSAttribute vomsAttrs = VOMSACUtils.deserializeVOMSAttributes(ac);

      VOMSValidationResult result = validationStrategy.validateAC(vomsAttrs);

      synchronized (listenerLock) {
        validationResultListener.notifyValidationResult(result);
      }

      if (result.isValid()) {
        validatedAcs.add(ac);
      }
    }

    return validatedAcs;
  }

  public void setValidationResultListener(ValidationResultListener listener) {

    synchronized (listenerLock) {
      if (listener != null) {
        this.validationResultListener = listener;
      }
    }
  }
}
