package de.renew.net.serialisation;

import java.io.IOException;
import java.io.ObjectOutput;
import java.util.Collection;
import java.util.Optional;

import de.renew.net.INetLookup;
import de.renew.net.Net;
import de.renew.simulator.api.ISimulationLockExecutor;
import de.renew.util.RenewObjectOutputStream;

/**
 * A {@code NetSerializer} can be used to write all known {@code Net} instances to an {@link ObjectOutput} 
 * (see {@link #saveAllKnownNets(ObjectOutput)}).
 */
public class NetSerializer implements INetSerializer {

    private final ISimulationLockExecutor _lockExecutor;
    private final INetLookup _netLookup;

    /**
     * Creates a new {@code NetSerializer} that will use the given {@code InheritableSimulationThreadLock}
     * to ensure that all actions are performed mutually exclusive.
     *
     * @param lockExecutor will be used to ensure that all actions are performed mutually exclusive
     * @param netLookup the {@code INetLookup} to use to find nets that should be saved.
     */
    public NetSerializer(ISimulationLockExecutor lockExecutor, INetLookup netLookup) {
        _lockExecutor = lockExecutor;
        _netLookup = netLookup;
    }

    @Override
    public void saveAllKnownNets(ObjectOutput outputStream) throws IOException {
        _lockExecutor.lock();
        try {
            Optional<RenewObjectOutputStream> renewObjectOutputStream = Optional.empty();
            if (outputStream instanceof RenewObjectOutputStream) {
                renewObjectOutputStream = Optional.of((RenewObjectOutputStream) outputStream);
            }
            renewObjectOutputStream.ifPresent(stream -> stream.beginDomain(Net.class));

            // Save all currently known nets.
            Collection<Net> allKnownNets = _netLookup.getAllKnownNets();
            outputStream.writeInt(allKnownNets.size());
            for (Net net : allKnownNets) {
                outputStream.writeObject(net);
            }

            // If a RenewObjectOutputStream is used, write
            // all delayed fields NOW.
            if (renewObjectOutputStream.isPresent()) {
                renewObjectOutputStream.get().writeDelayedObjects();
                renewObjectOutputStream.get().endDomain(Net.class);
            }

        } finally {
            _lockExecutor.unlock();
        }
    }
}
