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

import org.apache.log4j.Logger;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.Document;
import org.w3c.dom.Attr;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.NS;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.Constants;
import de.tu_dresden.diplom.richter_mirko_mat2628335.common.handler.BaseHandler;

/**
 * This class represents a coordination context that is red from the SOAP-header (maybe
 * created by the witness too and than written to the header)
 */
public class CoordinationContext extends BaseContext implements IFCoordinationContext {

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

   private String coordinationType = null;
   private Element contextElement = null;
   private String creationTime = null;
   private String identification = null;
   private String expires = null;
   private String registrationServiceAddress = null;
   private String timeoutDigestAgreementOnRequestPhaseMS = null;
   private String timeoutDigestAgreementOnResponsePhaseMS = null;

   /**
    * @see IFCoordinationContext#getCoordinationType()
    */
   public String getCoordinationType() {
      return coordinationType;
   }

   /**
    * Sets the coordination type.
    * @param coordinationType
    */
   public void setCoordinationType(String coordinationType) {
      this.coordinationType = coordinationType;
   }

   /**
    * @see IFCoordinationContext#getContextElement()
    */
   public Element getContextElement() {
      return contextElement;
   }

   /**
    * Sets the context-Element that has beend red from the SOAP-Header
    * @param contextElement
    */
   public void setContextElement(Element contextElement) {
      this.contextElement = contextElement;
      readElements();
   }

   /**
    * @see IFCoordinationContext#getIdentification()
    */
   public String getIdentification() {
      return identification;
   }

   /**
    * Sets the identification of the context
    * @param identification
    */
   public void setIdentification(String identification) {
      this.identification = identification;
   }

   /**
    * @see IFCoordinationContext#getExpires()
    */
   public String getExpires() {
      return expires;
   }

   /**
    * @see IFCoordinationContext#getExpiresLong()
    */
   public long getExpiresLong() {
      final String fn = "[getExpiresLong] ";
      long result = 0;
      if (getExpires() != null) {
         try {
            result = Long.parseLong(getExpires());
         } catch (NumberFormatException e) {
            logger.error(fn + "failure while parsing expires: '" + getExpires() + "'!");
         }
      }
      return result;
   }

   /**
    * Sets expires.
    * @param expires
    */
   public void setExpires(String expires) {
      this.expires = expires;
   }

   /**
    * @see IFCoordinationContext#getRegistrationServiceAddress()
    */
   public String getRegistrationServiceAddress() {
      return registrationServiceAddress;
   }

   /**
    * Sets the registrationservice-adresss (witness-service)
    * @param registrationServiceAddress
    */
   public void setRegistrationServiceAddress(String registrationServiceAddress) {
      this.registrationServiceAddress = registrationServiceAddress;
   }

   /**
    * @see IFCoordinationContext#getTimeoutDigestAgreementOnRequestPhaseMS()
    */
   public String getTimeoutDigestAgreementOnRequestPhaseMS() {
      return timeoutDigestAgreementOnRequestPhaseMS;
   }

   /**
    * @see IFCoordinationContext#getTimeoutDigestAgreementOnRequestPhaseMSLong()
    */
   public long getTimeoutDigestAgreementOnRequestPhaseMSLong() {
      final String fn = "[getTimeoutDigestAgreementOnRequestPhaseMSLong] ";
      long result = 0;
      if (getTimeoutDigestAgreementOnRequestPhaseMS() != null) {
         try {
            result = Long.parseLong(getTimeoutDigestAgreementOnRequestPhaseMS());
         } catch (NumberFormatException e) {
            logger.error(fn + "failure while parsing timeoutDigestAgreementOnRequestPhaseMS: '" + getTimeoutDigestAgreementOnRequestPhaseMS() + "'!");
         }
      }
      return result;
   }

   /**
    * Stets the timeout for digest agreement in request-phase
    * @param timeoutDigestAgreementOnRequestPhaseMS
    */
   public void setTimeoutDigestAgreementOnRequestPhaseMS(String timeoutDigestAgreementOnRequestPhaseMS) {
      this.timeoutDigestAgreementOnRequestPhaseMS = timeoutDigestAgreementOnRequestPhaseMS;
   }

