/* GraphBundleSaver.java
 * =========================================================================
 * This file is part of the GrInvIn project - http://www.grinvin.org
 * 
 * Copyright (C) 2005-2008 Universiteit Gent
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 * 
 * A copy of the GNU General Public License can be found in the file
 * LICENSE.txt provided with the source distribution of this program (see
 * the META-INF directory in the source jar). This license can also be
 * found on the GNU website at http://www.gnu.org/licenses/gpl.html.
 * 
 * If you did not receive a copy of the GNU General Public License along
 * with this program, contact the lead developer, or write to the Free
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

package org.grinvin.io.graphs;

import java.io.IOException;
import java.io.OutputStream;
import java.util.Properties;
import java.util.zip.ZipOutputStream;

import org.grinvin.graphs.AnnotationView;
import org.grinvin.graphs.EmbeddingView;
import org.grinvin.graphs.GraphBundleView;
import org.grinvin.graphs.GraphView;
import org.grinvin.gui.icons.DefaultGraphIconFactory;
import org.grinvin.gui.icons.GraphIconFactory;
import org.grinvin.io.InvariantValuesSaver;
import org.grinvin.io.SectionSaver;
import org.grinvin.io.ZipOutputStreamSectionSaver;

/**
 * Saves a {@link GraphBundleView} onto an output stream.
 */
public class GraphBundleSaver {
    
    //
    private final GraphBundleView bundle;
    
    //
    private final SectionSaver ssaver;
    
    //
    private final String path;
    
    /**
     * Default constructor.
     */
    private GraphBundleSaver(GraphBundleView bundle, SectionSaver ssaver) {
        this(bundle, ssaver, null);
    }
    
    private GraphBundleSaver(GraphBundleView bundle, SectionSaver ssaver, String path) {
        this.bundle = bundle;
        this.ssaver = ssaver;
        this.path = path;
    }
    
    //
    private static final String ZIPFILE_COMMENT =
            "Graph bundle -- Saved by org.grinvin.io.GraphBundleSaver";
    
    //
    private static final Class<DefaultGraphIconFactory>
            DEFAULT_GRAPH_ICON_FACTORY_CLASS = DefaultGraphIconFactory.class;
    
    /**
     * Create the entry with the given name.
     */
    private OutputStream createEntry(String name) throws IOException {
        if (path != null)
            return ssaver.createSection(path + "/" + name);
        else
            return ssaver.createSection(name);
    }
    
    /**
     * Save the meta-information for the graph.
     */
    private void saveMeta() throws IOException {
        Properties meta = new Properties();
        meta.setProperty("version", "1.1");
        meta.setProperty("nrOfEmbeddings", Integer.toString(bundle.getEmbeddingCount()));
        meta.setProperty("nrOfAnnotations", Integer.toString(bundle.getAnnotationCount()));
        GraphIconFactory gif = bundle.getGraphIconFactory();
        if (gif != null) {
            Class<?> gifClass = gif.getClass();
            if (!DEFAULT_GRAPH_ICON_FACTORY_CLASS.equals(gifClass))
                meta.setProperty("graphIconFactory", gifClass.getName());
        }
        OutputStream out = createEntry("meta-info.xml");
        meta.storeToXML(out, null);
    }
    
    /**
     * Save the graph properties.
     */
    private void saveProperties() throws IOException {
        OutputStream out = createEntry("resources.xml");
        bundle.getProperties().save(out);
    }
    
    /**
     * Save the abstract graph.
     */
    private void saveGraph() throws IOException {
        GraphView graph = bundle.getGraph();
        OutputStream out = createEntry("graph.xml");
        GraphSaver.save(graph, out);
    }
    
    /**
     * Save the embedding with the given index.
     */
    private void saveEmbedding(int index) throws IOException {
        EmbeddingView embedding = bundle.getEmbedding(index);
        String name = "embedding_" + index + ".xml";
        OutputStream out = createEntry(name);
        EmbeddingSaver.save(embedding, out);
    }

    /**
     * Save the annotation with the given index.
     */
    private void saveAnnotation(int index) throws IOException {
        AnnotationView annotation = bundle.getAnnotation(index);
        String name = "annotation_" + index + ".xml";
        OutputStream out = createEntry(name);
        AnnotationSaver.save(annotation, out);
    }
    
    //
    private void saveInvariantValues() throws IOException {
        OutputStream out = createEntry("invariantvalues.xml");
        InvariantValuesSaver.save(bundle.getInvariantValues(), out);
    }
    
    /**
     * Save the bundle and close the outputstream.
     */
    private void save() throws IOException {
        saveProperties();
        saveGraph();
        saveMeta();
        for (int i=0; i < bundle.getEmbeddingCount(); i++)
            saveEmbedding(i);
        for (int i=0; i < bundle.getAnnotationCount(); i++)
            saveAnnotation(i);
        saveInvariantValues();
    }
    
    /**
     * Save the bundle onto the output stream.
     * @param bundle Bundle which should be saved
     * @param out Output stream to which the bundle should be written
     */
    public static void saveToZip(GraphBundleView bundle, OutputStream out) throws IOException {
        ZipOutputStream zip = new ZipOutputStream(out);
        zip.setComment(ZIPFILE_COMMENT);
        GraphBundleSaver saver = new GraphBundleSaver(bundle, new ZipOutputStreamSectionSaver(zip));
        saver.save();
        zip.finish();
        out.close();
    }
    
    public static void save(GraphBundleView bundle, SectionSaver ssaver, String path) throws IOException {
        GraphBundleSaver saver = new GraphBundleSaver(bundle, ssaver, path);
        saver.save();
    }
    
}
