package de.renew.draw.storables.impl.util.internal;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

import de.renew.draw.storables.ontology.Drawing;
import de.renew.draw.storables.ontology.Storable;
import de.renew.util.ObjectInputStreamUsingBottomLoader;


/**
 * This class contains utility methods for convenience handling of {@code Storable}.
 *
 * @author cabac
 */
public class StorableHelper {
    private static final org.apache.log4j.Logger LOGGER =
        org.apache.log4j.Logger.getLogger(StorableHelper.class);

    /**
     * Deep clones the given Storable by writing to an OutputStream and creating a new Object from the InputStream.
     * Throws an Exception if cloning of the given Storable failed.
     *
     * @param original the Storable to be cloned
     * @param <S>      the type of the object being cloned, which has to extend {@link Storable}
     * @return a deep clone of the original Storable
     * @throws Exception throws Exception if Storable wasn't cloned successfully
     */
    @SuppressWarnings("unchecked")
    public static <S extends Storable> S cloneStorable(S original) throws Exception {
        try {
            ByteArrayOutputStream byteOutStream = new ByteArrayOutputStream();

            ObjectOutputStream objectOutStream = new ObjectOutputStream(byteOutStream);
            objectOutStream.writeObject(original);
            objectOutStream.flush();

            ByteArrayInputStream byteInStream =
                new ByteArrayInputStream(byteOutStream.toByteArray());
            ObjectInputStream objectInStream = new ObjectInputStreamUsingBottomLoader(byteInStream);

            return (S) objectInStream.readObject();
        } catch (Exception e) {
            String errorMsg = String.format(
                "Could not clone Storable%s",
                (original instanceof Drawing) ? ": " + ((Drawing) original).getName() : ".");

            LOGGER.error(errorMsg, e);
            throw new Exception(errorMsg, e);
        }
    }

}