package de.renew.gui;

import de.renew.net.Net;
import de.renew.net.Place;
import de.renew.net.PlaceInstance;
import de.renew.net.event.PlaceEvent;
import de.renew.net.event.PlaceEventListener;
import de.renew.net.event.PlaceEventProducer;
import de.renew.net.event.TokenEvent;


/**
 * Breakpoints of this type listen to place events.
 * These events may be produced by places themselves
 * (then the breakpoint will be hit at all instances) or
 * by a single place instance (then the breakpoint
 * will be hit at that instance only).
 *
 * @author Michael Duvigneau
 */
class PlaceInstanceBreakpoint implements Breakpoint, PlaceEventListener {
    private BreakpointManager manager;
    private final int reactOn;
    private PlaceEventProducer producer;
    private Net net;
    private PlaceInstance hitInstance;
    private String description;

    /**
     * Creates a breakpoint waiting for a place to change its marking.
     *
     * @param manager  the breakpoint manager supplies needed information and methods
     * @param producer the place (instance) to observe
     * @param reactOn  on which event to react on. Only values that are valid for places will cause
     *                 the breakpoint to be hit.
     * @param net      the net to which the place belongs
     **/
    PlaceInstanceBreakpoint(
        BreakpointManager manager, PlaceEventProducer producer, int reactOn, Net net)
    {
        if (reactOn == Breakpoint.DEFAULT) {
            reactOn = Breakpoint.MARKINGCHANGE;
        }
        producer.addPlaceEventListener(this);
        this.manager = manager;
        this.reactOn = reactOn;
        this.producer = producer;
        this.net = net;
        this.hitInstance = null;
        composeDescription();
    }

    @Override
    public boolean wantSynchronousNotification() {
        return true;
    }

    private void composeDescription() {
        StringBuilder desc = new StringBuilder("BP: ");
        if (producer instanceof PlaceInstance) {
            desc.append("place instance ").append(producer);
        } else if (producer instanceof Place) {
            desc.append("place ").append(net.getName()).append(".").append(producer);
        } else {
            desc.append("PlaceEventProducer ").append(producer);
        }
        switch (reactOn) {
            case Breakpoint.MARKINGCHANGE:
                desc.append(" changes marking");
                break;
            case Breakpoint.MARKINGCHANGENOTEST:
                desc.append(" changes marking (test arcs ignored)");
                break;
            case Breakpoint.TOKENADDED:
                desc.append(" gets one token added");
                break;
            case Breakpoint.TOKENREMOVED:
                desc.append(" loses one token");
                break;
            case Breakpoint.TOKENTESTCHANGE:
                desc.append(" changes test status of tokens");
                break;
            default:
                desc.append(" <wrong mode!>");
                break;
        }
        description = desc.toString();
    }

    // implementation of de.renew.event.PlaceEventListener interface
    private void hitBP(PlaceEvent event) {
        hitInstance = event.getPlaceInstance();
        manager.stopSimulation();
        manager.informHitBreakpoint(this);
    }

    @Override
    public void markingChanged(PlaceEvent event) {
        switch (reactOn) {
            case Breakpoint.MARKINGCHANGE:
            case Breakpoint.MARKINGCHANGENOTEST:
                hitBP(event);
            default:
        }
    }

    @Override
    public void tokenAdded(TokenEvent event) {
        switch (reactOn) {
            case Breakpoint.MARKINGCHANGE:
            case Breakpoint.MARKINGCHANGENOTEST:
            case Breakpoint.TOKENADDED:
                hitBP(event);
            default:
        }
    }

    @Override
    public void tokenRemoved(TokenEvent event) {
        switch (reactOn) {
            case Breakpoint.MARKINGCHANGE:
            case Breakpoint.MARKINGCHANGENOTEST:
            case Breakpoint.TOKENREMOVED:
                hitBP(event);
            default:
        }
    }

    @Override
    public void tokenTested(TokenEvent event) {
        switch (reactOn) {
            case Breakpoint.MARKINGCHANGE:
            case Breakpoint.TOKENTESTCHANGE:
                hitBP(event);
            default:
        }
    }

    @Override
    public void tokenUntested(TokenEvent event) {
        switch (reactOn) {
            case Breakpoint.MARKINGCHANGE:
            case Breakpoint.TOKENTESTCHANGE:
                hitBP(event);
            default:
        }
    }

    @Override
    public Object getTaggedElement() {
        return producer;
    }

    @Override
    public Object getHitElement() {
        return hitInstance;
    }

    @Override
    public Net getTaggedNet() {
        return net;
    }

    @Override
    public void release() {
        producer.removePlaceEventListener(this);
        this.manager = null;
        this.producer = null;
        this.net = null;
        this.hitInstance = null;
    }

    @Override
    public String toString() {
        return description;
    }
}
