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

import com.sun.identity.saml2.assertion.AssertionFactory;
import com.sun.identity.saml2.assertion.Attribute;
import com.sun.identity.saml2.assertion.AttributeStatement;
import com.sun.identity.saml2.assertion.EncryptedAttribute;
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 AttributeStatementImpl implements AttributeStatement {
   private List<Attribute> attrs;
   private List<EncryptedAttribute> encAttrs;
   private boolean mutable = true;

   private void validateData() throws SAML2Exception {
      if ((this.attrs == null || this.attrs.isEmpty()) && (this.encAttrs == null || this.encAttrs.isEmpty())) {
         if (SAML2SDKUtils.DEBUG.messageEnabled()) {
            SAML2SDKUtils.DEBUG.message("AttributeStatementImpl.validateData: missing Attribute or EncryptedAttribute element.");
         }

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

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

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

         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("wrongInput"));
      } else {
         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("Attribute")) {
                  Attribute attr = AssertionFactory.getInstance().createAttribute((Element)child);
                  if (this.attrs == null) {
                     this.attrs = new ArrayList<>();
                  }

                  this.attrs.add(attr);
               } else {
                  if (!childName.equals("EncryptedAttribute")) {
                     if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                        SAML2SDKUtils.DEBUG.message("AttributeStatementImpl.parse Element: Invalid element:" + childName);
                     }

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

                  EncryptedAttribute encAttr = AssertionFactory.getInstance().createEncryptedAttribute((Element)child);
                  if (this.encAttrs == null) {
                     this.encAttrs = new ArrayList<>();
                  }

                  this.encAttrs.add(encAttr);
               }
            }
         }

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

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

         this.mutable = false;
      }
   }

   public AttributeStatementImpl() {
   }

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

   public AttributeStatementImpl(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 List<Attribute> getAttribute() {
      return this.attrs;
   }

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

      this.attrs = value;
   }

   @Override
   public List<EncryptedAttribute> getEncryptedAttribute() {
      return this.encAttrs;
   }

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

      this.encAttrs = value;
   }

   @Override
   public final void makeImmutable() {
      if (this.mutable) {
         if (this.attrs != null) {
            for (Attribute attr : this.attrs) {
               attr.makeImmutable();
            }

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

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

         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("AttributeStatement").append(uri).append(">");
      if (this.attrs != null) {
         for (Attribute attr : this.attrs) {
            result.append(attr.toXMLString(includeNS, declareNS));
         }
      }

      if (this.encAttrs != null) {
         for (EncryptedAttribute encAttr : this.encAttrs) {
            result.append(encAttr.toXMLString(includeNS, declareNS));
         }
      }

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