/*
 * Copyright 2006-2021 DLR, Germany
 * 
 * SPDX-License-Identifier: EPL-1.0
 * 
 * https://rcenvironment.de/
 */

package de.rcenvironment.core.gui.wizards.toolintegration;

import java.io.File;
import java.util.LinkedList;
import java.util.List;

import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CLabel;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.DirectoryDialog;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

import de.rcenvironment.core.component.model.impl.ToolIntegrationConstants;
import de.rcenvironment.core.gui.resources.api.ImageManager;
import de.rcenvironment.core.gui.resources.api.StandardImages;
import de.rcenvironment.core.utils.common.StringUtils;

/**
 * A dialog for inserting a copy command in pre/post script.
 * 
 * @author Sascha Zur
 */
public class WizardInsertCopyCommandDialog extends Dialog {

    private static final String REQUIRED = " (required)";

    private static final String FILE = "File";

    private static final String SLASH = "/";

    private static final String DIRECTORY_PREFIX = "Directory: ";

    private static final String INPUT_PREFIX = "Input ";

    private static final String TARGET_STRING = "*TARGET*";

    private static final String SOURCE_STRING = "*SOURCE*";

    private static final String SHUTIL_COPYTREE_STRING = StringUtils
        .format("shutil.copytree(\"%s\", \"%s\")", SOURCE_STRING, TARGET_STRING);

    private static final String SHUTIL_COPY_STRING = StringUtils.format("shutil.copy(\"%s\", \"%s\")", SOURCE_STRING, TARGET_STRING);

    private static final int SOURCE = 0;

    private static final int TARGET = 1;

    private Button fileToFileButton;

    private Button dirButton;

    private String command;

    private final List<String> inputs;

    private final String[] directories;

    private Button fileToDirButton;

    private Button[] predefOptionButtons;

    private Button[] customOptionButtons;

    private Combo[] predefCombos;

    private Text[] predefTexts;

    private Text[] customTexts;

    private Button[] customChooseButtons;

    private Label previewCommandLabel;

    private Label[] slashLabel;

    private CLabel validationLabel;

    public WizardInsertCopyCommandDialog(Shell parentShell, List<String> endpointNames, String[] directories) {
        super(parentShell);
        this.inputs = endpointNames;
        this.directories = directories;
        setShellStyle(SWT.CLOSE | SWT.MAX | SWT.TITLE | SWT.BORDER | SWT.RESIZE
            | getDefaultOrientation());
    }

    @Override
    protected void configureShell(Shell shell) {
        super.configureShell(shell);
        shell.setText(Messages.copyDialogShellText);
        command = SHUTIL_COPY_STRING;
    }

