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

import com.sun.identity.saml2.assertion.Advice;
import com.sun.identity.saml2.assertion.Assertion;
import com.sun.identity.saml2.assertion.AssertionFactory;
import com.sun.identity.saml2.assertion.AssertionIDRef;
import com.sun.identity.saml2.assertion.EncryptedAssertion;
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 AdviceImpl implements Advice {
   private static final String ADVICE_ELEMENT = "Advice";
   private static final String ASSERTION_URI_REF_ELEMENT = "AssertionURIRef";
   private static final String ASSERTION_ID_REF_ELEMENT = "AssertionIDRef";
   private static final String ASSERTION_ELEMENT = "Assertion";
   private static final String ENCRYPTED_ASSERTION_ELEMENT = "EncryptedAssertion";
   private List<Assertion> assertions = new ArrayList<>();
   private List<EncryptedAssertion> encryptedAssertions = new ArrayList<>();
   private List<AssertionIDRef> assertionIDRefs = new ArrayList<>();
   private List<String> assertionURIRefs = new ArrayList<>();
   private List<String> additionalInfo = new ArrayList<>();
   private boolean isMutable = true;

   public AdviceImpl() {
   }

   public AdviceImpl(String xml) throws SAML2Exception {
      Document document = XMLUtils.toDOMDocument(xml, SAML2SDKUtils.DEBUG);
      if (document != null) {
         Element rootElement = document.getDocumentElement();
         this.processElement(rootElement);
         this.makeImmutable();
      } else {
         SAML2SDKUtils.DEBUG.error("AdviceImpl.processElement(): invalid XML input");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("errorObtainingElement"));
      }
   }

   public AdviceImpl(Element element) throws SAML2Exception {
      this.processElement(element);
      this.makeImmutable();
   }

   private void processElement(Element element) throws SAML2Exception {
      if (element == null) {
         SAML2SDKUtils.DEBUG.error("AdviceImpl.processElement(): invalid root element");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("invalid_element"));
      }

      String elemName = element.getLocalName();
      if (elemName == null) {
         SAML2SDKUtils.DEBUG.error("AdviceImpl.processElement(): local name missing");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missing_local_name"));
      }

      if (!elemName.equals("Advice")) {
         SAML2SDKUtils.DEBUG.error("AdviceImpl.processElement(): invalid local name " + elemName);
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("invalid_local_name"));
      }

      NodeList nodes = element.getChildNodes();
      int numOfNodes = nodes.getLength();

      for (int nextElem = 0; nextElem < numOfNodes; nextElem++) {
         Node child = nodes.item(nextElem);
         if (child.getNodeType() == 1) {
            String childName = child.getLocalName();
            if (childName != null) {
               String ns = child.getNamespaceURI();
               if ("urn:oasis:names:tc:SAML:2.0:assertion".equals(ns)) {
                  switch (childName) {
                     case "AssertionIDRef":
                        this.assertionIDRefs.add(AssertionFactory.getInstance().createAssertionIDRef((Element)child));
                        break;
                     case "AssertionURIRef":
                        this.assertionURIRefs.add(XMLUtils.getElementValue((Element)child));
                        break;
                     case "Assertion":
                        this.assertions.add(AssertionFactory.getInstance().createAssertion((Element)child));
                        break;
                     case "EncryptedAssertion":
                        this.encryptedAssertions.add(AssertionFactory.getInstance().createEncryptedAssertion((Element)child));
                        break;
                     default:
                        this.additionalInfo.add(XMLUtils.print(child));
                  }
               } else {
                  this.additionalInfo.add(XMLUtils.print(child));
               }
            }
         }
      }
   }

   @Override
   public List<Assertion> getAssertions() {
      return this.assertions;
   }

   @Override
   public void setAssertions(List<Assertion> assertions) throws SAML2Exception {
      if (!this.isMutable) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }

      this.assertions = assertions;
   }

   @Override
   public List<AssertionIDRef> getAssertionIDRefs() {
      return this.assertionIDRefs;
   }

   @Override
   public void setAssertionIDRefs(List<AssertionIDRef> idRefs) throws SAML2Exception {
      if (!this.isMutable) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }

      this.assertionIDRefs = idRefs;
   }

   @Override
   public List<String> getAssertionURIRefs() {
      return this.assertionURIRefs;
   }

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

      this.assertionURIRefs = uriRefs;
   }

   @Override
   public List<EncryptedAssertion> getEncryptedAssertions() {
      return this.encryptedAssertions;
   }

   @Override
   public void setEncryptedAssertions(List<EncryptedAssertion> encryptedAssertions) throws SAML2Exception {
      if (!this.isMutable) {
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("objectImmutable"));
      }

      this.encryptedAssertions = encryptedAssertions;
   }

   @Override
   public List<String> getAdditionalInfo() {
      return this.additionalInfo;
   }

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

      this.additionalInfo = info;
   }

   @Override
   public String toXMLString(boolean includeNSPrefix, boolean declareNS) throws SAML2Exception {
      StringBuilder sb = new StringBuilder(2000);
      String ns = "";
      String appendNS = "";
      if (declareNS) {
         ns = " xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\"";
      }

      if (includeNSPrefix) {
         appendNS = "saml:";
      }

      sb.append("<").append(appendNS).append("Advice").append(ns).append(">\n");
      if (this.assertionIDRefs != null) {
         for (AssertionIDRef assertionIDRef : this.assertionIDRefs) {
            sb.append(assertionIDRef.toXMLString(includeNSPrefix, false));
         }
      }

      if (this.assertionURIRefs != null) {
         for (String str : this.assertionURIRefs) {
            sb.append("<")
               .append(appendNS)
               .append("AssertionURIRef")
               .append(">")
               .append(str)
               .append("</")
               .append(appendNS)
               .append("AssertionURIRef")
               .append(">\n");
         }
      }

      if (this.assertions != null) {
         for (Assertion assertion : this.assertions) {
            sb.append(assertion.toXMLString(includeNSPrefix, false));
         }
      }

      if (this.encryptedAssertions != null) {
         for (EncryptedAssertion ea : this.encryptedAssertions) {
            sb.append(ea.toXMLString(includeNSPrefix, false));
         }
      }

      if (this.additionalInfo != null) {
         for (String str : this.additionalInfo) {
            sb.append(str).append("\n");
         }
      }

      sb.append("</").append(appendNS).append("Advice").append(">");
      return sb.toString();
   }

   @Override
   public final void makeImmutable() {
      if (this.isMutable) {
         if (this.assertions != null) {
            for (Assertion assertion : this.assertions) {
               assertion.makeImmutable();
            }

            this.assertions = Collections.unmodifiableList(this.assertions);
         }

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

         if (this.assertionIDRefs != null) {
            for (AssertionIDRef assertionIDRef : this.assertionIDRefs) {
               assertionIDRef.makeImmutable();
            }

            this.assertionIDRefs = Collections.unmodifiableList(this.assertionIDRefs);
         }

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

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

         this.isMutable = false;
      }
   }

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