package bndtools.launch.ui;

import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import org.bndtools.api.BndtoolsConstants;
import org.bndtools.api.ILogger;
import org.bndtools.api.Logger;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy;
import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Group;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.dialogs.ElementListSelectionDialog;
import org.eclipse.ui.dialogs.ElementTreeSelectionDialog;
import org.eclipse.ui.dialogs.ISelectionStatusValidator;
import org.eclipse.ui.model.WorkbenchContentProvider;
import org.eclipse.ui.model.WorkbenchLabelProvider;

import aQute.bnd.build.Project;
import aQute.bnd.build.Workspace;
import bndtools.Plugin;
import bndtools.central.Central;
import bndtools.launch.LaunchConstants;
import bndtools.utils.FileExtensionFilter;

public class ProjectLaunchTabPiece extends AbstractLaunchTabPiece {
    @SuppressWarnings("deprecation")
    private static final String ATTR_LOGLEVEL = LaunchConstants.ATTR_LOGLEVEL;
    private static final ILogger logger = Logger.getLogger(ProjectLaunchTabPiece.class);

    private static final String PROP_LAUNCH_TARGET = "targetName";
    private static final String PROP_ENABLE_TRACE = "enableTrace";

    // Model State
    private String targetName = null;
    private boolean enableTrace = false;

    // View
    private Text launchTargetTxt;
    private Button enableTraceBtn;

