package com.sun.identity.saml2.assertion.impl;

import com.sun.identity.saml2.assertion.AssertionFactory;
import com.sun.identity.saml2.assertion.AuthnContext;
import com.sun.identity.saml2.assertion.AuthnStatement;
import com.sun.identity.saml2.assertion.SubjectLocality;
import com.sun.identity.saml2.common.SAML2Exception;
import com.sun.identity.saml2.common.SAML2SDKUtils;
import com.sun.identity.shared.DateUtils;
import com.sun.identity.shared.xml.XMLUtils;
import java.text.ParseException;
import java.util.Date;
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class AuthnStatementImpl implements AuthnStatement {
   private AuthnContext authnContext;
   private SubjectLocality subjectLocality;
   private Date authnInstant;
   private String sessionIndex;
   private Date sessionNotOnOrAfter;
   private boolean mutable = true;

   private void validateData() throws SAML2Exception {
      if (this.authnContext == null) {
         if (SAML2SDKUtils.DEBUG.messageEnabled()) {
            SAML2SDKUtils.DEBUG.message("AuthnStatementImpl.validateData: missing Element AuthnContext.");
         }

         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingElement"));
      } else if (this.authnInstant == null) {
         if (SAML2SDKUtils.DEBUG.messageEnabled()) {
            SAML2SDKUtils.DEBUG.message("AuthnStatementImpl.validateData: missing attribute AuthnInstant.");
         }

         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingAttribute"));
      }
   }

   private void parseElement(Element element) throws SAML2Exception {
      if (element == null) {
         if (SAML2SDKUtils.DEBUG.messageEnabled()) {
            SAML2SDKUtils.DEBUG.message("AuthnStatementImpl.parseElement: Input is null.");
         }

         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("nullInput"));
      } else if (!SAML2SDKUtils.checkStatement(element, "AuthnStatement")) {
         if (SAML2SDKUtils.DEBUG.messageEnabled()) {
            SAML2SDKUtils.DEBUG.message("AuthnStatementImpl.parseElement: not AuthnStatement.");
         }

         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("wrongInput"));
      } else {
         NamedNodeMap atts = element.getAttributes();
         if (atts != null) {
            Node att = atts.getNamedItem("AuthnInstant");
            if (att != null) {
               try {
                  this.authnInstant = DateUtils.stringToDate(((Attr)att).getValue().trim());
               } catch (ParseException pe) {
                  throw new SAML2Exception(pe.getMessage());
               }
            }

            att = atts.getNamedItem("SessionIndex");
            if (att != null) {
               this.sessionIndex = ((Attr)att).getValue().trim();
            }

            att = atts.getNamedItem("SessionNotOnOrAfter");
            if (att != null) {
               try {
                  this.sessionNotOnOrAfter = DateUtils.stringToDate(((Attr)att).getValue().trim());
               } catch (ParseException pe) {
                  throw new SAML2Exception(pe.getMessage());
               }
            }
         }

         NodeList nl = element.getChildNodes();
         int length = nl.getLength();

         for (int i = 0; i < length; i++) {
            Node child = nl.item(i);
            String childName;
            if ((childName = child.getLocalName()) != null) {
               if (childName.equals("SubjectLocality")) {
                  if (this.subjectLocality != null) {
                     if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                        SAML2SDKUtils.DEBUG.message("AuthnStatementImpl.parseElement: included more than one SubjectLocality.");
                     }

                     throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("moreElement"));
                  }

                  if (this.authnContext != null) {
                     if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                        SAML2SDKUtils.DEBUG.message("AuthnStatementImpl.parseElement: SubjectLocality is out of sequence.");
                     }

                     throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("schemaViolation"));
                  }

                  this.subjectLocality = AssertionFactory.getInstance().createSubjectLocality((Element)child);
               } else {
                  if (!childName.equals("AuthnContext")) {
                     if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                        SAML2SDKUtils.DEBUG.message("AuthnStatementImpl.parseElement: Invalid element:" + childName);
                     }

                     throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("invalidElement"));
                  }

                  if (this.authnContext != null) {
                     if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                        SAML2SDKUtils.DEBUG.message("AuthnStatementImpl.parseElement: included more than one AuthnContext.");
                     }

                     throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("moreElement"));
                  }

                  this.authnContext = AssertionFactory.getInstance().createAuthnContext((Element)child);
               }
            }
         }

         this.validateData();
         this.mutable = false;
      }
   }

   public AuthnStatementImpl() {
   }

   public AuthnStatementImpl(Element element) throws SAML2Exception {
      this.parseElement(element);
   }

   public AuthnStatementImpl(String xmlString) throws SAML2Exception {
      Document doc = XMLUtils.toDOMDocument(xmlString, SAML2SDKUtils.DEBUG);
      if (doc == null) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("errorObtainingElement"));
      }

      this.parseElement(doc.getDocumentElement());
   }

   @Override
   public AuthnContext getAuthnContext() {
      return this.authnContext;
   }

   @Override
   public void setAuthnContext(AuthnContext value) throws SAML2Exception {
      if (!this.mutable) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }

      this.authnContext = value;
   }

   @Override
   public Date getAuthnInstant() {
      return this.authnInstant;
   }

   @Override
   public void setAuthnInstant(Date value) throws SAML2Exception {
      if (!this.mutable) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }

      this.authnInstant = value;
   }

   @Override
   public SubjectLocality getSubjectLocality() {
      return this.subjectLocality;
   }

   @Override
   public void setSubjectLocality(SubjectLocality value) throws SAML2Exception {
      if (!this.mutable) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }

      this.subjectLocality = value;
   }

   @Override
   public String getSessionIndex() {
      return this.sessionIndex;
   }

   @Override
   public void setSessionIndex(String value) throws SAML2Exception {
      if (!this.mutable) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }

      this.sessionIndex = value;
   }

   @Override
   public Date getSessionNotOnOrAfter() {
      return this.sessionNotOnOrAfter;
   }

   @Override
   public void setSessionNotOnOrAfter(Date value) throws SAML2Exception {
      if (!this.mutable) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }

      this.sessionNotOnOrAfter = value;
   }

   @Override
   public final void makeImmutable() {
      if (this.mutable) {
         if (this.subjectLocality != null) {
            this.subjectLocality.makeImmutable();
         }

         if (this.authnContext != null) {
            this.authnContext.makeImmutable();
         }

         this.mutable = false;
      }
   }

   @Override
   public boolean isMutable() {
      return this.mutable;
   }

   @Override
   public String toXMLString() throws SAML2Exception {
      return this.toXMLString(true, false);
   }

   @Override
   public String toXMLString(boolean includeNS, boolean declareNS) throws SAML2Exception {
      this.validateData();
      StringBuilder result = new StringBuilder(1000);
      String prefix = "";
      String uri = "";
      if (includeNS) {
         prefix = "saml:";
      }

      if (declareNS) {
         uri = " xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\"";
      }

      result.append("<")
         .append(prefix)
         .append("AuthnStatement")
         .append(uri)
         .append(" AuthnInstant=\"")
         .append(DateUtils.toUTCDateFormat(this.authnInstant))
         .append("\"");
      if (this.sessionIndex != null && !this.sessionIndex.trim().isEmpty()) {
         result.append(" SessionIndex=\"").append(this.sessionIndex).append("\"");
      }

      if (this.sessionNotOnOrAfter != null) {
         result.append(" SessionNotOnOrAfter=\"").append(DateUtils.toUTCDateFormat(this.sessionNotOnOrAfter)).append("\"");
      }

      result.append(">");
      if (this.subjectLocality != null) {
         result.append(this.subjectLocality.toXMLString(includeNS, declareNS));
      }

      result.append(this.authnContext.toXMLString(includeNS, declareNS));
      result.append("</").append(prefix).append("AuthnStatement>");
      return result.toString();
   }
}
