/* WorkspaceSaver.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;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ListIterator;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipOutputStream;

import org.grinvin.util.IOHelper;
import org.grinvin.workspace.Workspace;
import org.grinvin.workspace.WorkspaceException;

import org.jdom.DocType;
import org.jdom.Element;

/**
 * Saves workspaces to disk.
 */
public class WorkspaceSaver {
    
    //
    private static final Logger LOGGER
            = Logger.getLogger("org.grinvin.workspace", "org.grinvin.workspace.resources");
    
    //
    private static final String ZIPFILE_COMMENT =
            "Grinvin Workspace -- Saved by org.grinvin.io.WorkspaceSaver";
    
    //
    private Workspace workspace;
    
    //
    private SectionSaver ssaver;
    
    //
    private WorkspaceSaver(Workspace workspace, SectionSaver ssaver) {
        this.workspace = workspace;
        this.ssaver = ssaver;
    }
    
    private void saveMeta() throws IOException {
        Properties meta = new Properties();
        meta.setProperty("version", "1.1");
        OutputStream out = ssaver.createSection("meta-info.xml");
        meta.storeToXML(out, null);
    }

    
    /**
     * Save the workspace into the current workspace directory.
     */
    private void save() throws WorkspaceException, IOException {
        Element root = new Element("workspace");
        // in reverse order
        for (ListIterator<Subsystem> iter = workspace.subsystems.listIterator(workspace.subsystems.size());
        iter.hasPrevious(); )
            iter.previous().save(root, ssaver);
        DocType docType = new DocType("workspace", "-//GrInvIn IO//Workspace 1.1//EN", "http://downloads.grinvin.org/dtds/io/workspace-1.1.dtd");
        LoaderSaverHelper.outputXML(root, docType, ssaver.createSection("workspace.xml"));
        saveMeta();
    }
    
    /**
     * Save the workspace into a new workspace file. If the location
     * already exists, it is replaced.
     */
    public static void saveAs(Workspace workspace, File location) throws WorkspaceException {
        workspace.setLocation(location);
        save(workspace);
        LOGGER.log(Level.INFO, "log.created.workspace", location);
    }
    
    public static void save(Workspace workspace) throws WorkspaceException {
        File file = workspace.getLocation();
        assert file != null : "save called without location";
        if (file.isDirectory()) {
            saveToDirectory(workspace, file);
        } else {
            saveToZipFile(workspace, file);
        }
    }
        
    private static void saveToZipFile(Workspace workspace, File file) throws WorkspaceException {
        try {
            FileOutputStream out = new FileOutputStream(file);
            ZipOutputStream zip = new ZipOutputStream(out);
            zip.setComment(ZIPFILE_COMMENT);
            WorkspaceSaver saver = new WorkspaceSaver(workspace, new ZipOutputStreamSectionSaver(zip));
            saver.save();
            zip.finish();
            out.close();
            LOGGER.log(Level.INFO, "log.saved.workspace", file);
        } catch (IOException ex) {
            throw new WorkspaceException("I/O error while saving workspace", file, ex);
        }
    }
    
    private static void saveToDirectory(Workspace workspace, File directory) throws WorkspaceException {
        if (directory.exists()) {
            if (! IOHelper.deleteDirectory(directory))
                throw new WorkspaceException("Directory could not be removed", directory);
        }
        if (! directory.mkdir())
            throw new WorkspaceException("Could not create directory", directory);
        
        WorkspaceSaver saver = new WorkspaceSaver(workspace, new DirectorySectionSaver(directory));
        try {
            saver.save();
        } catch (IOException ex) {
            throw new WorkspaceException("I/O error while saving workspace", directory, ex);
        }
        LOGGER.log(Level.INFO, "log.created.workspace", directory);
    }
}
