package de.renew.formalism.pt;

import java.util.Collection;
import java.util.Iterator;

import de.renew.expression.ConstantExpression;
import de.renew.formalism.base.AbstractSingleNetCompiler;
import de.renew.net.ConstantTokenSource;
import de.renew.net.Place;
import de.renew.net.Transition;
import de.renew.net.inscription.TransitionInscription;
import de.renew.net.inscription.arc.Arc;
import de.renew.simulatorontology.shadow.ShadowArc;
import de.renew.simulatorontology.shadow.ShadowDeclarationNode;
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.ShadowPlace;
import de.renew.simulatorontology.shadow.SyntaxException;
import de.renew.unify.Tuple;


public class SinglePTNetCompiler extends AbstractSingleNetCompiler {
    // Check the validity of certain inscriptions.
    @Override
    public String checkDeclarationNode(String inscription, boolean special, ShadowNet shadowNet)
        throws SyntaxException
    {
        throw new SyntaxException("Declaration node is not allowed.");
    }

    @Override
    public String checkArcInscription(String inscription, boolean special, ShadowNet shadowNet)
        throws SyntaxException
    {
        parseNumber(inscription);
        return "inscription";
    }

    @Override
    public String checkTransitionInscription(
        String inscription, boolean special, ShadowNet shadowNet) throws SyntaxException
    {
        throw new SyntaxException("Transition inscription is not allowed.");
    }

    @Override
    public String checkPlaceInscription(String inscription, boolean special, ShadowNet shadowNet)
        throws SyntaxException
    {
        parseNumber(inscription);
        return "initialMarking";
    }

    private static int parseNumber(String inscr) throws SyntaxException {
        try {
            return Integer.parseInt(inscr.trim());
        } catch (NumberFormatException nfe) {
            throw new SyntaxException("Number expected", nfe).addObject(inscr);
        }
    }

    protected static int parseNumericalInscription(
        ShadowInscribable inscribable, int defaultValue, int minValue) throws SyntaxException
    {
        int result = defaultValue;
        boolean inscriptionFound = false;

        // Insert the inscriptions.
        for (Object elem : inscribable.elements()) {
            if (elem instanceof ShadowInscription) {
                if (inscriptionFound) {
                    throw new SyntaxException("Multiple inscriptions not supported")
                        .addObject(inscribable);
                }
                inscriptionFound = true;
                result = parseNumber(((ShadowInscription) elem).getInscription());
            } else if (!(elem instanceof ShadowArc)) {
                throw new SyntaxException("Unsupported inscription").addObject(inscribable)
                    .addObject(elem);
            }
        }
        if (result < minValue) {
            throw new SyntaxException("Inscribed value must not be lower than " + minValue)
                .addObject(inscribable);
        }
        return result;
    }

    @Override
    protected void compilePlaceInscriptions(ShadowPlace shadowPlace, Place place)
        throws SyntaxException
    {
        int marking = parseNumericalInscription(shadowPlace, 0, 0);
        for (int i = 0; i < marking; i++) {
            place.add(new ConstantTokenSource(Tuple.NULL));
        }
    }

    @Override
    protected void compileArc(ShadowArc shadowArc) throws SyntaxException {
        int shadowArcType = shadowArc.getShadowArcType();
        if (shadowArcType != ShadowArc.ORDINARY) {
            throw new SyntaxException("Unsupported arc type").addObject(shadowArc);
        }
        Arc.Type arcType = shadowArc.isPlaceToTransition() ? Arc.Type.IN : Arc.Type.OUT;
        int multiplicity = parseNumericalInscription(shadowArc, 1, 1);
        Transition transition = lookup.get(shadowArc.getTransition());
        Place place = lookup.get(shadowArc.getPlace());
        for (int i = 0; i < multiplicity; i++) {
            Arc arc = new Arc(
                place, transition, arcType, new ConstantExpression(null, Tuple.NULL),
                ConstantExpression.DOUBLE_ZERO_EXPRESSION);
            transition.add(arc);
        }
    }

    @Override
    protected Collection<TransitionInscription> compileTransitionInscription(
        ShadowInscription inscription) throws SyntaxException
    {
        throw new SyntaxException("Transitions may not carry inscriptions").addObject(inscription);
    }

    @Override
    public void parseDeclarations(ShadowNet shadowNet) throws SyntaxException {
        Iterator<ShadowNetElement> iterator = shadowNet.elements().iterator();
        while (iterator.hasNext()) {
            Object elem = iterator.next();
            if (elem instanceof ShadowDeclarationNode) {
                SyntaxException e = new SyntaxException("Declaration node is not allowed.");
                e.addObject(elem);
                while (iterator.hasNext()) {
                    elem = iterator.next();
                    if (elem instanceof ShadowDeclarationNode) {
                        e.addObject(elem);
                    }
                }
                throw e;
            }
        }
    }
}