    @Override
    protected Control createDialogArea(Composite parent) {
        Composite container = (Composite) super.createDialogArea(parent);
        container.setLayout(new GridLayout(1, false));
        GridData g = new GridData(GridData.FILL_BOTH | GridData.GRAB_HORIZONTAL);
        container.setLayoutData(g);
        Composite typeContainer = new Composite(container, SWT.NONE);
        typeContainer.setLayout(new GridLayout(5, false));
        Label sourceType = new Label(typeContainer, SWT.NONE);
        sourceType.setText(Messages.copy);
        GridData labelData = new GridData();
        labelData.horizontalSpan = 1;
        sourceType.setLayoutData(labelData);
        fileToFileButton = new Button(typeContainer, SWT.RADIO);
        fileToFileButton.setText(Messages.copyFileToFile);
        GridData fileData = new GridData();
        fileToFileButton.setLayoutData(fileData);
        fileToFileButton.setSelection(true);
        fileToDirButton = new Button(typeContainer, SWT.RADIO);
        fileToDirButton.setText(Messages.copyFileToDir);
        GridData fileToDirData = new GridData();
        fileToDirButton.setLayoutData(fileToDirData);

        dirButton = new Button(typeContainer, SWT.RADIO);
        dirButton.setText(Messages.copyDirToDir);
        GridData dirData = new GridData();
        dirData.horizontalSpan = 2;
        dirButton.setLayoutData(dirData);

        predefOptionButtons = new Button[2];
        customOptionButtons = new Button[2];
        predefCombos = new Combo[2];
        predefTexts = new Text[2];
        slashLabel = new Label[2];
        customTexts = new Text[2];
        customChooseButtons = new Button[2];

        Label separator = new Label(container, SWT.HORIZONTAL | SWT.SEPARATOR);
        separator.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        buildUpElements(SOURCE, container);
        Label separator3 = new Label(container, SWT.HORIZONTAL | SWT.SEPARATOR);
        separator3.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
        separator3.setVisible(false);
        buildUpElements(TARGET, container);

        Label separator2 = new Label(container, SWT.HORIZONTAL | SWT.SEPARATOR);
        separator2.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

        Label previewLabel = new Label(container, SWT.NONE);
        previewLabel.setText(Messages.insertCommand);
        previewCommandLabel = new Label(container, SWT.NONE);
        previewCommandLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));

        validationLabel = new CLabel(container, SWT.NONE);
        validationLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.GRAB_HORIZONTAL));
        validationLabel.setVisible(false);
        validationLabel.setImage(ImageManager.getInstance().getSharedImage(StandardImages.ERROR_16));

        return container;
    }

    private Text buildUpElements(int type, Composite container) {
        Label label = new Label(container, SWT.NONE);
        if (type == 0) {
            label.setText(Messages.source);
        } else {
            label.setText(Messages.target);
        }
        GridData data = new GridData();
        label.setLayoutData(data);

        Composite composite = new Composite(container, SWT.NONE);
        composite.setLayout(new GridLayout(5, false));
        GridData compData = new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL);
        compData.horizontalIndent = 10;
        composite.setLayoutData(compData);

        Button predefPathButton = new Button(composite, SWT.RADIO);
        predefPathButton.setText(Messages.predefinedPath);
        GridData pathButtonData = new GridData();
        pathButtonData.horizontalSpan = 5;
        predefPathButton.setLayoutData(pathButtonData);
        predefPathButton.setSelection(true);

        predefOptionButtons[type] = predefPathButton;

        Combo combo = new Combo(composite, SWT.READ_ONLY);
        GridData comboData = new GridData();
        fillCombo(combo, type);
        combo.setLayoutData(comboData);
        predefCombos[type] = combo;

        Label slash = new Label(composite, SWT.NONE);
        slash.setText(SLASH);
        GridData slashData = new GridData();
        slashData.widthHint = 5;
        slash.setLayoutData(slashData);
        slashLabel[type] = slash;
        Text predefText = new Text(composite, SWT.BORDER);
        GridData predefTextData = new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL);
        predefTextData.horizontalSpan = 3;
        predefText.setLayoutData(predefTextData);
        predefText.setMessage(Messages.subfolderOrFilename);
        predefTexts[type] = predefText;

        Button customPathButton = new Button(composite, SWT.RADIO);
        customPathButton.setText(Messages.customPath);
        GridData customButtonData = new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL);
        customButtonData.horizontalSpan = 5;
        customPathButton.setLayoutData(customButtonData);
        customOptionButtons[type] = customPathButton;

        Text text = new Text(composite, SWT.BORDER);
        GridData sourceTextData = new GridData(GridData.GRAB_HORIZONTAL | GridData.FILL_HORIZONTAL);
        sourceTextData.horizontalSpan = 4;
        text.setLayoutData(sourceTextData);
        text.addModifyListener(new ModifyListener() {

            @Override
            public void modifyText(ModifyEvent arg0) {
                updateSelectionAndValidateInput();
            }
        });
        customTexts[type] = text;
        Button selectionButton = new Button(composite, SWT.PUSH);
        selectionButton.setText(" ... ");
        selectionButton.addSelectionListener(new FileDirButtonChooser(text));
        customChooseButtons[type] = selectionButton;

        return text;
    }

    private void fillCombo(Combo combo, int type) {
        String selection = combo.getText();
        combo.removeAll();
        for (String input : inputs) {
            if (input.startsWith(FILE) && type == SOURCE && (fileToFileButton.getSelection() || fileToDirButton.getSelection())) {
                combo.add(INPUT_PREFIX + input);
            } else if (input.startsWith(FILE) && type == TARGET && fileToFileButton.getSelection()) {
                combo.add(INPUT_PREFIX + input);
            }
        }
        for (String dir : directories) {
            combo.add(DIRECTORY_PREFIX + dir);
        }
        if (selection != null && combo.indexOf(selection) >= 0) {
            combo.select(combo.indexOf(selection));
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void create() {
        super.create();
        installListeners();
        updateSelectionAndValidateInput();
    }

    private void installListeners() {
        UpdateListener ul = new UpdateListener();
        fileToFileButton.addSelectionListener(ul);
        fileToDirButton.addSelectionListener(ul);
        dirButton.addSelectionListener(ul);
        for (int i = 0; i < 2; i++) {
            predefOptionButtons[i].addSelectionListener(ul);
            customOptionButtons[i].addSelectionListener(ul);
            predefCombos[i].addSelectionListener(ul);
            customChooseButtons[i].addSelectionListener(ul);
        }
        for (int i = 0; i < 2; i++) {
            predefTexts[i].addModifyListener(ul);
            customTexts[i].addModifyListener(ul);
        }
    }

    protected void updateSelectionAndValidateInput() {
        for (int i = 0; i < 2; i++) {
            customTexts[i].setEnabled(!predefOptionButtons[i].getSelection());
            customChooseButtons[i].setEnabled(!predefOptionButtons[i].getSelection());
            predefCombos[i].setEnabled(predefOptionButtons[i].getSelection());
            predefTexts[i].setEnabled(predefOptionButtons[i].getSelection());
            if (predefCombos[i].getText() != null && predefCombos[i].getText().startsWith(INPUT_PREFIX + FILE)) {
                predefTexts[i].setVisible(false);
                slashLabel[i].setVisible(false);
            } else {
                predefTexts[i].setVisible(true);
                slashLabel[i].setVisible(true);
            }

        }
        if (fileToFileButton.getSelection()) {
            predefTexts[SOURCE].setMessage(Messages.subfolderOrFilename + REQUIRED);
            predefTexts[TARGET].setMessage(Messages.subfolderOrFilename);
        } else if (fileToDirButton.getSelection()) {
            predefTexts[SOURCE].setMessage(Messages.subfolderOrFilename + REQUIRED);
            predefTexts[TARGET].setMessage(Messages.subfolderOrFilename.substring(0, Messages.subfolderOrFilename.indexOf(SLASH)));
        } else if (dirButton.getSelection()) {
            predefTexts[SOURCE].setMessage(Messages.subfolderOrFilename.substring(0, Messages.subfolderOrFilename.indexOf(SLASH)));
            predefTexts[TARGET].setMessage(Messages.subfolderOrFilename.substring(0, Messages.subfolderOrFilename.indexOf(SLASH))
                + REQUIRED);
        }
        boolean isSourceValid = true;
        List<String> validationMessages = new LinkedList<>();

        if (predefOptionButtons[SOURCE].getSelection()) {
            if (predefCombos[SOURCE].getText() == null || predefCombos[SOURCE].getText().isEmpty()) {
                isSourceValid = false;
                validationMessages.add("Relative source path not valid.");
            }
            if (fileToFileButton.getSelection() || fileToDirButton.getSelection()) {
                if (predefTexts[SOURCE].getText() == null || predefTexts[SOURCE].getText().isEmpty()
                    && !predefCombos[SOURCE].getText().startsWith(INPUT_PREFIX + FILE)) {
                    isSourceValid = false;
                    validationMessages.add("Relative source file not valid.");
                }
            }
        } else {
            if (customTexts[SOURCE].getText() == null || customTexts[SOURCE].getText().isEmpty()) {
                isSourceValid = false;
                validationMessages.add("Absolute source path not valid.");
            } else if (!(new File(customTexts[SOURCE].getText())).isAbsolute()) {
                isSourceValid = false;
                validationMessages.add("Source path is not absolute.");
            }
        }

        boolean isTargetValid = true;
        if (predefOptionButtons[TARGET].getSelection()) {
            if (predefCombos[TARGET].getText() == null || predefCombos[TARGET].getText().isEmpty()) {
                isTargetValid = false;
                validationMessages.add("Relative target path not valid.");
            }
            if (dirButton.getSelection()) {
                if (predefTexts[TARGET].getText() == null || predefTexts[TARGET].getText().isEmpty()) {
                    isTargetValid = false;
                    validationMessages.add("Relative target directory not valid.");

                }
            }
        } else {
            if (customTexts[TARGET].getText() == null || customTexts[TARGET].getText().isEmpty()) {
                isTargetValid = false;
                validationMessages.add("Absolute target path not valid.");
            } else if (!(new File(customTexts[TARGET].getText())).isAbsolute()) {
                isTargetValid = false;
                validationMessages.add("Target path is not absolute.");
            }
        }
        if (!validationMessages.isEmpty()) {
            validationLabel.setForeground(Display.getCurrent().getSystemColor(SWT.COLOR_RED));
            validationLabel.setText(validationMessages.get(0));
            validationLabel.setVisible(true);
            validationLabel.pack();
        } else {
            validationLabel.setVisible(false);
        }
        boolean isValid = isSourceValid && isTargetValid;
        buildCommand(isValid);
        getButton(IDialogConstants.OK_ID).setEnabled(isValid);
    }

    private void buildCommand(boolean isValid) {
        if (dirButton.getSelection()) {
            command = SHUTIL_COPYTREE_STRING;
        } else {
            command = SHUTIL_COPY_STRING;
        }
        if (predefOptionButtons[SOURCE].getSelection()) {
            String slash = SLASH;
            if (predefCombos[SOURCE].getText() != null && predefCombos[SOURCE].getText().startsWith(INPUT_PREFIX + FILE)) {
                slash = "";
            }
            command =
                command.replace(SOURCE_STRING, getPlaceholder(predefCombos[SOURCE].getText()) + slash + predefTexts[SOURCE].getText());
        } else {
            command = command.replace(SOURCE_STRING, customTexts[SOURCE].getText());
        }

        if (predefOptionButtons[TARGET].getSelection()) {
            String slash = SLASH;
            if (predefCombos[TARGET].getText() != null && predefCombos[TARGET].getText().startsWith(INPUT_PREFIX + FILE)) {
                slash = "";
            }
            command =
                command.replace(TARGET_STRING, getPlaceholder(predefCombos[TARGET].getText()) + slash + predefTexts[TARGET].getText());
        } else {
            command = command.replace(TARGET_STRING, customTexts[TARGET].getText());
        }
        if (isValid) {
            previewCommandLabel.setText(command);
        } else {
            previewCommandLabel.setText("-");
        }

    }

    private String getPlaceholder(String selection) {
        if (selection != null && !selection.isEmpty()) {
            String name = selection.substring(selection.indexOf(":") + 2);
            String insertString = "${%s:%s}";
            if (selection.startsWith(INPUT_PREFIX)) {
                insertString = StringUtils.format(insertString, "in", name);
            } else if (selection.startsWith(DIRECTORY_PREFIX)) {
                int i = 0;
                while (!name.equals(ToolIntegrationConstants.DIRECTORIES_PLACEHOLDERS_DISPLAYNAMES[i])) {
                    i++;
                }
                if (i < ToolIntegrationConstants.DIRECTORIES_PLACEHOLDER.length) {
                    insertString = StringUtils.format(insertString, "dir", ToolIntegrationConstants.DIRECTORIES_PLACEHOLDER[i]);
                }
            }
            return insertString;
        }
        return "";
    }

    public String getCopyCommand() {
        return command;
    }

    /**
     * Listener for updating the dialog.
     * 
     * @author Sascha Zur
     */
    private class UpdateListener implements SelectionListener, ModifyListener {

        @Override
        public void widgetSelected(SelectionEvent arg0) {
            if (arg0.getSource().equals(fileToDirButton) || arg0.getSource().equals(fileToFileButton)
                || arg0.getSource().equals(dirButton)) {
                fillCombo(predefCombos[SOURCE], SOURCE);
                fillCombo(predefCombos[TARGET], TARGET);
            }
            updateSelectionAndValidateInput();
        }

        @Override
        public void widgetDefaultSelected(SelectionEvent arg0) {
            widgetSelected(arg0);
        }

        @Override
        public void modifyText(ModifyEvent arg0) {
            updateSelectionAndValidateInput();
        }
    }

    /**
     * Opens the correct dialog for the file or directory choose buttons.
     * 
     * @author Sascha Zur
     */
    private class FileDirButtonChooser implements SelectionListener {

        private final Text text;

        FileDirButtonChooser(Text text) {
            this.text = text;
        }

        @Override
        public void widgetDefaultSelected(SelectionEvent arg0) {
            widgetSelected(arg0);
        }

        @Override
        public void widgetSelected(SelectionEvent arg0) {
            String result = "";
            if (fileToFileButton.getSelection() || fileToDirButton.getSelection() && arg0.getSource().equals(customChooseButtons[SOURCE])) {
                FileDialog f = new FileDialog(getShell());
                result = f.open();

            } else {
                DirectoryDialog d = new DirectoryDialog(getShell());
                result = d.open();
            }
            if (result != null) {
                text.clearSelection();
                text.setText(result.replaceAll("\\\\", "/"));
                text.forceFocus();

            }
        }
    }
}
