package de.renew.net.serialisation;

import java.io.IOException;
import java.io.ObjectInput;
import java.io.StreamCorruptedException;

import org.apache.log4j.Logger;

import de.renew.net.INetLookup;
import de.renew.net.Net;
import de.renew.util.RenewObjectInputStream;

/**
 * Implementation for reading {@link Net} instances from an {@link java.io.OutputStream}.
 */
public class NetDeserializer implements INetDeserializer {

    private static final Logger LOGGER = Logger.getLogger(NetDeserializer.class);
    private static final String COPY_POSTFIX = "_COPY";
    private final INetLookup _netLookup;

    /**
     * Creates a new {@code NetDeserializer} using the given {@code INetLookup} to make the loaded nets known.
     *
     * @param netLookup the {@code INetLookup} to make the loaded nets known
     */
    public NetDeserializer(INetLookup netLookup) {
        _netLookup = netLookup;
    }

    @Override
    public void loadNets(ObjectInput objectInput) throws IOException, ClassNotFoundException {
        // Read all stored Nets.
        int count = objectInput.readInt();
        try {
            for (int i = 0; i < count; i++) {
                Net net = (Net) de.renew.util.ClassSource.readObject(objectInput);
                readNet(net, objectInput);
                _netLookup.makeNetKnown(net);
            }
        } catch (ClassCastException e) {
            LOGGER.debug(e.getMessage(), e);
            throw new StreamCorruptedException(
                "Object other than Net found " + "when looking for nets: " + e.getMessage());
        }

        // If a RenewObjectInputStream is used, read
        // all delayed fields NOW.
        if (objectInput instanceof RenewObjectInputStream renewObjectInputStream) {
            renewObjectInputStream.readDelayedObjects();
        }
    }

    /**
     * If the {@code copiousBehaviour} flag of the
     * {@link RenewObjectInputStream} is active, the net checks for name
     * clashes before it makes itself known to {@code Net.forName()}.
     * If a clash is detected, the net name is extended by hash symbols
     * until it becomes unique.
     *
     * @param net the already deserialized net
     * @param inputStream the stream to read objects from
     **/
    private void readNet(Net net, ObjectInput inputStream) {
        if (inputStream instanceof RenewObjectInputStream rIn) {
            if (!rIn.isCopiousBehaviour()) {
                return;
            }
            StringBuilder newName = new StringBuilder(net.getName());
            while (_netLookup.isKnownNet(newName.toString())) {
                newName.append(COPY_POSTFIX);
            }
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug(
                    "Deserialized Net copy: changed name from " + net.getName() + " to " + newName
                        + ".");
            }
            net.setName(newName.toString());
        }
    }
}
