/*******************************************************************************
 * Copyright (c) 2009 Andrey Loskutov.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * Contributor:  Andrey Loskutov - initial API and implementation
 *******************************************************************************/
package de.loskutov.anyedit.actions;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.dialogs.MessageDialogWithToggle;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.ui.IEditorInput;

import de.loskutov.anyedit.AnyEditToolsPlugin;
import de.loskutov.anyedit.IAnyEditConstants;
import de.loskutov.anyedit.Messages;
import de.loskutov.anyedit.util.EclipseUtils;

public class SaveToFileAction extends AbstractOpenAction {

    private static final int CANCEL = -1;

    private static final int APPEND = 0;

    private static final int OVERRIDE = 1;

    private static String lastUsedFile;

    public SaveToFileAction() {
        super();
    }

    @Override
    protected void handleAction(IDocument doc, ISelectionProvider selectionProvider,
            IEditorInput currentInput) {

        IPreferenceStore prefs = AnyEditToolsPlugin.getDefault().getPreferenceStore();
        boolean shouldAsk = prefs.getBoolean(IAnyEditConstants.SAVE_TO_SHOW_OPTIONS);
        boolean shouldOpenEditor = prefs
                .getBoolean(IAnyEditConstants.SAVE_TO_OPEN_EDITOR);
        boolean ignoreSelection = prefs
                .getBoolean(IAnyEditConstants.SAVE_TO_IGNORE_SELECTION);
        ITextSelection selection = (ITextSelection) selectionProvider.getSelection();
        boolean hasSelection = false;
        if (!ignoreSelection && selection != null && selection.getLength() > 0) {
            hasSelection = true;
        } else {
            selection = new TextSelection(doc, 0, doc.getLength());
        }

        /*
         * Show dialog if prefs is set, asking for open in editor
         */
        if (shouldAsk) {
            MessageDialogWithToggle dialogWithToggle = MessageDialogWithToggle
                    .openYesNoCancelQuestion(AnyEditToolsPlugin.getShell(),
                            Messages.SaveTo_ShouldOpen,
                            hasSelection ? Messages.SaveTo_MessageSelection
                                    : Messages.SaveTo_MessageNoSelection,
                                    Messages.SaveTo_MessageToggle, false, prefs,
                                    IAnyEditConstants.SAVE_TO_SHOW_OPTIONS);

            int returnCode = dialogWithToggle.getReturnCode();
            if (returnCode != IDialogConstants.YES_ID
                    && returnCode != IDialogConstants.NO_ID) {
                return;
            }
            shouldOpenEditor = returnCode == IDialogConstants.YES_ID;
            if (!prefs.getBoolean(IAnyEditConstants.SAVE_TO_SHOW_OPTIONS)) {
                prefs.setValue(IAnyEditConstants.SAVE_TO_OPEN_EDITOR, shouldOpenEditor);
            }
        }

        /*
         * open file selection dialog (external)
         */
        File file = getFileFromUser();
        if (file == null) {
            return;
        }

        /*
         * if selected file exists, ask for override/append/another file
         */
        int overrideOrAppend = checkForExisting(file);
        if (overrideOrAppend == CANCEL) {
            return;
        }

        IFile iFile = EclipseUtils.getIFile(new Path(file.getAbsolutePath()));
        /*
         * if selected file is in the workspace, checkout it or show error message
         */
        if (iFile == null || !checkout(iFile, overrideOrAppend)) {
            return;
        }

        /*
         * save it
         */
        doSave(doc, selection, file, overrideOrAppend);

        /*
         * and if option is on, open in editor
         */
        if (shouldOpenEditor) {
            new DefaultOpenEditorParticipant().openEditor(doc, selectionProvider,
                    currentInput, iFile);
        }
    }

