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

import com.sun.identity.saml2.common.SAML2Exception;
import com.sun.identity.saml2.common.SAML2SDKUtils;
import com.sun.identity.saml2.protocol.Artifact;
import com.sun.identity.shared.encode.Base64;
import com.sun.identity.shared.xml.XMLUtils;
import java.nio.charset.StandardCharsets;
import org.w3c.dom.Element;

public class ArtifactImpl implements Artifact {
   private static final int ARTIFACT_4_LENGTH = 44;
   private static final byte ARTIFACT_4_TYPE_CODE_BYTE1 = 0;
   private static final byte ARTIFACT_4_TYPE_CODE_BYTE2 = 4;
   private static final byte[] ARTIFACT_4_TYPE_CODE = new byte[]{0, 4};
   private String artifact;
   private String messageHandle;
   private String sourceID;
   private byte[] typeCode;
   private int endpointIndex = -1;

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

         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("nullInput"));
      } else {
         String tag = element.getLocalName();
         if (tag != null && tag.equals("Artifact")) {
            String value = XMLUtils.getElementValue(element);
            this.parseArtifactValue(value);
         } else {
            if (SAML2SDKUtils.DEBUG.messageEnabled()) {
               SAML2SDKUtils.DEBUG.message("ArtifactImpl.parseElement: not Artifact.");
            }

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

   private void parseArtifactValue(String value) throws SAML2Exception {
      String method = "ArtifactImpl.parseArtifactValue: ";
      if (value != null && !value.isEmpty()) {
         byte[] raw;
         try {
            raw = Base64.decode(value);
         } catch (Exception e) {
            if (SAML2SDKUtils.DEBUG.messageEnabled()) {
               SAML2SDKUtils.DEBUG.message("ArtifactImpl.parseElement: exception occurred while decoding artifact:", e);
            }

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

         if (raw.length != 44) {
            if (SAML2SDKUtils.DEBUG.messageEnabled()) {
               SAML2SDKUtils.DEBUG.message("ArtifactImpl.parseElement: the length is not 44:" + raw.length);
            }

            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("wrongInput"));
         } else if (raw[0] == 0 && raw[1] == 4) {
            this.typeCode = ARTIFACT_4_TYPE_CODE;
            this.artifact = value;
            byte[] endpointIndexB = new byte[2];
            byte[] sBytes = new byte[20];
            byte[] mBytes = new byte[20];
            System.arraycopy(raw, 2, endpointIndexB, 0, 2);
            System.arraycopy(raw, 4, sBytes, 0, 20);
            System.arraycopy(raw, 24, mBytes, 0, 20);

            try {
               this.sourceID = SAML2SDKUtils.byteArrayToString(sBytes);
               this.messageHandle = SAML2SDKUtils.byteArrayToString(mBytes);
            } catch (Exception e) {
               SAML2SDKUtils.DEBUG.error("ArtifactImpl.parseElement: encoding exception: ", e);
               this.sourceID = new String(sBytes, StandardCharsets.UTF_8);
               this.messageHandle = new String(mBytes, StandardCharsets.UTF_8);
            }

            this.endpointIndex = SAML2SDKUtils.twoBytesToInt(endpointIndexB);
         } else {
            if (SAML2SDKUtils.DEBUG.messageEnabled()) {
               SAML2SDKUtils.DEBUG.message("ArtifactImpl.parseElement: wrong typecode.");
            }

            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("wrongInput"));
         }
      } else {
         if (SAML2SDKUtils.DEBUG.messageEnabled()) {
            SAML2SDKUtils.DEBUG.message(method + "empty input.");
         }

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

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

   public ArtifactImpl(String encodedArtifactValue) throws SAML2Exception {
      this.parseArtifactValue(encodedArtifactValue);
   }

   public ArtifactImpl(byte[] typecode, int endpointindex, String sourceid, String messagehandle) throws SAML2Exception {
      if (typecode == null || typecode.length == 2 && typecode[0] == 0 && typecode[1] == 4) {
         this.typeCode = ARTIFACT_4_TYPE_CODE;
         if (sourceid == null) {
            SAML2SDKUtils.DEBUG.error("ArtifactImpl: null sourceID.");
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("nullInput"));
         }

         this.sourceID = sourceid;
         if (messagehandle == null) {
            this.messageHandle = SAML2SDKUtils.generateMessageHandle();
         } else {
            this.messageHandle = messagehandle;
         }

         byte[] idBytes;
         byte[] handleBytes;
         try {
            idBytes = SAML2SDKUtils.stringToByteArray(this.sourceID);
            handleBytes = SAML2SDKUtils.stringToByteArray(this.messageHandle);
         } catch (Exception e) {
            SAML2SDKUtils.DEBUG.error("ArtifactImpl: encoding exception: ", e);
            idBytes = this.sourceID.getBytes(StandardCharsets.UTF_8);
            handleBytes = this.messageHandle.getBytes(StandardCharsets.UTF_8);
         }

         if (idBytes.length == 20 && handleBytes.length == 20) {
            byte[] indexArray = SAML2SDKUtils.intToTwoBytes(endpointindex);
            this.endpointIndex = endpointindex;
            byte[] raw = new byte[44];
            raw[0] = 0;
            raw[1] = 4;
            raw[2] = indexArray[0];
            raw[3] = indexArray[1];

            for (int i = 0; i < 20; i++) {
               raw[4 + i] = idBytes[i];
               raw[24 + i] = handleBytes[i];
            }

            try {
               this.artifact = Base64.encode(raw).trim();
            } catch (Exception e) {
               if (SAML2SDKUtils.DEBUG.messageEnabled()) {
                  SAML2SDKUtils.DEBUG.message("ArtifactImpl: exception encode input:", e);
               }

               throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("errorCreateArtifact"));
            }
         } else {
            SAML2SDKUtils.DEBUG.error("ArtifactImpl: wrong input length.");
            throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("wrongInput"));
         }
      } else {
         SAML2SDKUtils.DEBUG.error("ArtifactImpl: wrong typecode.");
         throw new SAML2Exception(SAML2SDKUtils.BUNDLE.getString("wrongInput"));
      }
   }

   @Override
   public String getArtifactValue() {
      return this.artifact;
   }

   @Override
   public String getSourceID() {
      return this.sourceID;
   }

   @Override
   public String getMessageHandle() {
      return this.messageHandle;
   }

   @Override
   public byte[] getTypeCode() {
      return this.typeCode;
   }

   @Override
   public int getEndpointIndex() {
      return this.endpointIndex;
   }

   @Override
   public String toXMLString(boolean includeNS, boolean declareNS) throws SAML2Exception {
      if (this.artifact != null && !this.artifact.trim().isEmpty()) {
         StringBuilder xml = new StringBuilder(200);
         String prefix = "";
         if (includeNS) {
            prefix = "samlp:";
         }

         String uri = "";
         if (declareNS) {
            uri = " xmlns:samlp=\"urn:oasis:names:tc:SAML:2.0:protocol\"";
         }

         xml.append("<").append(prefix).append("Artifact").append(uri).append(">").append(this.artifact).append("</").append(prefix).append("Artifact>");
         return xml.toString();
      } else {
         if (SAML2SDKUtils.DEBUG.messageEnabled()) {
            SAML2SDKUtils.DEBUG.message("ArtifactImpl.toXMLString: artifact value is empty");
         }

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