package de.renew.gui.nin;

import java.awt.Dimension;
import java.awt.Point;

import CH.ifa.draw.figures.AttributeFigure;
import de.renew.gui.ArcConnection;
import de.renew.gui.CPNDrawing;
import de.renew.gui.CPNTextFigure;
import de.renew.gui.InscribableFigure;
import de.renew.gui.PlaceFigure;
import de.renew.gui.TransitionFigure;
import de.renew.simulatorontology.shadow.ShadowArc;

/**
 * This class allows building net templates, which can later
 * be used to build specific net drawings for various use cases.
 * Currently, the template builder can create a {@link CPNDrawing}
 * to be used as a system net in the P/T NiN formalism.
 * With small changes, it is possible to build
 * other net templates with this.
 *
 * @author Lukas Voß
 */
public class TemplateBuilder {

    private static final Point[] elementPositions = new Point[] {
        new Point(200, 100), new Point(100, 100), new Point(100, 200), new Point(100, 300),
        new Point(200, 300) };
    private static final String[] inscriptions = new String[] {
        "[]", "netA:new NetA;\nnetB:new NetB", "", "NetReferences", "netA;netB",
        ":getReference(net)", "net",
        "this:getReference(netA);\nthis:getReference(netB);\nnetA:sync();\nnetB:sync()" };

    /**
     * Build a net template for the PT-Channel Net-In-Net formalism.
     * It consists of an initially marked place in front of a transition
     * which can create new net instances,
     * a place to hold these created instances
     * and a transition via which the instances can synchronize.
     *
     * @return the constructed net template
     */
    public static CPNDrawing buildSystemNetTemplate() {
        CPNDrawing systemNet = new CPNDrawing();

        PlaceFigure initiallyMarkedPlace = buildPlace(
            systemNet, inscriptions[0], CPNTextFigure.STYLE[2], CPNTextFigure.INSCRIPTION,
            elementPositions[0], 0, 0);
        TransitionFigure netCreationTransition =
            buildTransition(systemNet, inscriptions[1], elementPositions[1], 0, -25);
        buildArc(
            initiallyMarkedPlace, netCreationTransition, ShadowArc.ORDINARY, inscriptions[2],
            systemNet, 0, 0);

        PlaceFigure referenceHolder = buildPlace(
            systemNet, inscriptions[3], CPNTextFigure.STYLE[0], CPNTextFigure.NAME,
            elementPositions[2], 65, 0);
        buildArc(
            netCreationTransition, referenceHolder, ShadowArc.ORDINARY, inscriptions[4], systemNet,
            0, 0);

        TransitionFigure getReferenceTransition =
            buildTransition(systemNet, inscriptions[5], elementPositions[3], 0, 20);
        buildArc(
            referenceHolder, getReferenceTransition, ShadowArc.TEST, inscriptions[6], systemNet, 0,
            0);
        buildTransition(systemNet, inscriptions[7], elementPositions[4], 75, -20);

        return systemNet;
    }

    /**
     * Build a place for the net template.
     *
     * @param systemNet        net this place gets added to
     * @param inscription      the inscription for the place
     * @param style            style of the inscription (bold, italic etc.)
     * @param inscriptionType  type of the inscription (name, inscription etc.)
     * @param position         position of the place in the net
     * @param moveInscriptionX after creation at place position, move inscription in x-direction
     * @param moveInscriptionY after creation at place position, move inscription in y-direction
     * @return the newly created place
     */
    private static PlaceFigure buildPlace(
        CPNDrawing systemNet, String inscription, int style, int inscriptionType, Point position,
        int moveInscriptionX, int moveInscriptionY)
    {
        PlaceFigure place = new PlaceFigure();
        systemNet.add(place);
        setFigurePosition(place, position);
        CPNTextFigure inscriptionText = new CPNTextFigure(inscriptionType);
        inscriptionText.setText(inscription);
        inscriptionText.setAttribute("TextType", style);
        addChildText(place, inscriptionText, systemNet, moveInscriptionX, moveInscriptionY);
        return place;
    }

    /**
     * Build a transition for the net template.
     *
     * @param systemNet        net this transition gets added to
     * @param inscription      the inscription for the transition
     * @param position         position of the transition in the net
     * @param moveInscriptionX after creation at transition position, move inscription in x-direction
     * @param moveInscriptionY after creation at transition position, move inscription in y-direction
     * @return the newly created transition
     */
    private static TransitionFigure buildTransition(
        CPNDrawing systemNet, String inscription, Point position, int moveInscriptionX,
        int moveInscriptionY)
    {
        TransitionFigure transition = new TransitionFigure();
        systemNet.add(transition);
        setFigurePosition(transition, position);
        CPNTextFigure inscriptionText = new CPNTextFigure(CPNTextFigure.INSCRIPTION);
        inscriptionText.setText(inscription);
        addChildText(transition, inscriptionText, systemNet, moveInscriptionX, moveInscriptionY);
        return transition;
    }

    /**
     * Build an arc for the net template.
     *
     * @param outgoing         figure this arc goes out of
     * @param incoming         figure this arc goes into
     * @param inscription      inscription of the arc
     * @param systemNet        net this transition gets added to
     * @param moveInscriptionX after creation at arc position, move inscription in x-direction
     * @param moveInscriptionY after creation at arc position, move inscription in y-direction
     */
    private static void buildArc(
        InscribableFigure outgoing, InscribableFigure incoming, int arcType, String inscription,
        CPNDrawing systemNet, int moveInscriptionX, int moveInscriptionY)
    {
        ArcConnection addedArc = new ArcConnection(arcType);
        addedArc.startPoint(outgoing.center());
        addedArc.endPoint(incoming.center());
        systemNet.add(addedArc);
        addedArc.connectStart(outgoing.connectorAt(outgoing.center()));
        addedArc.connectEnd(incoming.connectorAt(incoming.center()));
        if (!inscription.isBlank()) {
            CPNTextFigure creationText = new CPNTextFigure(CPNTextFigure.INSCRIPTION);
            creationText.setText(inscription);
            addChildText(addedArc, creationText, systemNet, moveInscriptionX, moveInscriptionY);
        }
        addedArc.updateConnection();
    }

    /**
     * Set the display box of the figure.
     * Should be either a {@link PlaceFigure} or a {@link TransitionFigure}.
     *
     * @param figure   set this figure's display box
     * @param position coordinates for the display box
     */
    private static void setFigurePosition(AttributeFigure figure, Point position) {
        Dimension d;
        if (figure instanceof PlaceFigure) {
            d = PlaceFigure.defaultDimension();
        } else {
            d = TransitionFigure.defaultDimension();
        }
        int w2 = d.width / 2;
        int h2 = d.height / 2;
        figure.displayBox(
            new Point(position.x - w2, position.y - h2),
            new Point(position.x - w2 + d.width, position.y - h2 + d.height));
    }

    /**
     * Add an inscription to an inscribable figure.
     *
     * @param parent this figure should be inscribed
     * @param child  this is the inscription
     */
    private static void addChildText(
        InscribableFigure parent, CPNTextFigure child, CPNDrawing systemNet, int moveX, int moveY)
    {
        systemNet.add(child);
        parent.addChild(child);
        child.setParent(parent);
        child.moveBy(moveX, moveY);
    }
}
