package de.renew.gui;

import java.rmi.RemoteException;
import java.util.Vector;

import CH.ifa.draw.figures.TextFigure;
import de.renew.draw.storables.ontology.Figure;
import de.renew.plugin.PluginProperties;
import de.renew.propertymanagement.prop.ConfigurablePropertyManager;
import de.renew.propertymanagement.prop.PropertyChangeEvent;
import de.renew.propertymanagement.prop.PropertyChangeListener;
import de.renew.remote.ObjectAccessor;
import de.renew.util.TextToken;


/**
 * @author joern
 *
 * This is a singleton class that is used to get gui representation
 * for tokens.
 * FigureCreators are registered by the class they can represent
 * and will be called if a token of such class occurs.
 * If a FigureCreator returns null as a result, a TextFigure
 * with content a text version of the token will be created.
 */
public class FigureCreatorComposition
    implements FigureCreator, TextFigureCreator, FigureCreatorHolder
{
    private static final org.apache.log4j.Logger logger =
        org.apache.log4j.Logger.getLogger(FigureCreatorComposition.class);
    private static final String ARC_DEFAULT_PROP_NAME = "de.renew.defaults.gui.arc";
    private static final String PLACE_DEFAULT_PROP_NAME = "de.renew.defaults.gui.place";
    private static final String TRANSITION_DEFAULT_PROP_NAME = "de.renew.defaults.gui.transition";
    public static String _arcConnectionDefaultName = "x";
    public static String _placeFigureDefaultName = "[]";
    public static String _transitionFigureDefaultName = ":s()";
    private final Vector<FigureCreator> _figureCreators = new Vector<>();
    private final Vector<TextFigureCreator> _textFigureCreators = new Vector<>();

    public FigureCreatorComposition(PluginProperties plugincfg) {
        final ConfigurablePropertyManager propManager = ConfigurablePropertyManager.getInstance();
        propManager.addConfigurableProperty(
            ARC_DEFAULT_PROP_NAME, _arcConnectionDefaultName, "Gui", "Arc default inscription",
            null);
        propManager.addConfigurableProperty(
            PLACE_DEFAULT_PROP_NAME, _placeFigureDefaultName, "Gui", "Place default inscription",
            null);
        propManager.addConfigurableProperty(
            TRANSITION_DEFAULT_PROP_NAME, _transitionFigureDefaultName, "Gui",
            "Transition inscription", null);

        _arcConnectionDefaultName = PluginProperties.getUserProperties()
            .getProperty(ARC_DEFAULT_PROP_NAME, plugincfg.getProperty(ARC_DEFAULT_PROP_NAME, "x"));
        _placeFigureDefaultName = PluginProperties.getUserProperties().getProperty(
            PLACE_DEFAULT_PROP_NAME, plugincfg.getProperty(PLACE_DEFAULT_PROP_NAME, "[]"));
        _transitionFigureDefaultName = PluginProperties.getUserProperties().getProperty(
            TRANSITION_DEFAULT_PROP_NAME,
            plugincfg.getProperty(TRANSITION_DEFAULT_PROP_NAME, ":s()"));

        propManager.addPropertyChangeListener(new PropertyChangeListener() {
            @Override
            public void propertyChanged(PropertyChangeEvent event) {
                switch (event.getProperty()) {
                    case ARC_DEFAULT_PROP_NAME -> _arcConnectionDefaultName = event.getNewValue();
                    case PLACE_DEFAULT_PROP_NAME -> _placeFigureDefaultName = event.getNewValue();
                    case TRANSITION_DEFAULT_PROP_NAME ->
                        _transitionFigureDefaultName = event.getNewValue();
                }
            }

            @Override
            public void propertyAdded(PropertyChangeEvent event) {

            }

            @Override
            public void propertyRemoved(PropertyChangeEvent event) {

            }
        });
    }

    @Override
    public void registerCreator(FigureCreator creator) {
        _figureCreators.add(creator);
    }

    @Override
    public void registerCreator(TextFigureCreator creator) {
        _textFigureCreators.add(creator);
    }

    @Override
    public void unregisterCreator(FigureCreator creator) {
        _figureCreators.remove(creator);
    }

    @Override
    public void unregisterCreator(TextFigureCreator creator) {
        _textFigureCreators.remove(creator);
    }

    @Override
    public boolean canCreateFigure(ObjectAccessor o, boolean expanded) throws RemoteException {
        for (FigureCreator fc : _figureCreators) {
            if (fc.canCreateFigure(o, expanded)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean canCreateFigure(InscribableFigure f) {
        for (FigureCreator figureCreator : _figureCreators) {
            TextFigureCreator fc = (TextFigureCreator) figureCreator;
            if (fc.canCreateFigure(f)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public boolean canCreateDefaultInscription(InscribableFigure f) {
        for (FigureCreator figureCreator : _figureCreators) {
            TextFigureCreator fc = (TextFigureCreator) figureCreator;
            if (fc.canCreateDefaultInscription(f)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public TextFigure createTextFigure(InscribableFigure figure) {
        TextFigure result = null;
        for (TextFigureCreator creator : _textFigureCreators) {
            if (creator.canCreateFigure(figure)) {
                logger.debug("FigureCreation: " + creator + " creates a text figure for " + figure);
                result = creator.createTextFigure(figure);
                break;
            }
        }
        if (result == null) {
            logger.debug("FigureCreation: creating default text figure for " + figure);
            return new CPNTextFigure();
        }
        return result;
    }

    @Override
    public String getDefaultInscription(InscribableFigure figure) {
        String result = null;
        for (TextFigureCreator creator : _textFigureCreators) {
            if (creator.canCreateDefaultInscription(figure)) {
                logger
                    .debug("FigureCreation: " + creator + " creates an inscription for " + figure);
                result = creator.getDefaultInscription(figure);
                break;
            }
        }
        if (result == null) {
            logger.debug("FigureCreation: creating default inscription for " + figure);
            if (figure instanceof ArcConnection) {
                return _arcConnectionDefaultName;
            }
            if (figure instanceof PlaceFigure) {
                return _placeFigureDefaultName;
            }
            if (figure instanceof TransitionFigure) {
                return _transitionFigureDefaultName;
            }
            return "";
        }
        return result;
    }

    @Override
    public Figure getTokenFigure(ObjectAccessor token, boolean expanded) throws RemoteException {
        Figure result = null;
        for (FigureCreator creator : _figureCreators) {
            if (creator.canCreateFigure(token, expanded)) {
                logger.debug(creator + " creates a representation for this token.");
                result = creator.getTokenFigure(token, expanded);
                break;
            }
        }
        if (result == null) {
            logger.debug("creating default representation for this token.");
            return new TextFigure(objectToString(token), true);
        }
        if (logger.isDebugEnabled()) {
            logger.debug("got token figure for token " + token + " --> result figure " + result);
        }
        return result;
    }

    public static String objectToString(ObjectAccessor token) throws RemoteException {
        StringBuilder output = new StringBuilder();

        if (token == null) {
            output.append("null");
        } else if (token.isInstanceOf(String.class)) {
            output.append('"').append(token.asString()).append('"');
        } else if (token.isInstanceOf(TextToken.class)) {
            output.append(token.asTextToken().toTokenText());
        } else {
            output.append(token.asString());
        }
        return output.toString();
    }
}