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.simulator.Binding;
import de.renew.engine.thread.SimulationThreadPool;
import de.renew.simulatorontology.simulation.SimulationEnvironment;
import de.renew.simulatorontology.simulation.StepIdentifier;


/**
 * Implements the BindingAccessor interface.
 * The binding accessor wraps bindings to provide the
 * functionality to list and fire bindings from the client.
 *
 * @author Thomas Jacob
 */
public class BindingAccessorImpl extends UnicastRemoteObject implements BindingAccessor {
    /**
     * A static logger instance used for logging messages in the BindingAccessorImpl class.
     * This logger is configured using the Log4j framework and is intended to capture
     * diagnostic and informational messages specific to the operations of the BindingAccessorImpl class.
     */
    public static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(BindingAccessorImpl.class);

    /**
     * The simulation environment this object is situated in.
     */
    private SimulationEnvironment _environment;

    /**
     * The object for the accessor.
     */
    protected final Binding _binding;

    /**
     * Creates a new BindingAccessor.
     * @param binding The binding for the accessor.
     * @param environment The simulation environment this accessor belongs to.
     * @exception java.rmi.RemoteException if an RMI failure occured.
     */
    public BindingAccessorImpl(Binding binding, SimulationEnvironment environment)
        throws RemoteException
    {
        super(0, SocketFactoryDeterminer.getInstance(), SocketFactoryDeterminer.getInstance());
        this._binding = binding;
        this._environment = environment;
    }

    /**
     * Executes the binding.
     * @param asynchronous Whether to execute the binding asynchronously.
     * @exception java.rmi.RemoteException if an RMI failure occured.
     */
    @Override
    public boolean execute(final boolean asynchronous) throws RemoteException {
        final StepIdentifier stepIdentifier = _environment.getSimulator().nextStepIdentifier();
        Future<Boolean> future =
            SimulationThreadPool.getCurrent().submitAndWait(new Callable<Boolean>()
            {
                @Override
                public Boolean call() throws Exception {
                    return _binding.execute(stepIdentifier, asynchronous);
                }
            });
        try {
            return 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);
        }

        // We should never return nothing but some error occured befor.
        return false;

    }

    /**
     * Returns the description of the binding.
     * @return The description.
     * @exception java.rmi.RemoteException if an RMI failure occured.
     */
    @Override
    public String getDescription() throws RemoteException {
        Future<String> future =
            SimulationThreadPool.getCurrent().submitAndWait(new Callable<String>()
            {
                @Override
                public String call() throws Exception {
                    return _binding.getDescription();
                }
            });
        try {
            return 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);
        }

        // We should never return nothing but some error occured befor.
        return null;
    }
}