/*
 * Created on Dec 27, 2004
 *
 */
package de.renew.shadowcompiler;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.net.URL;
import java.util.Iterator;

import org.apache.log4j.Logger;

import de.renew.simulatorontology.loading.Finder;
import de.renew.simulatorontology.shadow.ShadowNet;
import de.renew.simulatorontology.shadow.ShadowNetSystem;
import de.renew.util.ClassSource;
import de.renew.util.ObjectInputStreamUsingBottomLoader;
import de.renew.util.StringUtil;


/**
 * This class is responsible for finding compiled
 * shadow nets in either classpath relative (see
 * {@link #findNetClasspathRel(String, String)})
 * or file sources (see {@link #findNetFile(String, String)}).
 * <p>
 * <!-- copied from old DefaultShadowNetLoader -->
 * Additionally, the shadow net system stored in the file may
 * contain only one shadow net, which must have the same name as
 * the net and the file, too. This restriction exists to avoid
 * unexpected results from loading shadow nets out of files with
 * different names.
 * </p>
 * @author Till Kothe
 */
public class SNSFinder extends Finder {
    /** The logger for this class. */
    private static final Logger LOGGER = Logger.getLogger(SNSFinder.class);


    /**
     * An array of file name extensions that indicate serialized
     * {@code ShadowNetSystem} files.
     **/
    private final String[] _shadowExtensions = new String[] { ".sns", ".sha" };


    /* (non-Javadoc)
     * @see de.renew.simulatorontology.loading.Finder#findNetFile(java.lang.String)
     */
    @Override
    public ShadowNetSystem findNetFile(String name, String path) {
        int ext = 0; // counter for Shadow net extensions
        String fullFileName;
        boolean error = true;
        URL url = null;

        while (error && (ext < _shadowExtensions.length)) {
            // First step: Check whether the
            // source file exists and create URL for it.
            error = false;

            fullFileName = path + _shadowExtensions[ext];
            LOGGER.debug("SNSFinder: looking for: " + fullFileName);
            try {
                File file = new File(fullFileName);
                if (file.canRead()) {
                    url = file.toURI().toURL();
                } else {
                    error = true;
                }
            } catch (Exception e) {
                LOGGER.error("SNSFinder: " + fullFileName + " caused " + e);
                LOGGER.debug(e.getMessage(), e);
                error = true;
            }


            // Second step: If we have a URL to an existing
            // source file, read, load and return it.
            if (!error) {
                try {
                    ShadowNetSystem netSystem = loadNetSystemFromURL(url, name);

                    if (netSystem != null) {
                        return netSystem;
                    }
                } catch (Exception e) {
                    LOGGER.error("SNSFinder: " + url + " caused " + e);
                    LOGGER.debug(e.getMessage(), e);
                    error = true;
                }
            }
            ext++;
        }

        return null;
    }

    /* (non-Javadoc)
     * @see de.renew.simulatorontology.loading.Finder#findNetClasspathRel(java.lang.String)
     */
    @Override
    public ShadowNetSystem findNetClasspathRel(String name, String path) {
        int ext = 0;
        String fullFileName;
        boolean error = true;

        while (error && (ext < _shadowExtensions.length)) {
            // First step: Generate URL from net path, net
            // name and extension. Also check whether the
            // source file exists.
            error = false;

            fullFileName = path + _shadowExtensions[ext];
            LOGGER.debug("SNSFinder: looking for: " + fullFileName);
            URL url =
                ClassSource.getClassLoader().getResource(StringUtil.convertToSlashes(fullFileName));
            error = (url == null);


            // Second step: If we have a URL to an existing
            // source file, read, load and return it.
            if (!error) {
                ShadowNetSystem netSystem;
                try {
                    netSystem = loadNetSystemFromURL(url, name);
                    if (netSystem != null) {
                        return netSystem;
                    }
                } catch (Exception e) {
                    LOGGER.error("SNSFinder: " + url + " caused " + e);
                    LOGGER.debug(e.getMessage(), e);
                    error = true;
                }
            }
            ext++;
        }

        return null;
    }

    /**
     * Load a net with the given name from the given URL.
     *
     * @param url the url from where to load the net system.
     * @param name the name of the net to load. It is used to verify whether
     *             the net system really contains the net that is requested.
     * @return the loaded {@code ShadowNetSystem} if successful, otherwise
     *         {@code null}
     * @throws IOException if an I/O error occurs
     **/
    private ShadowNetSystem loadNetSystemFromURL(URL url, String name)
        throws IOException, ClassNotFoundException
    {
        LOGGER.debug("Loading shadow net from " + url);
        InputStream stream = url.openStream();
        ObjectInput input = new ObjectInputStreamUsingBottomLoader(stream);
        ShadowNetSystem netSystem = (ShadowNetSystem) input.readObject();
        input.close();

        Iterator<ShadowNet> nets = netSystem.elements().iterator();
        ShadowNet net;
        if (nets.hasNext()) {
            net = nets.next();
            if (nets.hasNext()) {
                LOGGER.error("SNSFinder: " + url + " contains more than one shadow net. Ignored.");
            } else if (net.getName().equals(name)) {
                // looks good. let's return the net system.
                LOGGER.debug("SNSFinder: found net " + name + " at " + url);
                return netSystem;
            }
        }
        LOGGER.warn("SNSFinder: " + url + " was empty!? Ignored.");

        return null;
    }
}