package de.renew.formalism.fs;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import de.renew.engine.searchqueue.SearchQueue;
import de.renew.engine.thread.SimulationThreadPool;
import de.renew.net.Net;
import de.renew.net.NetInstanceImpl;
import de.renew.net.NetLookup;
import de.renew.net.Place;
import de.renew.net.SimulatablePlaceInstance;
import de.renew.simulatorontology.loading.NetNotFoundException;
import de.renew.unify.Impossible;


/**
 * A net instance that can be initialized with a predefined marking.
 */
public class ObjectNetInstance extends NetInstanceImpl {

    /**
     * Creates an ObjectNetInstance for a net given by its name and initializes
     * it with the specified marking.
     *
     * @param netName the name of the net to instantiate
     * @param marking a mapping of place names to tokens to initialize the net
     * @throws Impossible if the initialization fails
     * @throws NetNotFoundException if no definition for the net could be found
     */
    protected ObjectNetInstance(String netName, Hashtable<String, Vector<Object>> marking)
        throws Impossible, NetNotFoundException
    {
        this(new NetLookup().findForName(netName), marking);
    }

    /**
     * Creates an ObjectNetInstance for the given net and initializes
     * it with the specified marking.
     *
     * @param net the net to instantiate
     * @param marking a mapping of place names to token values
     * @throws Impossible if the initialization fails
     */
    protected ObjectNetInstance(final Net net, final Hashtable<String, Vector<Object>> marking)
        throws Impossible
    {
        super(net);
        Future<Object> future =
            SimulationThreadPool.getCurrent().submitAndWait(new Callable<Object>()
            {
                @Override
                public Object call() throws Exception {
                    // Build hashtable of place names.
                    Hashtable<String, Place> nameToPlace = new Hashtable<String, Place>();
                    synchronized (net) {
                        Iterator<Place> iterator = net.places().iterator();
                        while (iterator.hasNext()) {
                            Place place = iterator.next();
                            nameToPlace.put(place.toString(), place);
                        }
                    }


                    // Transfer tokens.
                    Enumeration<String> enumeration = marking.keys();
                    while (enumeration.hasMoreElements()) {
                        String name = enumeration.nextElement();
                        SimulatablePlaceInstance instance =
                            (SimulatablePlaceInstance) getInstance(nameToPlace.get(name));

                        Enumeration<Object> tokens = (marking.get(name)).elements();
                        while (tokens.hasMoreElements()) {
                            // This used to be done silently. As there should be no 
                            // listeners yet, it can be done openly just as well.
                            instance.insertToken(tokens.nextElement(), SearchQueue.getTime());
                        }
                    }
                    return null;
                }
            });
        try {
            future.get();
        } catch (InterruptedException e) {
            _logger.error("Timeout while waiting for simulation thread to finish", e);
        } catch (ExecutionException e) {
            _logger.error("Simulation thread threw an exception", e);
        }
    }
}