/*
 *
 *          Copyright (c) 2013,2019-2020  AT&T Knowledge Ventures
 *                     SPDX-License-Identifier: MIT
 */
package com.att.research.xacml.std.jaxp;

import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import javax.xml.XMLConstants;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import oasis.names.tc.xacml._3_0.core.schema.wd_17.ResponseType;
import oasis.names.tc.xacml._3_0.core.schema.wd_17.ResultType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.att.research.xacml.std.StdMutableResponse;
import com.att.research.xacml.util.MainUtils;

/**
 * JaxpResponse extends {@link com.att.research.xacml.std.StdMutableResponse} with methods for creation from
 * JAXP elements.
 * 
 * @author car
 * @version $Revision: 1.1 $
 */
public class JaxpResponse extends StdMutableResponse {
	private static final Logger	logger	= LoggerFactory.getLogger(JaxpResponse.class);

	protected JaxpResponse() {
	}

	public static JaxpResponse newInstance(ResponseType responseType) {
		if (responseType == null) {
			throw new NullPointerException("Null ResponseType");
		} else if (responseType.getResult() == null || responseType.getResult().size() == 0) {
			throw new IllegalArgumentException("No ResultTypes in ResponseType");
		}
		JaxpResponse	jaxpResponse	= new JaxpResponse();
		
		Iterator<ResultType>	iterResults	= responseType.getResult().iterator();
		while (iterResults.hasNext()) {
			jaxpResponse.add(JaxpResult.newInstance(iterResults.next()));
		}
		
		return jaxpResponse;
	}
	/**
	 * Creates a new <code>JaxpResponse</code> by loading it from an XML <code>File</code>.
	 * 
	 * @param fileXmlResponse the <code>File</code> containing the Response XML
	 * @return a new <code>JaxpResponse</code> generated by parsing the given XML file 
	 * @throws ParserConfigurationException
	 * @throws IOException
	 * @throws SAXException
	 * @throws JAXBException
	 */
	public static JaxpResponse load(File fileXmlResponse) throws ParserConfigurationException, IOException, SAXException, JAXBException {
		if (fileXmlResponse == null) {
			throw new NullPointerException("Null File");
		}
		
    	/*
    	 * Create XML document factory and builder
    	 */
		DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
        documentBuilderFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
	    documentBuilderFactory.setNamespaceAware(true);
		DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
		
		/*
		 * Parse the file into a Document
		 */
		Document	document	= documentBuilder.parse(fileXmlResponse);
		if (document == null) {
			logger.error("No Document returned parsing {}", fileXmlResponse.getAbsolutePath());
			return null;
		}
		
		NodeList	nodeListRoot	= document.getChildNodes();
		if (nodeListRoot == null || nodeListRoot.getLength() == 0) {
			logger.warn("No child elements of the XML document");
			return null;
		}
		Node		nodeRoot		= nodeListRoot.item(0);
		if (nodeRoot == null || nodeRoot.getNodeType() != Node.ELEMENT_NODE) {
			logger.warn("Root of the document is not an ELEMENT");
			return null;
		}
		
		JAXBContext 				context 			= JAXBContext.newInstance(ResponseType.class);
		Unmarshaller 				unmarshaller 		= context.createUnmarshaller();
		JAXBElement<ResponseType>	jaxbElementResponse = unmarshaller.unmarshal((Element)nodeRoot, ResponseType.class);
		if (jaxbElementResponse == null || jaxbElementResponse.getValue() == null) {
			logger.error("JAXB unmarshalling did not return a ResponseType node");
			return null;
		}
		return JaxpResponse.newInstance(jaxbElementResponse.getValue());
		
	}
	
    //
    // This main() method should only be used for local testing, and not
    // for running anything in a production environment.
    //
	public static void main(String[] args) { //NOSONAR
        Collection<String> santized = MainUtils.santizeArguments(args);
        if (santized.isEmpty()) {
            return;
        }
		for (String fileName: santized) {
			JaxpResponse	jaxpResponse	= null;
			try {
				jaxpResponse	= JaxpResponse.load(new File(fileName));
			} catch (Exception ex) {
				logger.error("Failed to load \"" + fileName + "\" as a JaxpResponse", ex);
				continue;
			}
			if (jaxpResponse == null) {
				logger.warn("Null JaxpResponse returned for file {}", fileName);
			} else {
				logger.info("JaxpResponse for file {}={}", fileName, jaxpResponse);
			}
		}
	}

}