package de.renew.gui.nin;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.FigureEnumeration;
import de.renew.gui.ArcConnection;
import de.renew.gui.CPNTextFigure;
import de.renew.gui.TransitionFigure;

/**
 * A PTCNetInformation object holds information about a
 * PTC net, which it gets at instantiation.
 * When instantiated, it calculates all necessary information
 * which it provides.
 * Because some of the information requires to loop through
 * all net elements, they are saved in fields and thus
 * only need to be calculated once.
 */
public class PTCNetInformation {

    private Map<TransitionFigure, String> initialUplinkTransitions;
    private Map<TransitionFigure, String> initialDownlinkTransitions;
    private Collection<ArcConnection> initialArcFigures;
    private Map<ArcConnection, CPNTextFigure> initialArcInscriptions;
    private Map<TransitionFigure, CPNTextFigure> initialTransitionInscriptions;

    /**
     * Creates a new <code>PTCNetInformation</code> instance.
     *
     * @param ptc_netdrawing hold information of this drawing.
     **/
    public PTCNetInformation(Drawing ptc_netdrawing) {
        extractFigures(ptc_netdrawing);
    }

    /**
     * Extracts all relevant figures of a given net drawing and
     * saves them in a corresponding field.
     * This is done because it is more efficient to set a field once
     * and being able to read it at anytime, then always looping through
     * all figures of a drawing, which might contain many figures.
     *
     * @param netdrawing extract the figures of this drawing
     */
    private void extractFigures(Drawing netdrawing) {
        initialArcFigures = new HashSet<>();
        initialArcInscriptions = new HashMap<>();
        initialTransitionInscriptions = new HashMap<>();
        initialUplinkTransitions = new HashMap<>();
        initialDownlinkTransitions = new HashMap<>();

        FigureEnumeration enumerator = netdrawing.figures();
        while (enumerator.hasMoreElements()) {
            Figure fig = enumerator.nextFigure();
            if (fig instanceof ArcConnection) {
                initialArcFigures.add((ArcConnection) fig);
            } else if (fig instanceof CPNTextFigure text) {
                if ((text.getType() == CPNTextFigure.INSCRIPTION)
                    && text.parent() instanceof ArcConnection) {
                    initialArcInscriptions.put((ArcConnection) text.parent(), text);
                }
                if ((text.getType() == CPNTextFigure.INSCRIPTION)
                    && text.parent() instanceof TransitionFigure) {
                    initialTransitionInscriptions.put((TransitionFigure) text.parent(), text);
                    Matcher matcher = parseChannelInscription(text);
                    if (matcher != null) {
                        if (matcher.group(1).isBlank()) {
                            initialUplinkTransitions
                                .put((TransitionFigure) text.parent(), matcher.group(2));
                        } else {
                            initialDownlinkTransitions
                                .put((TransitionFigure) text.parent(), matcher.group(2));
                        }
                    }
                }
            }
        }
    }

    /**
     * Parse a text figure to check if it is an up- or downlink.
     * If it is, return the corresponding matcher, which
     * contains the groups of the text.
     * - group(1): NetReference name of downlink (empty for uplinks)
     * - group(2): Channel name
     *
     * @param textFigure parse this text figure
     * @return matcher which contains information about the up-/downlink inscription
     */
    private Matcher parseChannelInscription(CPNTextFigure textFigure) {
        Pattern pattern = Pattern.compile("(.*):(.*)\\(.*\\)");
        Matcher m = pattern.matcher(textFigure.getText());
        if (m.find()) {
            return m;
        }
        return null;
    }

    /**
     * Calculate all arcs that start at the given transition.
     *
     * @param fig transition with outgoing arcs
     * @return set of arcs that come out of the transition
     */
    public Set<ArcConnection> getOutgoingArcs(TransitionFigure fig) {
        Set<ArcConnection> arcs = new HashSet<>();
        for (ArcConnection arc : initialArcFigures) {
            if (arc.startFigure() == fig) {
                arcs.add(arc);
            }
        }
        return arcs;
    }

    /**
     * Calculate all arcs that end at the given transition.
     *
     * @param fig transition with incoming arcs
     * @return set of arcs that lead into the transition
     */
    public Set<ArcConnection> getIncomingArcs(TransitionFigure fig) {
        Set<ArcConnection> arcs = new HashSet<>();
        for (ArcConnection arc : initialArcFigures) {
            if (arc.endFigure() == fig) {
                arcs.add(arc);
            }
        }
        return arcs;
    }

    // Getters for fields

    public Map<TransitionFigure, String> getInitialUplinkTransitions() {
        return initialUplinkTransitions;
    }

    public Map<TransitionFigure, String> getInitialDownlinkTransitions() {
        return initialDownlinkTransitions;
    }

    public Map<ArcConnection, CPNTextFigure> getInitialArcInscriptions() {
        return initialArcInscriptions;
    }

    public Map<TransitionFigure, CPNTextFigure> getInitialTransitionInscriptions() {
        return initialTransitionInscriptions;
    }
}