    @Override
    @SuppressWarnings("unused")
    public Control createControl(Composite parent) {
        Group projectGroup = new Group(parent, SWT.NONE);
        projectGroup.setText("Launch:");

        launchTargetTxt = new Text(projectGroup, SWT.BORDER);

        Button projectNameBrowseBtn = new Button(projectGroup, SWT.PUSH);
        projectNameBrowseBtn.setText("Browse Projects");

        new Label(projectGroup, SWT.NONE); // Spacer

        Button bndrunBrowseBtn = new Button(projectGroup, SWT.PUSH);
        bndrunBrowseBtn.setText("Browse Run Files");

        enableTraceBtn = new Button(projectGroup, SWT.CHECK);
        enableTraceBtn.setText("Enable launcher tracing.");

        // LISTENERS
        projectNameBrowseBtn.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                doBrowseProject();
            }
        });
        bndrunBrowseBtn.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                doBrowseBndrun();
            }
        });
        launchTargetTxt.addListener(SWT.Modify, new Listener() {
            @Override
            public void handleEvent(Event event) {
                String oldName = targetName;
                targetName = launchTargetTxt.getText();
                setDirty(true);
                firePropertyChange(PROP_LAUNCH_TARGET, oldName, targetName);
            }
        });
        enableTraceBtn.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                boolean oldValue = enableTrace;
                enableTrace = enableTraceBtn.getSelection();
                setDirty(true);
                firePropertyChange(PROP_ENABLE_TRACE, oldValue, enableTrace);
            }
        });

        // LAYOUT
        GridLayout layout = new GridLayout(2, false);
        layout.verticalSpacing = 0;
        layout.horizontalSpacing = 0;
        projectGroup.setLayout(layout);
        launchTargetTxt.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
        projectNameBrowseBtn.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
        bndrunBrowseBtn.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));

        return projectGroup;
    }

    void doBrowseProject() {
        ElementListSelectionDialog dialog = new ElementListSelectionDialog(launchTargetTxt.getShell(), new WorkbenchLabelProvider());
        dialog.setTitle("Project Selection");

        dialog.setMessage("Select a project to constrain your search.");

        List<IProject> projects = loadProjects();
        dialog.setElements(projects.toArray());

        if (Window.OK == dialog.open()) {
            IProject selected = (IProject) dialog.getFirstResult();
            launchTargetTxt.setText(selected.getName());
        }
    }

    void doBrowseBndrun() {
        ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(launchTargetTxt.getShell(), new WorkbenchLabelProvider(), new WorkbenchContentProvider());
        dialog.setValidator(new ISelectionStatusValidator() {
            @Override
            public IStatus validate(Object[] selection) {
                if (selection.length > 0 && selection[0] instanceof IFile) {
                    return new Status(IStatus.OK, Plugin.PLUGIN_ID, IStatus.OK, "", null); //$NON-NLS-1$
                }
                return new Status(IStatus.ERROR, Plugin.PLUGIN_ID, IStatus.ERROR, "", null); //$NON-NLS-1$
            }
        });
        dialog.setAllowMultiple(false);
        dialog.setTitle("Run File Selection");
        dialog.setMessage("Select the Run File to launch.");
        dialog.addFilter(new FileExtensionFilter(LaunchConstants.EXT_BNDRUN));
        dialog.setInput(ResourcesPlugin.getWorkspace());

        if (dialog.open() == Window.OK) {
            Object[] files = dialog.getResult();
            if (files != null && files.length == 1) {
                IPath path = ((IResource) files[0]).getFullPath()
                    .makeRelative();
                launchTargetTxt.setText(path.toString());
            } else {
                launchTargetTxt.setText("");
            }
        }
    }

    static List<IProject> loadProjects() {
        Collection<Project> projects;
        try {
            Workspace workspace = Central.getWorkspace();
            projects = workspace.getAllProjects();
        } catch (Exception e) {
            logger.logError("Internal error querying projects.", e);
            return Collections.emptyList();
        }
        List<IProject> result = new ArrayList<IProject>(projects.size());
        IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace()
            .getRoot();
        for (Project project : projects) {
            IProject iproject = workspaceRoot.getProject(project.getName());
            if (iproject != null && iproject.isOpen()) {
                result.add(iproject);
            }
        }
        return result;
    }

    @Override
    public void performApply(ILaunchConfigurationWorkingCopy configuration) {
        configuration.setAttribute(LaunchConstants.ATTR_LAUNCH_TARGET, launchTargetTxt.getText());

        IResource targetResource = ResourcesPlugin.getWorkspace()
            .getRoot()
            .findMember(launchTargetTxt.getText());
        if (targetResource != null && targetResource.exists()) {
            IProject project = targetResource.getProject();
            if (project != null)
                configuration.setAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, project.getName());
            else
                configuration.removeAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME);
        }

        configuration.setAttribute(LaunchConstants.ATTR_TRACE, enableTrace);
        configuration.removeAttribute(ATTR_LOGLEVEL);
    }

    @Override
    public void initializeFrom(ILaunchConfiguration configuration) throws CoreException {
        targetName = configuration.getAttribute(LaunchConstants.ATTR_LAUNCH_TARGET, (String) null);
        if (targetName == null)
            targetName = configuration.getAttribute(IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, (String) null);
        if (targetName != null) {
            launchTargetTxt.setText(targetName);
        }
        enableTrace = configuration.getAttribute(LaunchConstants.ATTR_TRACE, LaunchConstants.DEFAULT_TRACE);
        enableTraceBtn.setSelection(enableTrace);
    }

    @Override
    public String checkForError() {
        if (targetName == null || targetName.length() == 0) {
            return "Launch target must be specified";
        }

        IResource targetResource = ResourcesPlugin.getWorkspace()
            .getRoot()
            .findMember(targetName);
        if (targetResource == null || !targetResource.exists()) {
            return MessageFormat.format("Launch target {0} does not exist.", targetName);
        }

        if (targetResource.getType() == IResource.PROJECT) {
            IProject project = (IProject) targetResource;
            if (!project.isOpen()) {
                return MessageFormat.format("Project {0} is closed.", targetName);
            }
            try {
                if (!project.hasNature(BndtoolsConstants.NATURE_ID)) {
                    return MessageFormat.format("Project {0} is not a Bnd OSGi project.", targetName);
                }
            } catch (CoreException e) {
                logger.logError("Error checking for Bnd OSGi project nature", e);
                return "Error checking for Bnd OSGi project nature";
            }
        } else if (targetResource.getType() == IResource.FILE) {
            if (!targetResource.getName()
                .endsWith(LaunchConstants.EXT_BNDRUN)) {
                return MessageFormat.format("Selected file {0} is not a .bndrun file.", targetName);
            }
        }
        return null;
    }

    @Override
    public void setDefaults(ILaunchConfigurationWorkingCopy configuration) {
        configuration.setAttribute(LaunchConstants.ATTR_TRACE, LaunchConstants.DEFAULT_TRACE);
    }
}