/*--------------------------------------------------------------*
  Copyright (C) 2006-2015 OpenSim Ltd.

  This file is distributed WITHOUT ANY WARRANTY. See the file
  'License' for details on this and other legal matters.
*--------------------------------------------------------------*/

package org.omnetpp.scave.editors;

import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.Status;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.XMLMemento;
import org.omnetpp.common.util.StringUtils;
import org.omnetpp.scave.ScavePlugin;


/**
 * State of the scave editor that is saved when the editor is closed
 * and restored when opened.
 * <p>
 * It is saved in persistent properties of the input file of the editor.
 *
 * @author tomi
 */
public class ScaveEditorMemento implements IMemento
{
    public static final String ZOOM = "Zoom";

    /* Name of the root element of the XML memento */
    private static final String ROOT_ELEMENT = "ScaveState";

    /* Names and keys used for persistent properties*/
    private static final String SCAVE_STATE = "ScaveState";
    private static final String CHUNK_COUNT = "ScaveStateChunkCount";
    private static final String CHUNK = "ScaveStateChunk";
    private static final String QUALIFIER = ScavePlugin.PLUGIN_ID;
    private static final QualifiedName KEY_CHUNK_COUNT = new QualifiedName(QUALIFIER, CHUNK_COUNT);
    private static final QualifiedName KEY_SCAVE_STATE = new QualifiedName(QUALIFIER, SCAVE_STATE);
    // keys that can be used with this memento
    private String[] mementoKeys = {ZOOM, ScaveNavigationLocation.TAG_PAGE_ID,
            ScaveNavigationLocation.TAG_TEXT, ScaveNavigationLocation.TAG_PAGE_MEMENTO,
            ScaveEditor.PAGE, ScaveEditor.PAGE_ID, ScaveEditor.ACTIVE_PAGE };

    /* The IMemento methods are delegated to this XMLMemento */
    private XMLMemento memento;

    public ScaveEditorMemento() {
        memento = XMLMemento.createWriteRoot(ROOT_ELEMENT);
    }

    public ScaveEditorMemento(IFile file) throws CoreException {
        load(file);
    }

    public IMemento createChild(String type, String id) {
        return memento.createChild(type, id);
    }

    public IMemento createChild(String type) {
        return memento.createChild(type);
    }

    public boolean equals(Object obj) {
        return memento.equals(obj);
    }

    public IMemento getChild(String type) {
        return memento.getChild(type);
    }

    public IMemento[] getChildren() {
        // XXX this should delegate to memento.getChildren() on eclipse 4.2
        throw new UnsupportedOperationException("getChildren() is not supported in ScaveEditorMemento");
    }

    public IMemento[] getChildren(String type) {
        return memento.getChildren(type);
    }

    public Float getFloat(String key) {
        return memento.getFloat(key);
    }

    public String getID() {
        return memento.getID();
    }

    public Integer getInteger(String key) {
        return memento.getInteger(key);
    }

    public Boolean getBoolean(String key) {
        return memento.getBoolean(key);
    }

    public String getString(String key) {
        return memento.getString(key);
    }

    public String getTextData() {
        return memento.getTextData();
    }

    public int hashCode() {
        return memento.hashCode();
    }

    public void putFloat(String key, float f) {
        memento.putFloat(key, f);
    }

    public void putInteger(String key, int n) {
        memento.putInteger(key, n);
    }

    public void putBoolean(String key, boolean value) {
        memento.putBoolean(key, value);
    }

    public void putMemento(IMemento memento) {
        memento.putMemento(memento);
    }

    public void putString(String key, String value) {
        memento.putString(key, value);
    }

    public void putTextData(String data) {
        memento.putTextData(data);
    }

    public void save(IFile file) throws CoreException {
        try {
            StringWriter writer = new StringWriter();
            memento.save(writer);
            String[] chunks = StringUtils.split(writer.toString(), 2000);
            if (chunks.length == 1) {
                file.setPersistentProperty(KEY_SCAVE_STATE, chunks[0]);
            }
            else {
                file.setPersistentProperty(KEY_CHUNK_COUNT, String.valueOf(chunks.length));
                for (int i = 0; i < chunks.length; ++i) {
                    QualifiedName key = new QualifiedName(QUALIFIER, CHUNK+"."+i);
                    file.setPersistentProperty(key, chunks[i]);
                }
            }
        } catch (Exception e) {
            IStatus error = new Status(IStatus.ERROR, ScavePlugin.PLUGIN_ID, 0,
                                    "Cannot save Scave editor state.", e);
            throw new CoreException(error);
        }
    }

    protected void load(IFile file) throws CoreException {
        try {
            String state = file.getPersistentProperty(KEY_SCAVE_STATE);

            if (state == null) {
                int count = getPersistentIntProperty(file, KEY_CHUNK_COUNT);
                if (count >= 0 && count < 100) { // limit state to ~200k
                    String[] chunks = new String[count];
                    for (int i = 0; i < count; ++i) {
                        QualifiedName key = new QualifiedName(QUALIFIER, CHUNK+"."+i);
                        String chunk = file.getPersistentProperty(key);
                        if (chunk == null)
                            throw new Exception("Scave editor state is corrupted.");
                        chunks[i] = chunk;
                    }
                    state = StringUtils.join(chunks);
                }
                else if (count != -1)
                    throw new Exception("Scave editor state is corrupted.");
            }
            if (state != null) {
                Reader reader = new StringReader(state);
                memento = XMLMemento.createReadRoot(reader);
            }
        } catch (Exception e) {
            IStatus error = new Status(IStatus.ERROR, ScavePlugin.PLUGIN_ID, 0,
                    "Cannot load Scave editor state.", e);
            throw new CoreException(error);
        }

        if (memento == null) {
            IStatus warning = new Status(IStatus.WARNING, ScavePlugin.PLUGIN_ID, 0,
                    String.format("No Scave editor state was saved for file: %s.", file.getLocation().toOSString()), null);
            throw new CoreException(warning);
        }
    }

    public String toString() {
        return memento.toString();
    }

    private int getPersistentIntProperty(IFile file, QualifiedName key) throws CoreException {
        try {
            return Integer.parseInt(file.getPersistentProperty(key));
        } catch (NumberFormatException e) {
            return -1;
        }
    }

    public String[] getAttributeKeys() {
        return mementoKeys;
    }

    public String getType() {
        return "ScaveEditorMemento";
    }


}