   /**
    * @see IFCoordinationContext#getTimeoutDigestAgreementOnResponsePhaseMS()
    */
   public String getTimeoutDigestAgreementOnResponsePhaseMS() {
      return timeoutDigestAgreementOnResponsePhaseMS;
   }

   /**
    * @see IFCoordinationContext#getTimeoutDigestAgreementOnResponsePhaseMSLong()
    */
   public long getTimeoutDigestAgreementOnResponsePhaseMSLong() {
      final String fn = "[getTimeoutDigestAgreementOnResponsePhaseMSLong] ";
      long result = 0;
      if (getTimeoutDigestAgreementOnResponsePhaseMS() != null) {
         try {
            result = Long.parseLong(getTimeoutDigestAgreementOnResponsePhaseMS());
         } catch (NumberFormatException e) {
            logger.error(fn + "failure while parsing timeoutDigestAgreementOnResponsePhaseMS: '" + getTimeoutDigestAgreementOnResponsePhaseMS() + "'!");
         }
      }
      return result;
   }

   /**
    * Stets the timeout for digest agreement in response-phase
    * @param timeoutDigestAgreementOnResponsePhaseMS
    */
   public void setTimeoutDigestAgreementOnResponsePhaseMS(String timeoutDigestAgreementOnResponsePhaseMS) {
      this.timeoutDigestAgreementOnResponsePhaseMS = timeoutDigestAgreementOnResponsePhaseMS;
   }

