/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.wss4j.stax.impl.transformer;

import org.apache.wss4j.common.ext.Attachment;
import org.apache.wss4j.common.util.CRLFOutputStream;
import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.stax.ext.XMLSecurityConstants;
import org.apache.xml.security.stax.ext.stax.XMLSecEvent;
import org.apache.xml.security.stax.impl.transformer.TransformIdentity;
import org.apache.xml.security.stax.impl.transformer.canonicalizer.Canonicalizer20010315_ExclOmitCommentsTransformer;

import javax.xml.stream.XMLStreamException;

import java.io.InputStream;
import java.util.Map;

public class AttachmentContentSignatureTransform extends TransformIdentity {

    public static final String ATTACHMENT = "attachment";

    private Attachment attachment;

    @Override
    public XMLSecurityConstants.TransformMethod getPreferredTransformMethod(XMLSecurityConstants.TransformMethod forInput) {
        switch (forInput) {
            case XMLSecEvent:
                return XMLSecurityConstants.TransformMethod.InputStream;
            case InputStream:
                return XMLSecurityConstants.TransformMethod.InputStream;
            default:
                throw new IllegalArgumentException("Unsupported class " + forInput.name());
        }
    }

    @Override
    public void setProperties(Map<String, Object> properties) throws XMLSecurityException {
        this.attachment = (Attachment) properties.get(ATTACHMENT);
    }

    protected Attachment getAttachment() {
        return attachment;
    }

    /*
             * http://docs.oasis-open.org/wss-m/wss/v1.1.1/os/wss-SwAProfile-v1.1.1-os.html
             * 5.2 Referencing Attachments
             * This profile assumes, since it is not defined in RFC 2396 Section 4.2, that
             * all cid: references are not same-document references and that therefore, under
             * XMLDSIG, dereferencing a cid: URI always yields an octet stream as input to the
             * transform chain [RFC2396], [XMLDSIG].
             */
    @Override
    public void transform(XMLSecEvent xmlSecEvent) throws XMLStreamException {
        throw new UnsupportedOperationException("transform(XMLSecEvent) not allowed");
    }

    @Override
    public void transform(InputStream inputStream) throws XMLStreamException {
        String mimeType = getAttachment().getMimeType();

        if (mimeType != null
            && (mimeType.matches("(?i)(text/xml).*")
                || mimeType.matches("(?i)(application/xml).*")
                || mimeType.matches("(?i)(application|image)/.*\\+xml.*"))) {
            /* 5.4.2:
             * Content of an XML Content-Type MUST be XML canonicalized using
             * Exclusive XML Canonicalization without comments, as specified by
             * the URI http://www.w3.org/2001/10/xml-exc-c14n# [Excl-Canon].
             * The reason for requiring Exclusive Canonicalization is that many
             * implementations will support Exclusive Canonicalization for other
             * XML Signature purposes, since this form of canonicalization
             * supports context changes. The InclusiveNamespace PrefixList
             * attribute SHOULD be empty or not present.
             */
            Canonicalizer20010315_ExclOmitCommentsTransformer canon =
                    new Canonicalizer20010315_ExclOmitCommentsTransformer();
            try {
                canon.setOutputStream(getOutputStream());
            } catch (XMLSecurityException e) {
                throw new XMLStreamException(e);
            }
            canon.transform(inputStream);

        } else if (mimeType != null && mimeType.matches("(?i)(text/).*")) {
            CRLFOutputStream crlfOutputStream = new CRLFOutputStream(getOutputStream());
            try {
                setOutputStream(crlfOutputStream);
            } catch (XMLSecurityException e) {
                throw new XMLStreamException(e);
            }
            super.transform(inputStream);
        } else {
            super.transform(inputStream);
        }
    }
}
