/*-
 * #%L
 * This file is part of "Apromore Community".
 * 
 * Copyright (C) 2015 - 2017 Queensland University of Technology.
 * %%
 * Copyright (C) 2018 - 2020 Apromore Pty Ltd.
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
 * #L%
 */

package ee.ut.mining.log.poruns.fpes;

import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.google.common.collect.Multimap;

import ee.ut.eventstr.freq.FrequencyAwarePrimeEventStructure;
import ee.ut.graph.transitivity.BitsetDAGTransitivity;
import ee.ut.mining.log.poruns.PORuns;
import ee.ut.org.processmining.framework.util.Pair;

public class PORuns2FPES {
	public static FrequencyAwarePrimeEventStructure<Integer> getPrimeEventStructure(PORuns runs, String modelName) {
		Map<Integer, String> lmap = runs.getLabels();
		int size = lmap.size();
		List<String> labels = new ArrayList<>(size);
		for (int i = 0; i < size; i++)
			labels.add(lmap.get(i));

		return getPrimeEventStructure(runs.getSuccessors(), runs.getConcurrency(), runs.getSources(),
				runs.getSinks(), labels, runs.getEquivalenceClasses(), modelName);
	}
	public static FrequencyAwarePrimeEventStructure<Integer> getPrimeEventStructure(
			Multimap<Integer, Integer> adj, Multimap<Integer, Integer> conc,
			List<Integer> sources, List<Integer> sinks,
			List<String> labels, Multimap<Integer, Integer> equivalenceClasses, String modelName) {
		int size = labels.size();
		Pair<BitSet[], BitSet[]> pair = BitsetDAGTransitivity.transitivityDAG(adj, size, sources);
		BitSet[] causality = pair.getFirst();
		BitSet[] dcausality = pair.getSecond();
		BitSet[] invcausality = new BitSet[size];
		BitSet[] concurrency = new BitSet[size];
		BitSet[] conflict = new BitSet[size];
		
		for (int i = 0; i < size; i++) {
			invcausality[i] = new BitSet();
			concurrency[i] = new BitSet();
			conflict[i] = new BitSet();
		}
		
		for (Entry<Integer, Integer> entry: conc.entries())
			concurrency[entry.getKey()].set(entry.getValue());
		
		for (int i = 0; i < size; i++)
			for (int j = causality[i].nextSetBit(0); j >= 0; j = causality[i].nextSetBit(j + 1))
				invcausality[j].set(i);
		
		for (int i = 0; i < size; i++) {
			BitSet union = (BitSet) causality[i].clone();
			union.or(invcausality[i]);
			union.or(concurrency[i]);
			union.set(i); // Remove IDENTITY
			conflict[i].flip(0, size);
			conflict[i].xor(union);
		}
		
		Map<Integer, Integer> occurrences = new HashMap<Integer, Integer>();
		for (Integer event: equivalenceClasses.keySet())
			occurrences.put(event, equivalenceClasses.get(event).size());

		double[][] fmatrix = new double[size][size];
		for (int i = 0; i < size; i++) {
			for (int j = 0; j < size; j++) {
				if (causality[i].get(j))
					fmatrix[i][j] = (occurrences.get(j) + 0.0f) / occurrences.get(i);
			}
		}

		
		FrequencyAwarePrimeEventStructure<Integer> pes = 
				new FrequencyAwarePrimeEventStructure<Integer>(labels, causality, dcausality, invcausality,
						concurrency, conflict, sources, sinks, occurrences, fmatrix);		
		return pes;
	}	
}
