/**
 * Copyright (c) 2016, the Alpha Team.
 * All rights reserved.
 *
 * Additional changes made by Siemens.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1) Redistributions of source code must retain the above copyright notice, this
 *    list of conditions and the following disclaimer.
 *
 * 2) Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package at.ac.tuwien.kr.alpha.commons.util;

import java.io.ByteArrayInputStream;
import java.net.URL;
import java.nio.channels.Channels;
import java.nio.channels.ReadableByteChannel;
import java.nio.charset.StandardCharsets;
import java.util.AbstractMap;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedSet;
import java.util.StringJoiner;
import java.util.stream.Collector;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.stringtemplate.v4.ST;
import org.stringtemplate.v4.STGroup;
import org.stringtemplate.v4.STGroupFile;

// TODO shouldn't be public API
public class Util {
	private static final String LITERATE_INDENT = "    ";

	public static <K, V> Map.Entry<K, V> entry(K key, V value) {
		return new AbstractMap.SimpleEntry<>(key, value);
	}

	public static <K, U> Collector<Map.Entry<K, U>, ?, Map<K, U>> entriesToMap() {
		return Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue);
	}

	public static <E> String join(String prefix, Iterable<E> iterable, String suffix) {
		return join(prefix, iterable, ", ", suffix);
	}

	public static <E> String join(String prefix, Iterable<E> iterable, String delimiter, String suffix) {
		StringJoiner joiner = new StringJoiner(delimiter, prefix, suffix);
		for (E element : iterable) {
			joiner.add(element.toString());
		}
		return joiner.toString();
	}

	public static <U extends T, T extends Comparable<T>> int compareSortedSets(SortedSet<U> a, SortedSet<U> b) {
		if (a.size() != b.size()) {
			return a.size() - b.size();
		}

		if (a.isEmpty() && b.isEmpty()) {
			return 0;
		}

		final Iterator<U> ita = a.iterator();
		final Iterator<U> itb = b.iterator();

		do {
			final int result = ita.next().compareTo(itb.next());

			if (result != 0) {
				return result;
			}
		} while (ita.hasNext() && itb.hasNext());

		return 0;
	}

	public static RuntimeException oops(String message, Exception e) {
		return new RuntimeException(message + "! Should not happen.", e);
	}

	public static RuntimeException oops(String message) {
		// We do not call oops(String, Exception) here to not bloat the stack trace.
		return new RuntimeException(message + "! Should not happen.");
	}

	public static RuntimeException oops() {
		return oops("Reached fatal state");
	}

	public static Stream<String> literate(Stream<String> input) {
		return input.map(l -> {
			if (l.startsWith(LITERATE_INDENT)) {
				return l.substring(LITERATE_INDENT.length());
			}
			return "% " + l;
		});
	}

	public static ReadableByteChannel streamToChannel(Stream<String> lines) {
		return Channels.newChannel(new ByteArrayInputStream(lines.collect(Collectors.joining(System.lineSeparator())).getBytes(StandardCharsets.UTF_8)));
	}

	public static int arrayGrowthSize(int oldSize) {
		// Growth factor is 1.5.
		return oldSize + (oldSize >> 1);
	}

	/**
	 * Creates a stringtemplate (see {@link ST}) intended for rendering to ASP code from a given string.
	 * The template uses "$" as attribute delimiter.
	 * 
	 * @param template the template string
	 * @return an ST that is initialized with the given template string
	 */
	public static ST aspStringTemplate(String template) {
		return new ST(template, '$', '$');
	}

	/**
	 * Loads a stringtemplate group intended for rendering ASP code from a file.
	 * Templates are assumed to use "$" as attribute delimiter.
	 * 
	 * @param classPathUrl
	 * @return
	 */
	public static STGroup loadStringTemplateGroup(URL url) {
		STGroupFile groupFile = new STGroupFile(url, "UTF-8", '$', '$');
		return groupFile;
	}
	
}
