package de.renew.gui;

import java.awt.Color;
import java.awt.Font;
import java.io.IOException;

import CH.ifa.draw.figures.TextFigure;
import CH.ifa.draw.framework.ParentFigure;
import de.renew.draw.storables.ontology.Figure;
import de.renew.draw.storables.ontology.StorableInput;
import de.renew.draw.storables.ontology.StorableOutput;
import de.renew.simulatorontology.shadow.ShadowInscribable;
import de.renew.simulatorontology.shadow.ShadowInscription;
import de.renew.simulatorontology.shadow.ShadowNet;
import de.renew.simulatorontology.shadow.ShadowNetElement;
import de.renew.simulatorontology.shadow.ShadowNode;


public class CPNTextFigure extends TextFigure implements ShadowHolder {
    public static final int LABEL = 0;
    public static final int INSCRIPTION = 1;
    public static final int NAME = 2;
    public static final int AUX = 3;
    public static final int COMM = 4;
    public static final int[] STYLE =
        new int[] { Font.ITALIC, Font.PLAIN, Font.BOLD, Font.ITALIC + Font.BOLD, Font.PLAIN };
    public static final CPNTextFigure Label = new CPNTextFigure(LABEL);
    public static final CPNTextFigure Inscription = new CPNTextFigure(INSCRIPTION);
    public static final CPNTextFigure Name = new CPNTextFigure(NAME);
    public static final CPNTextFigure Aux = new CPNTextFigure(AUX);
    public static final CPNTextFigure Comm = new CPNTextFigure(COMM);

    /**
     * Determines the semantic type of the text.
     * @serial
     **/
    protected int fType;

    /**
     * The shadow of this inscription figure.
     * Initially <code>null</code>, will be created
     * when needed.
     * <p>
     * This field is transient because its information
     * can be regenerated via <code>buildShadow(...)</code>.
     * </p>
     **/
    protected transient ShadowInscription shadow = null;

    public CPNTextFigure() {
        this(LABEL);
    }

    public CPNTextFigure(int type) {
        this(type, true);
    }

    public CPNTextFigure(int type, boolean canBeConnected) {
        super(canBeConnected);
        fType = type;
        setAttribute("FontStyle", STYLE[type]);
        if (fType == COMM) {
            setTextColor(Color.BLUE);
        }
        setAlignment(CENTER);
    }

    public int getType() {
        return fType;
    }

    @Override
    public void setAttribute(String name, Object value) {
        if (name.equals("TextType")) {
            int newType = (Integer) value;
            if (canBeParent(newType, parent())) {
                willChange();
                fType = newType;
                super.setAttribute("FontStyle", STYLE[fType]);
            }
        } else {
            super.setAttribute(name, value);
        }
    }

    @Override
    public Object getAttribute(String name) {
        if (name.equals("TextType")) {
            return fType;
        }
        return super.getAttribute(name);
    }

    @Override
    public void write(StorableOutput dw) {
        super.write(dw);
        dw.writeInt(fType);
    }

    @Override
    public void read(StorableInput dr) throws IOException {
        super.read(dr);
        fType = dr.readInt();
    }

    protected void readWithoutType(StorableInput dr) throws IOException {
        super.read(dr);
        fType = INSCRIPTION;
    }

    protected boolean canBeParent(int type, ParentFigure parent) {
        return switch (type) {
            case LABEL -> true;
            case INSCRIPTION -> parent instanceof InscribableFigure;
            case AUX, NAME, COMM ->
                parent instanceof TransitionFigure || parent instanceof PlaceFigure;
            default -> false;
        };
    }

    @Override
    public boolean canBeParent(ParentFigure parent) {
        if (super.canBeParent(parent)) {
            return canBeParent(fType, parent);
        }
        return false;
    }

    @Override
    public void release() {
        super.release();
        if (shadow != null) {
            shadow.discard();
        }
    }

    // Build a shadow in the given shadow net.
    @Override
    public ShadowNetElement buildShadow(ShadowNet net) {
        shadow = null;
        if (fType != LABEL && parent() instanceof ShadowHolder) {
            ShadowInscribable parentShadow =
                (ShadowInscribable) ((ShadowHolder) parent()).getShadow();
            if (parentShadow != null) {
                if ((fType == INSCRIPTION) || (fType == AUX)) {
                    shadow = new ShadowInscription(parentShadow, getText());
                    shadow.setContext(this);
                    shadow.setID(this.getID());
                    shadow.setSpecial(fType == AUX);
                } else if (fType == NAME && parentShadow instanceof ShadowNode shadowNode) {
                    shadowNode.setName(getText());
                } else if (fType == COMM && parentShadow instanceof ShadowNode shadowNode) {
                    shadowNode.setComment(getText());
                }
            }
        }
        return shadow;
    }

    // Get the associated shadow, if any.
    @Override
    public ShadowNetElement getShadow() {
        return shadow;
    }

    /**
     * Determines whether the given figure is an inscription figure.
     * It is an inscription figure if it is a CPNTextFigure and its type is {@link #INSCRIPTION}.
     * @param figure the figure to be checked
     * @return {@code true} if the given figure is an inscription, else {@code false}
     */
    public static boolean isInscription(Figure figure) {
        return figure instanceof CPNTextFigure
            && ((CPNTextFigure) figure).getType() == CPNTextFigure.INSCRIPTION;
    }
}