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

import com.sun.identity.saml2.assertion.SubjectConfirmationData;
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.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
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 SubjectConfirmationDataImpl implements SubjectConfirmationData {
   private static final String ELEMENT_NAME = "SubjectConfirmationData";
   private boolean mutable;
   private Date notOnOrAfter;
   private String inResponseTo;
   private List<Object> content;
   private String recipient;
   private Date notBefore;
   private String address;
   private String contentType;

   public SubjectConfirmationDataImpl() {
      this.mutable = true;
   }

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

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

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

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

         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("nullInput"));
      } else {
         String tag = element.getLocalName();
         if (tag != null && tag.equals("SubjectConfirmationData")) {
            NamedNodeMap attrs = element.getAttributes();
            this.parseAttributes(attrs);
            this.parseContent(element);
         } else {
            if (SAML2SDKUtils.DEBUG.messageEnabled()) {
               SAML2SDKUtils.DEBUG.message("parseElement: not SubjectConfirmationData.");
            }

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

   private void parseContent(Element element) {
      if (element != null) {
         NodeList nList = element.getChildNodes();
         if (nList != null && nList.getLength() > 0) {
            for (int i = 0; i < nList.getLength(); i++) {
               Node childNode = nList.item(i);
               if (childNode.getLocalName() != null) {
                  this.getContent().add(childNode);
               }
            }
         }

         if (this.content == null) {
            this.content = new ArrayList<>();
            this.content.add(XMLUtils.getElementValue(element));
         }
      }
   }

   private void parseAttributes(NamedNodeMap attrs) throws SAML2Exception {
      if (attrs != null) {
         try {
            int length = attrs.getLength();

            for (int i = 0; i < length; i++) {
               Attr attr = (Attr)attrs.item(i);
               String attrName = attr.getName().trim();
               String attrValue = attr.getValue().trim();
               switch (attrName) {
                  case "Address":
                     this.address = attrValue;
                     break;
                  case "InResponseTo":
                     this.inResponseTo = attrValue;
                     break;
                  case "NotBefore":
                     this.notBefore = DateUtils.stringToDate(attrValue);
                     break;
                  case "NotOnOrAfter":
                     this.notOnOrAfter = DateUtils.stringToDate(attrValue);
                     break;
                  case "Recipient":
                     this.recipient = attrValue;
                     break;
                  case "xsi:type":
                     this.contentType = attrValue;
               }
            }
         } catch (ParseException e) {
            if (SAML2SDKUtils.DEBUG.messageEnabled()) {
               SAML2SDKUtils.DEBUG.message("parseAttributes: " + e);
            }

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

   @Override
   public Date getNotOnOrAfter() {
      return this.notOnOrAfter;
   }

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

      this.notOnOrAfter = value;
   }

   @Override
   public String getInResponseTo() {
      return this.inResponseTo;
   }

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

      this.inResponseTo = value;
   }

   @Override
   public List<Object> getContent() {
      if (this.content == null) {
         this.content = new ArrayList<>();
      }

      return this.content;
   }

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

      this.content = value;
   }

   @Override
   public String getRecipient() {
      return this.recipient;
   }

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

      this.recipient = value;
   }

   @Override
   public Date getNotBefore() {
      return this.notBefore;
   }

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

      this.notBefore = value;
   }

   @Override
   public String getAddress() {
      return this.address;
   }

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

      this.address = value;
   }

   @Override
   public String getContentType() {
      return this.contentType;
   }

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

      this.contentType = attribute;
   }

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

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

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

      xml.append("<").append(appendNS).append("SubjectConfirmationData");
      xml.append(ns).append(" ");
      xml.append(this.getElementValue(includeNSPrefix, declareNS));
      xml.append("</").append(appendNS).append("SubjectConfirmationData").append(">");
      return xml.toString();
   }

   protected String getElementValue(boolean includeNSPrefix, boolean declareNS) throws SAML2Exception {
      StringBuilder xml = new StringBuilder();
      if (this.notOnOrAfter != null) {
         xml.append("NotOnOrAfter=\"");
         xml.append(DateUtils.toUTCDateFormat(this.notOnOrAfter));
         xml.append("\" ");
      }

      if (this.inResponseTo != null) {
         xml.append("InResponseTo=\"");
         xml.append(this.inResponseTo);
         xml.append("\" ");
      }

      if (this.recipient != null) {
         xml.append("Recipient=\"");
         xml.append(this.recipient);
         xml.append("\" ");
      }

      if (this.notBefore != null) {
         xml.append("NotBefore=\"");
         xml.append(DateUtils.toUTCDateFormat(this.notBefore));
         xml.append("\" ");
      }

      if (this.address != null) {
         xml.append("Address=\"");
         xml.append(this.address);
         xml.append("\" ");
      }

      if (this.contentType != null) {
         xml.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"").append(" ").append("xsi:type=\"").append(this.contentType).append("\" ");
      }

      xml.append(">");
      if (!this.getContent().isEmpty()) {
         for (Object obj : this.getContent()) {
            if (obj instanceof Element) {
               xml.append(XMLUtils.print((Element)obj)).append(" ");
            } else if (obj instanceof String) {
               xml.append((String)obj);
            }
         }
      }

      return xml.toString();
   }

   @Override
   public void makeImmutable() {
      if (this.mutable) {
         this.mutable = false;
         if (this.content != null && !this.content.isEmpty()) {
            this.content = Collections.unmodifiableList(this.content);
         }
      }
   }

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