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

import com.sun.identity.saml2.assertion.AudienceRestriction;
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 AudienceRestrictionImpl extends ConditionAbstractImpl implements AudienceRestriction {
   private static final String AUDIENCE_RESTRICTION_ELEMENT = "AudienceRestriction";
   private static final String AUDIENCE_ELEMENT = "Audience";
   private List<String> audiences = new ArrayList<>();
   private boolean isMutable = true;

   public AudienceRestrictionImpl() {
   }

   public AudienceRestrictionImpl(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("AudienceRestrictionImpl.processElement(): invalid XML input");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("errorObtainingElement"));
      }
   }

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

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

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

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

      NodeList nodes = element.getChildNodes();
      int numOfNodes = nodes.getLength();
      if (numOfNodes < 1) {
         SAML2SDKUtils.DEBUG.error("AudienceRestrictionImpl.processElement(): AudienceRestriction has no subelements");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missing_subelements"));
      }

      int nextElem = 0;
      boolean hasAudience = false;

      while (nextElem < numOfNodes) {
         Node child = nodes.item(nextElem);
         if (child.getNodeType() == 1) {
            String childName = child.getLocalName();
            if (childName != null) {
               if (!childName.equals("Audience")) {
                  SAML2SDKUtils.DEBUG.error("AudienceRestrictionImpl.processElement(): unexpected subelement " + childName);
                  throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("unexpected_subelement"));
               }

               this.audiences.add(XMLUtils.getElementValue((Element)child));
               hasAudience = true;
            }
         }

         nextElem++;
      }

      if (!hasAudience) {
         SAML2SDKUtils.DEBUG.error("AudienceRestrictionImpl.processElement(): AudienceRestriction has no subelements");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missing_subelements"));
      }
   }

   @Override
   public List<String> getAudience() {
      return this.audiences;
   }

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

      this.audiences = audiences;
   }

   @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("AudienceRestriction").append(ns).append(">\n");
      if (this.audiences == null) {
         SAML2SDKUtils.DEBUG.error("AudienceRestrictionImpl.processElement(): AudienceRestriction has no subelements");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("missing_subelements"));
      }

      for (String au : this.audiences) {
         sb.append("<").append(appendNS).append("Audience").append(">").append(au).append("</").append(appendNS).append("Audience").append(">\n");
      }

      sb.append("</").append(appendNS).append("AudienceRestriction").append(">\n");
      return sb.toString();
   }

   @Override
   public final void makeImmutable() {
      if (this.isMutable) {
         if (this.audiences != null) {
            this.audiences = Collections.unmodifiableList(this.audiences);
         }

         this.isMutable = false;
      }
   }

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