    private void doSave(IDocument doc, ITextSelection selection, File file,
            int overrideOrAppend) {

        int startSelection = selection.getOffset();
        int stopSelection = startSelection + selection.getLength();
        int endLine = selection.getEndLine();

        FileWriter fw;
        try {
            fw = new FileWriter(file, overrideOrAppend == APPEND);
        } catch (IOException e) {
            AnyEditToolsPlugin.errorDialog("Couldn't open file for writing: " + file, e);
            return;
        }

        try {
            for (int i = selection.getStartLine(); i <= endLine; i++) {
                IRegion lineInfo = doc.getLineInformation(i);

                boolean useStartLineOffset = lineInfo.getOffset() > startSelection;
                int startIndex;
                if (useStartLineOffset) {
                    startIndex = 0;
                } else {
                    startIndex = startSelection - lineInfo.getOffset();
                }

                int stopIndex;
                boolean useStopLineOffset = lineInfo.getOffset() + lineInfo.getLength() > stopSelection;
                if (useStopLineOffset) {
                    stopIndex = stopSelection - lineInfo.getOffset();
                } else {
                    stopIndex = lineInfo.getLength();
                }
                String line = doc.get(lineInfo.getOffset() + startIndex, stopIndex
                        - startIndex);

                if (line == null) {
                    continue;
                }
                fw.write(line);
                String lineDelimiter = doc.getLineDelimiter(doc
                        .getLineOfOffset(stopIndex));
                if (lineDelimiter != null) {
                    fw.write(lineDelimiter);
                }
                fw.flush();
            }
        } catch (Exception e) {
            AnyEditToolsPlugin.errorDialog("Error during writing to file: " + file, e);
        } finally {
            try {
                fw.close();
            } catch (IOException e) {
                AnyEditToolsPlugin.logError("Couldn't close file: " + file, e);
            }
        }

    }

    /**
     * @param file
     *            non null
     * @param overrideOrAppend
     * @return true if file doesn't exist and was created or writable
     */
    private boolean checkout(IFile file, int overrideOrAppend) {
        if (file.getLocation() == null) {
            File file2 = new File(file.getFullPath().toOSString());
            if (!file2.exists()) {
                try {
                    file2.createNewFile();
                } catch (IOException e) {
                    AnyEditToolsPlugin.errorDialog("Couldn't create file: " + file, e);
                    return false;
                }
            }
            boolean canWrite = file2.canWrite();
            if (!canWrite) {
                AnyEditToolsPlugin.errorDialog("File is read-only: " + file);
            }
            return canWrite;
        }
        try {
            if (overrideOrAppend == APPEND && file.exists()) {
                file.appendContents(new ByteArrayInputStream(new byte[0]), true, true,
                        new NullProgressMonitor());
            } else {
                if (file.exists()) {
                    file.delete(true, new NullProgressMonitor());
                }
                file.create(new ByteArrayInputStream(new byte[0]), true,
                        new NullProgressMonitor());
            }
        } catch (CoreException e) {
            AnyEditToolsPlugin.errorDialog("File is read-only: " + file, e);
            return false;
        }
        return true;
    }

    /**
     * @param file
     *            non null
     * @return OVERRIDE if file not exists or exists and may be overriden, APPEND if it
     *         exists and should be reused, CANCEL if action should be cancelled
     */
    private int checkForExisting(File file) {
        if (file.exists()) {
            MessageDialog md = new MessageDialog(AnyEditToolsPlugin.getShell(),
                    Messages.SaveTo_ShouldOpen, null, Messages.SaveTo_FileExists,
                    MessageDialog.WARNING, new String[] { Messages.SaveTo_Append,
                Messages.SaveTo_Override, "Cancel" }, 0);
            int result = md.open();
            switch (result) {
            case APPEND: // Append btn index
                return APPEND;
            case OVERRIDE: // Override btn index
                return OVERRIDE;
            default:
                return CANCEL;
            }
        }
        return OVERRIDE;
    }

    private File getFileFromUser() {
        FileDialog fd = new FileDialog(AnyEditToolsPlugin.getShell(), SWT.SAVE);
        if (lastUsedFile == null) {
            String property = System.getProperty("user.home");
            fd.setFilterPath(property);
        } else {
            fd.setFileName(lastUsedFile);
        }
        fd.setFilterExtensions(new String[] { "*.log", "*.txt" });
        String fileStr = fd.open();
        if (fileStr != null) {
            lastUsedFile = fileStr;
            return new File(fileStr);
        }
        return null;
    }

}
