/**
 * 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.
 */

/*
 * XSEC
 *
 * DSIGXPathFilterExpr := Class that holds an XPath Filter expression
 *
 * Author(s): Berin Lautenbach
 *
 * $Id: DSIGXPathFilterExpr.cpp 1833341 2018-06-11 16:25:41Z scantor $
 *
 */

#include <xsec/dsig/DSIGSignature.hpp>
#include <xsec/dsig/DSIGXPathFilterExpr.hpp>
#include <xsec/framework/XSECDefs.hpp>
#include <xsec/framework/XSECException.hpp>
#include <xsec/framework/XSECEnv.hpp>

#include "../utils/XSECDOMUtils.hpp"

#include <xercesc/dom/DOMNode.hpp>
#include <xercesc/dom/DOMNamedNodeMap.hpp>
#include <xercesc/util/XMLUniDefs.hpp>

XERCES_CPP_NAMESPACE_USE

XMLCh filterStr[] = {
    chLatin_F,
    chLatin_i,
    chLatin_l,
    chLatin_t,
    chLatin_e,
    chLatin_r,
    chNull
};


// --------------------------------------------------------------------------------
//           Constructors and Destructors
// --------------------------------------------------------------------------------

DSIGXPathFilterExpr::DSIGXPathFilterExpr(const XSECEnv* env, DOMNode* node) :
mp_env(env),
mp_xpathFilterNode(node),
mp_exprTextNode(NULL),
mp_NSMap(NULL),
m_filterType(FILTER_UNION),
m_loaded(false) {

}

DSIGXPathFilterExpr::DSIGXPathFilterExpr(const XSECEnv* env) :
mp_env(env),
mp_xpathFilterNode(NULL),
mp_exprTextNode(NULL),
mp_NSMap(NULL),
m_filterType(FILTER_UNION),
m_loaded(false)  {

}


DSIGXPathFilterExpr::~DSIGXPathFilterExpr() {}

// --------------------------------------------------------------------------------
//           Load existing DOM structure
// --------------------------------------------------------------------------------

void DSIGXPathFilterExpr::load() {

    // Find the XPath expression

    if (mp_xpathFilterNode == NULL ||
        !strEquals(getXPFLocalName(mp_xpathFilterNode), "XPath")) {

        throw XSECException(XSECException::ExpectedDSIGChildNotFound,
            "Expected <XPath> as first node in DSIGXPathFilterExpr::load");
    }


    // Check for attributes - in particular any namespaces

    mp_NSMap = mp_xpathFilterNode->getAttributes();

    // Find the filter type
    DOMNode * a;
    if (mp_NSMap == NULL ||
        ((a = mp_NSMap->getNamedItem(filterStr)) == NULL)) {

        throw XSECException(XSECException::ExpectedDSIGChildNotFound,
            "Expected Filter attribute of <XPath> node in in DSIGXPathFilterExpr::load");
    }

    const XMLCh * f = a->getNodeValue();
    if (strEquals(f, "intersect")) {
        m_filterType = FILTER_INTERSECT;
    }
    else if (strEquals(f, "union")) {
        m_filterType = FILTER_UNION;
    }
    else if (strEquals(f, "subtract")) {
        m_filterType = FILTER_SUBTRACT;
    }
    else {
        throw XSECException(XSECException::ExpectedDSIGChildNotFound,
                "DSIGXPathFilterExpr::load Expected on of intersect, union or subtract as filter type");
    }

    // Find the text node
    mp_exprTextNode = findFirstChildOfType(mp_xpathFilterNode, DOMNode::TEXT_NODE);

    if (mp_exprTextNode == NULL) {
        throw XSECException(XSECException::ExpectedDSIGChildNotFound,
            "Expected Text Node in beneath <XPath> in DSIGXPathFilterExpr::load");
    }

    // Gather the text - hold it in UTF16 format
    gatherChildrenText(mp_xpathFilterNode, m_expr);

    m_loaded = true;
}

// --------------------------------------------------------------------------------
//           Create a new filter
// --------------------------------------------------------------------------------

