package de.renew.remote;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;

import de.renew.engine.thread.SimulationThreadPool;
import de.renew.net.PlaceInstance;


/**
 * Implements the TokenCountsAccessor interface,
 * representing the token counts of a place instance as a snapshot.
 * Its contents will not change even if transitions fire.
 *
 * @author Thomas Jacob
 */
public class TokenCountsAccessorImpl extends UnicastRemoteObject implements TokenCountsAccessor {
    /**
     * A logger instance for the {@code TokenCountsAccessorImpl} class.
     * This logger is used to manage logging operations such as
     * debugging and error reporting for this class. It is initialized
     * with the class type {@code TokenCountsAccessorImpl} to enable
     * context-specific logging.
     */
    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(TokenCountsAccessorImpl.class);

    /**
     * The number of free tokens.
     */
    private int _freeTokenCount;

    /**
     * The number of tested tokens.
     */
    private int _testedTokenCount;

    /**
     * Creates a new TokenCountsAccessorImpl.
     * @param placeInstance The place instance for the marking accessor.
     * @exception java.rmi.RemoteException if an RMI failure occured.
     */
    public TokenCountsAccessorImpl(final PlaceInstance placeInstance) throws RemoteException {
        super(0, SocketFactoryDeterminer.getInstance(), SocketFactoryDeterminer.getInstance());

        Future<Object> future =
            SimulationThreadPool.getCurrent().submitAndWait(new Callable<Object>()
            {
                @Override
                public Object call() throws Exception {
                    placeInstance._lock.lock();
                    try {
                        _freeTokenCount = placeInstance.getNumberOfTokens();
                        _testedTokenCount = placeInstance.getNumberOfTestedTokens();
                    } finally {
                        placeInstance._lock.unlock();
                    }
                    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);
        }
    }

    /**
     * Return the number of free tokens in this marking.
     * Even if two tokens are equal, they are both counted individually.
     * Currently tested tokens are not included in this count.
     *
     * @return the number of free tokens
     * @exception java.rmi.RemoteException if an RMI failure occured.
     */
    @Override
    public int getFreeTokenCount() throws RemoteException {
        return _freeTokenCount;
    }

    /**
     * Return the number of currently tested tokens in this marking.
     *
     * @return the number of tested tokens
     * @exception java.rmi.RemoteException if an RMI failure occured.
     */
    @Override
    public int getTestedTokenCount() throws RemoteException {
        return _testedTokenCount;
    }

    /**
     * Returns whether the place instance has no tokens.
     *
     * @return Whether the place instance is empty.
     * @exception java.rmi.RemoteException if an RMI failure occured.
     */
    @Override
    public boolean isEmpty() throws RemoteException {
        return _freeTokenCount + _testedTokenCount <= 0;
    }
}