   /**
    * Reads the data out of the current contextElement (org.w3c.dom.Element)
    */
   private void readElements() {
      final String fn = "[readElements]";
      Element elem = (Element) getContextElement().getElementsByTagNameNS(NS.WS_UTILITY_NAMESPACE, "Expires").item(0);
      if (elem != null) {
         setExpires(elem.getTextContent());
         if (logger.isDebugEnabled()) logger.debug(fn + " setting EXPIRES to '" + getExpires() + "'");
      } else {
         logger.warn(fn + " couldn't find mandatory element EXPIRES!");
      }
      elem = (Element) getContextElement().getElementsByTagNameNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "TimeoutDigestAgreementOnRequestPhaseMS").item(0);
      if (elem != null) {
         setTimeoutDigestAgreementOnRequestPhaseMS(elem.getTextContent());
         if (logger.isDebugEnabled()) logger.debug(fn + " setting TimeoutDigestAgreementOnRequestPhaseMS to '" + getTimeoutDigestAgreementOnRequestPhaseMS() + "'");
      } else {
         logger.warn(fn + " couldn't find mandatory element TimeoutDigestAgreementOnRequestPhaseMS!");
      }
      elem = (Element) getContextElement().getElementsByTagNameNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "TimeoutDigestAgreementOnResponsePhaseMS").item(0);
      if (elem != null) {
         setTimeoutDigestAgreementOnResponsePhaseMS(elem.getTextContent());
         if (logger.isDebugEnabled()) logger.debug(fn + " setting TimeoutDigestAgreementOnResponsePhaseMS to '" + getTimeoutDigestAgreementOnResponsePhaseMS() + "'");
      } else {
         logger.warn(fn + " couldn't find mandatory element TimeoutDigestAgreementOnResponsePhaseMS!");
      }
      elem = (Element) getContextElement().getElementsByTagNameNS(NS.WS_UTILITY_NAMESPACE, "Identifier").item(0);
      if (elem != null) {
         setIdentification(elem.getTextContent());
         if (logger.isDebugEnabled()) logger.debug(fn + " setting IDENTIFIER to '" + getIdentification() + "'");
      } else {
         logger.warn(fn + " couldn't find mandatory element IDENTIFIER!");
      }
      elem = (Element) getContextElement().getElementsByTagNameNS(NS.WS_COORDINATION_NAMESPACE, "CoordinationType").item(0);
      if (elem != null) {
         setCoordinationType(elem.getTextContent());
         if (logger.isDebugEnabled()) logger.debug(fn + " setting COORDINATIONTYPE to '" + getCoordinationType() + "'");
      } else {
         logger.warn(fn + " couldn't find mandatory element COORDINATIONTYPE!");
      }
      //elem = (Element) context.getElementsByTagNameNS(NS.WS_COORDINATION_NAMESPACE, "RegistrationService").item(0);
      //if (elem != null) {
      elem = (Element) getContextElement().getElementsByTagNameNS(NS.WS_UTILITY_NAMESPACE, "Address").item(0);
      if (elem != null) {
         setRegistrationServiceAddress(elem.getTextContent());
         if (logger.isDebugEnabled()) logger.debug(fn + " setting REGISTRATIONADDRESS to '" + getRegistrationServiceAddress() + "'");
      } else {
         logger.warn(fn + " couldn't find mandatory element REGISTRATIONADDRESS!");
      }
      //} else {
      //   logger.warn(fn + " couldn't find mandatory element REGISTRATIONSERVICE!");
      //}
      elem = (Element) getContextElement().getElementsByTagNameNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "AllowedSignatureMethods").item(0);
      if (elem != null) {
         if (elem.getAttributeNode("restricted").getTextContent().equals("true")) {
            if (logger.isDebugEnabled()) logger.debug(fn + " Restriction in allowed signature algorithms!");
            NodeList signatureMethods = elem.getElementsByTagNameNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "SignatureMethod");
            //System.out.println("found '" + signatureMethods.getLength() + "' signature elements");
            for (int i = signatureMethods.getLength() - 1; i >= 0; i--) {
               Element node = (Element) signatureMethods.item(i);
               String alg = node.getAttribute("algorithm");
               addAllowedSignatureAlgorithm(alg);
               if (logger.isDebugEnabled()) logger.debug(fn + " adding signatureMethod '" + alg + "'");
            }
         } else {
            if (logger.isDebugEnabled()) logger.debug(fn + " No restriction in allowed signature algorithms!");
         }
      } else {
         logger.warn(fn + " couldn't find mandatory element ALLOWEDSIGNATUREMETHODS!");
      }
      elem = (Element) getContextElement().getElementsByTagNameNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "CreationTime").item(0);
      if (elem != null) {
         setCreationTime(elem.getTextContent());
         if (logger.isDebugEnabled()) logger.debug(fn + " setting CREATIONTIME to '" + getCreationTime() + "'");
      } else {
         logger.warn(fn + " couldn't find mandatory element CREATIONTIME!");
      }
      elem = (Element) getContextElement().getElementsByTagNameNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "DigestMethod").item(0);
      if (elem != null) {
         setDigestAlgorithm(elem.getAttributeNode("algorithm").getTextContent());
         if (logger.isDebugEnabled()) logger.debug(fn + " setting DIGESTMETHOD to '" + getDigestAlgorithm() + "'");
      } else {
         logger.warn(fn + " couldn't find mandatory element DIGESTMETHOD!");
      }
   }

   /**
    * Creates CoordinationContext as org.w3c.dom.Element out of the set data
    * @param doc document into which this Element should be integrated
    * @param countingCtx the surrounding countingContext (used for NS-lookups)
    * @return The Element that has been created.
    */
   public Element makeContextElement(Document doc, Element countingCtx) {
      Element context = doc.createElementNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, Constants.ELEMENT_NAME_COORDINATION_CONTEXT);
      context.setPrefix(countingCtx.lookupPrefix(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE));
      {
         Element expires = doc.createElementNS(NS.WS_UTILITY_NAMESPACE, "Expires");
         expires.setPrefix(countingCtx.lookupPrefix(NS.WS_UTILITY_NAMESPACE));
         expires.setTextContent(getExpires());
         context.appendChild(expires);

         Element identifier = doc.createElementNS(NS.WS_UTILITY_NAMESPACE, "Identifier");
         identifier.setPrefix(countingCtx.lookupPrefix(NS.WS_UTILITY_NAMESPACE));
         identifier.setTextContent(getIdentification());
         context.appendChild(identifier);

         Element coordinationType = doc.createElementNS(NS.WS_COORDINATION_NAMESPACE, "CoordinationType");
         coordinationType.setPrefix(countingCtx.lookupPrefix(NS.WS_COORDINATION_NAMESPACE));
         coordinationType.setTextContent(BaseHandler.CONTEXT_COORDINATION_TYPE);
         context.appendChild(coordinationType);

         Element registrationService = doc.createElementNS(NS.WS_COORDINATION_NAMESPACE, "RegistrationService");
         registrationService.setPrefix(countingCtx.lookupPrefix(NS.WS_COORDINATION_NAMESPACE));
         {
            Element address = doc.createElementNS(NS.WS_UTILITY_NAMESPACE, "Address");
            address.setPrefix(countingCtx.lookupPrefix(NS.WS_UTILITY_NAMESPACE));
            address.setTextContent(getRegistrationServiceAddress());
            registrationService.appendChild(address);
         }
         context.appendChild(registrationService);

         Element creationTime = doc.createElementNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "CreationTime");
         creationTime.setPrefix(countingCtx.lookupPrefix(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE));
         creationTime.setTextContent(getCreationTime());
         context.appendChild(creationTime);

         Element communicationProperties = doc.createElementNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "CommunicationProperties");
         communicationProperties.setPrefix(countingCtx.lookupPrefix(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE));
         {
            Element allowedSignatureAlgorithms = doc.createElementNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "AllowedSignatureMethods");
            allowedSignatureAlgorithms.setPrefix(countingCtx.lookupPrefix(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE));
            String[] allowedAlgorithms = getAllowedSignatureAlgorithms();
            Attr restriction = doc.createAttributeNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "restricted");
            if (allowedAlgorithms == null) {
               restriction.setValue("false");
            } else {
               restriction.setValue("true");
               for (int i = 0; i < allowedAlgorithms.length; i++) {
                  String allowedAlgorithm = allowedAlgorithms[i];
                  Element algorithm = doc.createElementNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "SignatureMethod");
                  algorithm.setPrefix(countingCtx.lookupPrefix(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE));
                  Attr method = doc.createAttributeNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "algorithm");
                  method.setValue(allowedAlgorithm);
                  algorithm.setAttributeNodeNS(method);
                  allowedSignatureAlgorithms.appendChild(algorithm);
               }
            }
            allowedSignatureAlgorithms.setAttributeNodeNS(restriction);
            communicationProperties.appendChild(allowedSignatureAlgorithms);

            Element digestMethod = doc.createElementNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "DigestMethod");
            digestMethod.setPrefix(countingCtx.lookupPrefix(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE));
            Attr method = doc.createAttributeNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "algorithm");
            method.setValue(getDigestAlgorithm());
            digestMethod.setAttributeNodeNS(method);
            communicationProperties.appendChild(digestMethod);

            Element timeoutRequestMS = doc.createElementNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "TimeoutDigestAgreementOnRequestPhaseMS");
            timeoutRequestMS.setPrefix(countingCtx.lookupPrefix(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE));
            timeoutRequestMS.setTextContent(getTimeoutDigestAgreementOnRequestPhaseMS());
            communicationProperties.appendChild(timeoutRequestMS);

            Element timeoutResponseMS = doc.createElementNS(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE, "TimeoutDigestAgreementOnResponsePhaseMS");
            timeoutResponseMS.setPrefix(countingCtx.lookupPrefix(NS.ACCESS_CONTROL_PROTOTYP_NAMESPACE));
            timeoutResponseMS.setTextContent(getTimeoutDigestAgreementOnResponsePhaseMS());
            communicationProperties.appendChild(timeoutResponseMS);
         }
         context.appendChild(communicationProperties);
      }
      return context;
   }

   /**
    * @see IFCoordinationContext#getCreationTime()
    */
   public String getCreationTime() {
      return creationTime;
   }

   /**
    * Sets the creation-time of this coordination context
    * @param creationTime
    */
   public void setCreationTime(String creationTime) {
      this.creationTime = creationTime;
   }

   /**
    * Creates a String from this object.
    * @return
    */
   public String toString() {
      StringBuffer result = new StringBuffer("[CoordinationContext::");
      result.append(" identification=").append(getIdentification());
      result.append(" | coordinationType=").append(getCoordinationType());
      result.append(" | creationTime=").append(getCreationTime());
      result.append(" | expires=").append(getExpires());
      result.append(" | registrationServiceAddress=").append(getRegistrationServiceAddress());
      result.append("]");
      return result.toString();
   }

}
