package de.tu_dresden.diplom.richter_mirko_mat2628335.common.handler;

import org.apache.axis.MessageContext;
import org.apache.axis.AxisFault;
import org.apache.log4j.Logger;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.entity.IFEntity;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.keystore.ApplicationSpecificKeystoreUtility;
import de.tu_dresden.diplom.richter_mirko_mat2628335.witness.entity.EntityManagerFactory;

/**
 * Base-Class for all Handlers that sign any XML-Content
 */
public abstract class BaseSigner extends BaseContextHandler {

   private static final Logger logger = Logger.getLogger(BaseSigner.class);

   protected static final int SIGNATURE_METHOD_XSS4J_ENVELOPED = 0;
   protected static final int SIGNATURE_METHOD_TSIK_WSSEC = 1;

   protected static final int SIGNATURE_LEVEL_BODY = 10;
   protected static final int SIGNATURE_LEVEL_COORDINATION_CONTEXT = 11;
   protected static final int SIGNATURE_LEVEL_STEP_CONTEXT = 12;
   protected static final int SIGNATURE_LEVEL_COUNTING_CONTEXT = 13;

   public static final String SIGNATURE_TYPE_ENVELOPED = "enveloped";
   public static final String SIGNATURE_IMPLEMENTATION_XSS4J = "xss4j";

   public static final String SIGNATURE_TYPE_WS_SECURITY = "wssec";
   public static final String SIGNATURE_IMPLEMENTATION_TSIK = "tsik";

   public static final String OPTION_SIGNATURE_TYPE = "signatureType";
   public static final String OPTION_SIGNATURE_IMPLEMENTATION = "signatureImpl";
   public static final String OPTION_SIGNATURE_ENTITY = "signingEntity";

   private int signatureMethod = SIGNATURE_METHOD_XSS4J_ENVELOPED;
   private String signatureType = SIGNATURE_TYPE_ENVELOPED;
   private String signatureImpl = SIGNATURE_IMPLEMENTATION_XSS4J;

   /**
    * @see org.apache.axis.Handler#invoke(org.apache.axis.MessageContext)
    */
   public void invoke(MessageContext messageContext) throws AxisFault {
      final String fn = "[invoke] ";
      String signatureType = (String) getOption(OPTION_SIGNATURE_TYPE);
      if (signatureType != null) {
         if (signatureType.equals(SIGNATURE_TYPE_ENVELOPED)) {
            this.signatureType = SIGNATURE_TYPE_ENVELOPED;
         }else if(signatureType.equals(SIGNATURE_TYPE_WS_SECURITY)){
            this.signatureType = SIGNATURE_TYPE_WS_SECURITY;
         }else{
            logger.error(fn + "unknown signatureType: '" + signatureType + "'. Using default '" + SIGNATURE_TYPE_ENVELOPED + "'");
            this.signatureType = SIGNATURE_TYPE_ENVELOPED;
         }
      }else{
         if(logger.isInfoEnabled())logger.info(fn + "No explicite signature-type given! Using default ('" + SIGNATURE_TYPE_ENVELOPED  + "')");
         this.signatureType = SIGNATURE_TYPE_ENVELOPED;
      }
      String signatureImpl = (String) getOption(OPTION_SIGNATURE_IMPLEMENTATION);
      if(signatureImpl != null){
         if(signatureImpl.equals(SIGNATURE_IMPLEMENTATION_XSS4J)){
            this.signatureImpl = SIGNATURE_IMPLEMENTATION_XSS4J;
         }else if(signatureType.equals(SIGNATURE_IMPLEMENTATION_TSIK)){
            this.signatureImpl = SIGNATURE_IMPLEMENTATION_TSIK;
         }else{
            logger.error(fn + "unknown signatureImpl: '" + signatureImpl + "'. Using default '" + SIGNATURE_IMPLEMENTATION_XSS4J + "'");
            this.signatureType = SIGNATURE_IMPLEMENTATION_XSS4J;
         }
      }else{
         if(logger.isInfoEnabled())logger.info(fn + "No explicite signature-impl given! Using default ('" + SIGNATURE_IMPLEMENTATION_XSS4J  + "')");
         this.signatureType = SIGNATURE_IMPLEMENTATION_XSS4J;
      }
      if(this.signatureType == SIGNATURE_TYPE_WS_SECURITY && this.signatureImpl == SIGNATURE_IMPLEMENTATION_TSIK){
         signatureMethod = SIGNATURE_METHOD_TSIK_WSSEC;
      }else{
         signatureMethod = SIGNATURE_METHOD_XSS4J_ENVELOPED;
      }
   }

   /**
    * Gets the signature-method specified in the Axis-configuration-File
    * @return The Method
    */
   protected int getSignatureMethod() {
      return signatureMethod;
   }

   /**
    * Gets the signature-type specified in the Axis-configuration-File
    * @return The Type
    */
   public String getSignatureType() {
      return signatureType;
   }

   /**
    * Gets the signature-impl to bes used specified in the Axis-configuration-File
    * @return The Implementation
    */
   public String getSignatureImpl() {
      return signatureImpl;
   }

   /**
    * Checks wheather the implementation to be used is IBMs XSS4j or not
    * @return true if XSS4j to use, false else
    */
   protected boolean isXSS4JEnveloped(){
      return getSignatureMethod() == SIGNATURE_METHOD_XSS4J_ENVELOPED;
   }

   /**
    * Checks wheather the implementation to be used is Verisigns TSIK or not
    * @return true if TSIK to use, false else
    */
   protected boolean isTSIKWSSEC(){
      return getSignatureMethod() == SIGNATURE_METHOD_TSIK_WSSEC;
   }

   /**
    * Gets the entity that is being used to sign the content.
    * @return The entity in case it could be determined, NULL else
    */
   protected IFEntity getSigningEntity(){
      final String fn = "[getSigningEntity] ";
      IFEntity result = null;
      String signingEntity = (String) getOption(OPTION_SIGNATURE_ENTITY);
      if(signingEntity != null){
         if(signingEntity.equals("@handlingInfo")){
            result = getSigningEntityFromHandlingInfo();
         }else{
            result = EntityManagerFactory.getEntityManager().locateEntityByAlias(signingEntity);
            if(result == null){
               logger.fatal(fn + "couldn't locate entity that was specified by '" + signingEntity + "' in the handler-WSDD!");
            }
         }
      }else{
         if(logger.isInfoEnabled())logger.info(fn + "No signingEntity-parameter defined ... taking it from HandlingInfo!");
         result = getSigningEntityFromHandlingInfo();
      }
      return result;
   }

   /**
    * In case the entity ist not directly defined in the conf-file (value=@handlingInfo)
    * this method is used to get the right entity out of the current handlingInfo. From
    * which object within handlingInfo the entity shall be taken must be decided by the concrete
    * implementation
    * @return
    */
   public abstract IFEntity getSigningEntityFromHandlingInfo();
}