DOMElement* DSIGXPathFilterExpr::setFilter(DSIGXPathFilterExpr::XPathFilterType filterType,
                        const XMLCh * filterExpr) {

    if (m_loaded == true) {
        throw XSECException(XSECException::XPathFilterError,
            "DSIGXPathFilterExpr::setFilter - called when already loaded");
    }

    safeBuffer str;
    const XMLCh * prefix;
    DOMDocument *doc = mp_env->getParentDocument();
    DOMElement * xe;

    // Create the XPath element
    prefix = mp_env->getXPFNSPrefix();
    makeQName(str, prefix, "XPath");
    xe = doc->createElementNS(DSIGConstants::s_unicodeStrURIXPF, str.rawXMLChBuffer());
    mp_xpathFilterNode = xe;

    // Put in correct namespace
    prefix = mp_env->getXPFNSPrefix();

    // Set the namespace attribute
    if (prefix[0] == '\0') {
        str.sbTranscodeIn("xmlns");
    }
    else {
        str.sbTranscodeIn("xmlns:");
        str.sbXMLChCat(prefix);
    }

    xe->setAttributeNS(DSIGConstants::s_unicodeStrURIXMLNS,
                            str.rawXMLChBuffer(),
                            DSIGConstants::s_unicodeStrURIXPF);

    // Set the filter type
    m_filterType = filterType;

    switch (filterType) {

    case FILTER_INTERSECT :
        xe->setAttributeNS(NULL,MAKE_UNICODE_STRING("Filter"), MAKE_UNICODE_STRING("intersect"));
        break;

    case FILTER_SUBTRACT :
        xe->setAttributeNS(NULL,MAKE_UNICODE_STRING("Filter"), MAKE_UNICODE_STRING("subtract"));
        break;

    case FILTER_UNION :
        xe->setAttributeNS(NULL,MAKE_UNICODE_STRING("Filter"), MAKE_UNICODE_STRING("union"));
        break;

    default :
        mp_xpathFilterNode->release();
        throw XSECException(XSECException::XPathFilterError,
            "DSIGXPathFilterExpr::appendFilter - Unexpected Filter Type");
    }

    // Now add the actual filter

    mp_exprTextNode = doc->createTextNode(filterExpr);
    mp_xpathFilterNode->appendChild(mp_exprTextNode);

    mp_NSMap = mp_xpathFilterNode->getAttributes();

    m_expr.sbXMLChIn(filterExpr);
    m_loaded = true;
    return xe;
}


// --------------------------------------------------------------------------------
//           Find the type
// --------------------------------------------------------------------------------

DSIGXPathFilterExpr::XPathFilterType DSIGXPathFilterExpr::getFilterType() const {

    if (m_loaded == false) {
        throw XSECException(XSECException::LoadEmptyXPathFilter,
            "DSIGXPathFilterExpr::Element node loaded");
    }

    return m_filterType;
}

// --------------------------------------------------------------------------------
//           Set and clear namespaces
// --------------------------------------------------------------------------------

void DSIGXPathFilterExpr::setNamespace(const XMLCh* prefix, const XMLCh* value) {

    if (mp_xpathFilterNode == NULL) {
        throw XSECException(XSECException::XPathFilterError,
            "DSIGXPathFilterExpr::setNamespace - load not called");
    }

    safeBuffer str;

    str.sbTranscodeIn("xmlns:");
    str.sbXMLChCat(prefix);

    DOMElement* x = static_cast <DOMElement *> (mp_xpathFilterNode);

    x->setAttributeNS(DSIGConstants::s_unicodeStrURIXMLNS,
        str.rawXMLChBuffer(),
        value);

    mp_NSMap = mp_xpathFilterNode->getAttributes();
}

void DSIGXPathFilterExpr::deleteNamespace(const XMLCh* prefix) {

    if (mp_xpathFilterNode == NULL) {
        throw XSECException(XSECException::XPathFilterError,
            "DSIGXPathFilterExpr::deleteNamespace - load not called");
    }

    DOMElement* x = static_cast <DOMElement *> (mp_xpathFilterNode);

    x->removeAttributeNS(DSIGConstants::s_unicodeStrURIXMLNS, prefix);
}
