/**
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.oozie.ambari.view;

import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.net.URLEncoder;
import java.io.UnsupportedEncodingException;

import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.StreamingOutput;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerFactoryConfigurationError;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class Utils {
	private static final String XML_INDENT_SPACES = "4";
	private static final String XML_INDENT_AMT_PROP_NAME = "{http://xml.apache.org/xslt}indent-amount";
	private final String FEATURES_DISALLOW_DOCTYPE = "http://apache.org/xml/features/disallow-doctype-decl";
	private final static Logger LOGGER = LoggerFactory
			.getLogger(Utils.class);
	private final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
	Utils() {
		// Refer to https://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html#setFeature-java.lang.String-boolean-
		try{
			dbf.setFeature(FEATURES_DISALLOW_DOCTYPE, true);
			LOGGER.info("Setting feature disallow doctype to true");
		} catch (ParserConfigurationException | TransformerFactoryConfigurationError e) {
			LOGGER.error("Error in formatting xml", e);
			throw new RuntimeException(e);
		}
	}
	public String formatXml(String xml) {

		try {
			DocumentBuilder db = dbf.newDocumentBuilder();
			StreamResult result = new StreamResult(new StringWriter());
			Document document = db
					.parse(new InputSource(new StringReader(xml)));
			Transformer transformer = TransformerFactory.newInstance()
					.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty(XML_INDENT_AMT_PROP_NAME,
					XML_INDENT_SPACES);
			DOMSource source = new DOMSource(document);
			transformer.transform(source, result);
			return result.getWriter().toString();
		} catch (ParserConfigurationException | SAXException | IOException
				| TransformerFactoryConfigurationError | TransformerException e) {
			LOGGER.error("Error in formatting xml", e);
			throw new RuntimeException(e);
		}
	}
	public String generateXml(Document doc){
		DOMSource domSource = new DOMSource(doc);
		StringWriter writer = new StringWriter();
		StreamResult result = new StreamResult(writer);
		TransformerFactory tf = TransformerFactory.newInstance();
		Transformer transformer;
		try {
			transformer = tf.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.setOutputProperty(XML_INDENT_AMT_PROP_NAME,
					XML_INDENT_SPACES);
			try {
				transformer.transform(domSource, result);
			} catch (TransformerException e) {
				throw new RuntimeException(e);
			}
			return writer.toString();
		} catch (TransformerConfigurationException tce) {
			throw new RuntimeException(tce);
		}
		
	}

	public Map<String, String> getHeaders(HttpHeaders headers) {
		MultivaluedMap<String, String> requestHeaders = headers
				.getRequestHeaders();
		Set<Entry<String, List<String>>> headerEntrySet = requestHeaders
				.entrySet();
		HashMap<String, String> headersMap = new HashMap<String, String>();
		for (Entry<String, List<String>> headerEntry : headerEntrySet) {
			String key = headerEntry.getKey();
			List<String> values = headerEntry.getValue();
			headersMap.put(key, strJoin(values, ","));
		}
		return headersMap;
	}

	public String strJoin(List<String> strings, String separator) {
		StringBuilder stringBuilder = new StringBuilder();
		for (int i = 0, il = strings.size(); i < il; i++) {
			if (i > 0) {
				stringBuilder.append(separator);
			}
			stringBuilder.append(strings.get(i));
		}
		return stringBuilder.toString();
	}
	public MediaType deduceType(String stringResponse) {
		if (stringResponse.startsWith("{")) {
			return MediaType.APPLICATION_JSON_TYPE;
		} else if (stringResponse.startsWith("<")) {
			return MediaType.TEXT_XML_TYPE;
		} else {
			return MediaType.APPLICATION_JSON_TYPE;
		}
	}

	public String convertParamsToUrl(MultivaluedMap<String, String> parameters) {
		StringBuilder urlBuilder = new StringBuilder();
		boolean firstEntry = true;
		for (Map.Entry<String, List<String>> entry : parameters.entrySet()) {
			if (firstEntry) {
				urlBuilder.append("?");
			} else {
				urlBuilder.append("&");
			}
			boolean firstVal = true;
			for (String val : entry.getValue()) {
				try {
					val = URLEncoder.encode(val, "UTF-8");
				} catch(UnsupportedEncodingException e) {
					LOGGER.error(e.getMessage(), e);
				}
				urlBuilder.append(firstVal ? "" : "&").append(entry.getKey())
						.append("=").append(val);
				firstVal = false;
			}
			firstEntry = false;
		}
		return urlBuilder.toString();
	}


  public boolean isXml(String postBody) {
		try {
			DocumentBuilder db = dbf.newDocumentBuilder();
			InputSource is = new InputSource();
			is.setCharacterStream(new StringReader(postBody));
			Document doc = db.parse(is);
			return true;
		} catch (Exception e) {
			return false;
		}
  }

  public StreamingOutput streamResponse(final InputStream is) {
    return new StreamingOutput() {
      @Override
      public void write(OutputStream os) throws IOException,
        WebApplicationException {
        BufferedInputStream bis = new BufferedInputStream(is);
        BufferedOutputStream bos = new BufferedOutputStream(os);
        try {
          int data;
          while ((data = bis.read()) != -1) {
            bos.write(data);
          }
          bos.flush();
          is.close();
        } catch (IOException e) {
          LOGGER.error(e.getMessage(), e);
          throw e;
        } catch (Exception e) {
          LOGGER.error(e.getMessage(), e);
          throw new RuntimeException(e);
        } finally {
          bis.close();
        }
      }
    };
  }
}
