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

import com.sun.identity.saml2.assertion.AuthnContext;
import com.sun.identity.saml2.common.SAML2Exception;
import com.sun.identity.saml2.common.SAML2SDKUtils;
import com.sun.identity.shared.xml.XMLUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class AuthnContextImpl implements AuthnContext {
   private String authnContextClassRef;
   private String authnContextDecl;
   private String authnContextDeclRef;
   private List<String> authenticatingAuthority;
   private boolean mutable = true;

   private void validateData() throws SAML2Exception {
      if ((this.authnContextClassRef == null || this.authnContextClassRef.trim().isEmpty())
         && (this.authnContextDecl == null || this.authnContextDecl.trim().isEmpty())
         && (this.authnContextDeclRef == null || this.authnContextDeclRef.trim().isEmpty())) {
         if (SAML2SDKUtils.DEBUG.messageEnabled()) {
            SAML2SDKUtils.DEBUG.message("AuthnContextImpl.validateData: missing AuthnContextClassRef or AuthnContextDecl(Ref).");
         }

         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingElement"));
      } else if (this.authnContextDecl != null
         && !this.authnContextDecl.trim().isEmpty()
         && this.authnContextDeclRef != null
         && !this.authnContextDeclRef.trim().isEmpty()) {
         if (SAML2SDKUtils.DEBUG.messageEnabled()) {
            SAML2SDKUtils.DEBUG.message("AuthnContextImpl.validateData: AuthnContextDecl and AuthnContextDeclRef cannot present the same time.");
         }

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

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

         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("nullInput"));
      } else {
         String tag = element.getLocalName();
         if (tag != null && tag.equals("AuthnContext")) {
            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) {
                  switch (childName) {
                     case "AuthnContextClassRef":
                        if (this.authnContextClassRef != null) {
                           if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                              SAML2SDKUtils.DEBUG.message("AuthnContextImpl.parseElement: included more than one AuthnContextClassRef.");
                           }

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

                        if (this.authnContextDecl == null && this.authnContextDeclRef == null && this.authenticatingAuthority == null) {
                           this.authnContextClassRef = XMLUtils.getElementValue((Element)child);
                           if (this.authnContextClassRef != null && !this.authnContextClassRef.trim().isEmpty()) {
                              break;
                           }

                           if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                              SAML2SDKUtils.DEBUG.message("AuthnContextImpl.parseElement: value for AuthnContextClassRef is empty.");
                           }

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

                        if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                           SAML2SDKUtils.DEBUG.message("AuthnContextImpl.parseElement: AuthnContextClassRef should be the first child element.");
                        }

                        throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("schemaViolation"));
                     case "AuthnContextDecl":
                        if (this.authnContextDecl != null) {
                           if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                              SAML2SDKUtils.DEBUG.message("AuthnContextImpl.parseElement: included more than one AuthnContextDecl.");
                           }

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

                        this.authnContextDecl = XMLUtils.print(child);
                        if (this.authnContextDecl != null && !this.authnContextDecl.trim().isEmpty()) {
                           break;
                        }

                        if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                           SAML2SDKUtils.DEBUG.message("AuthnContextImpl.parseElement: value for AuthnContextDecl is empty.");
                        }

                        throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingElementValue"));
                     case "AuthnContextDeclRef":
                        if (this.authnContextDeclRef != null) {
                           if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                              SAML2SDKUtils.DEBUG.message("AuthnContextImpl.parseElement: included more than one AuthnContextDeclRef.");
                           }

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

                        this.authnContextDeclRef = XMLUtils.getElementValue((Element)child);
                        if (this.authnContextDeclRef != null && !this.authnContextDeclRef.trim().isEmpty()) {
                           break;
                        }

                        if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                           SAML2SDKUtils.DEBUG.message("AuthnContextImpl.parseElement: value for AuthnContextDeclRef is empty.");
                        }

                        throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingElementValue"));
                     case "AuthenticatingAuthority":
                        String authority = XMLUtils.getElementValue((Element)child);
                        if (authority != null && !authority.trim().isEmpty()) {
                           if (this.authenticatingAuthority == null) {
                              this.authenticatingAuthority = new ArrayList<>();
                           }

                           this.authenticatingAuthority.add(authority);
                           break;
                        }

                        if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                           SAML2SDKUtils.DEBUG.message("AuthnContextImpl.parseElement: value for AuthenticatingAuthority is empty.");
                        }

                        throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missingElementValue"));
                     default:
                        if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                           SAML2SDKUtils.DEBUG.message("AuthnContextImpl.parseElement: Invalid element:" + childName);
                        }

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

            if (this.authenticatingAuthority != null) {
               this.authenticatingAuthority = Collections.unmodifiableList(this.authenticatingAuthority);
            }

            this.mutable = false;
         } else {
            if (SAML2SDKUtils.DEBUG.messageEnabled()) {
               SAML2SDKUtils.DEBUG.message("AuthnContextImpl.parseElement: not AuthnContext.");
            }

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

   public AuthnContextImpl() {
   }

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

   public AuthnContextImpl(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 final void makeImmutable() {
      if (this.mutable && this.authenticatingAuthority != null) {
         this.authenticatingAuthority = Collections.unmodifiableList(this.authenticatingAuthority);
      }

      this.mutable = false;
   }

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

   @Override
   public String getAuthnContextClassRef() {
      return this.authnContextClassRef;
   }

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

      this.authnContextClassRef = value;
   }

   @Override
   public String getAuthnContextDeclRef() {
      return this.authnContextDeclRef;
   }

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

      this.authnContextDeclRef = value;
   }

   @Override
   public String getAuthnContextDecl() {
      return this.authnContextDecl;
   }

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

      this.authnContextDecl = value;
   }

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

      this.authenticatingAuthority = value;
   }

   @Override
   public List<String> getAuthenticatingAuthority() {
      return this.authenticatingAuthority;
   }

   @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("AuthnContext").append(uri).append(">");
      if (this.authnContextClassRef != null && !this.authnContextClassRef.trim().isEmpty()) {
         result.append("<")
            .append(prefix)
            .append("AuthnContextClassRef")
            .append(uri)
            .append(">")
            .append(this.authnContextClassRef)
            .append("</")
            .append(prefix)
            .append("AuthnContextClassRef>");
      }

      if (this.authnContextDecl != null && !this.authnContextDecl.trim().isEmpty()) {
         result.append(this.authnContextDecl);
      } else if (this.authnContextDeclRef != null && !this.authnContextDeclRef.trim().isEmpty()) {
         result.append("<")
            .append(prefix)
            .append("AuthnContextDeclRef")
            .append(uri)
            .append(">")
            .append(this.authnContextDeclRef)
            .append("</")
            .append(prefix)
            .append("AuthnContextDeclRef>");
      }

      if (this.authenticatingAuthority != null) {
         for (String authority : this.authenticatingAuthority) {
            if (authority != null && !authority.trim().isEmpty()) {
               result.append("<")
                  .append(prefix)
                  .append("AuthenticatingAuthority")
                  .append(uri)
                  .append(">")
                  .append(authority)
                  .append("</")
                  .append(prefix)
                  .append("AuthenticatingAuthority>");
            }
         }
      }

      result.append("</").append(prefix).append("AuthnContext>");
      return result.toString();
   }
}
