package bndtools.wizards.workspace;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.jar.Attributes;

import org.bndtools.api.ILogger;
import org.bndtools.api.Logger;
import org.bndtools.core.ui.icons.Icons;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.jface.window.Window;
import org.eclipse.jface.wizard.WizardPage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Image;
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.FileDialog;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableColumn;
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 org.eclipse.ui.plugin.AbstractUIPlugin;
import org.osgi.framework.Constants;

import aQute.bnd.osgi.Jar;
import bndtools.Plugin;
import bndtools.types.Pair;
import bndtools.utils.FileExtensionFilter;

public class AddFilesToRepositoryWizardPage extends WizardPage {
    private static final ILogger logger = Logger.getLogger(AddFilesToRepositoryWizardPage.class);

    private final Image jarImg = Icons.desc("jar")
        .createImage();
    private final Image warnImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "/icons/warning_obj.gif")
        .createImage();
    private final Image errorImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "/icons/error.gif")
        .createImage();
    private final Image okayImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "/icons/tick.png")
        .createImage();

    private final Map<File, Pair<String, String>> bsnMap = new HashMap<File, Pair<String, String>>();
    private final List<File> files = new ArrayList<File>(1);

    private TableViewer viewer;

    public AddFilesToRepositoryWizardPage(String pageName) {
        super(pageName);
    }

    public void setFiles(File[] files) {
        this.files.clear();
        for (File file : files) {
            analyseFile(file);
            this.files.add(file);
        }

        if (viewer != null && !viewer.getControl()
            .isDisposed()) {
            viewer.refresh();
            validate();
        }
    }

    public List<File> getFiles() {
        return files;
    }

    void analyseFile(File file) {
        try (Jar jar = new Jar(file)) {
            Attributes attribs = jar.getManifest()
                .getMainAttributes();
            String bsn = attribs.getValue(Constants.BUNDLE_SYMBOLICNAME);
            String version = attribs.getValue(Constants.BUNDLE_VERSION);

            bsnMap.put(file, Pair.newInstance(bsn, version));
        } catch (Exception e) {
            logger.logError("Error reading JAR file content", e);
        }
    }

    @Override
    @SuppressWarnings("unused")
    public void createControl(Composite parent) {
        setTitle("Add Files to Repository");

        Composite composite = new Composite(parent, SWT.NONE);

        new Label(composite, SWT.NONE).setText("Selected files:");
        new Label(composite, SWT.NONE); // Spacer;
        Table table = new Table(composite, SWT.FULL_SELECTION | SWT.MULTI | SWT.BORDER);
        table.setLinesVisible(true);
        table.setHeaderVisible(true);

        TableColumn col;
        col = new TableColumn(table, SWT.NONE);
        col.setText("Path");
        col.setWidth(300);
        col = new TableColumn(table, SWT.NONE);
        col.setText("Bundle Name/Version");
        col.setWidth(300);

        viewer = new TableViewer(table);
        viewer.setContentProvider(new ArrayContentProvider());
        viewer.setLabelProvider(new StyledCellLabelProvider() {
            @Override
            public void update(ViewerCell cell) {
                File file = (File) cell.getElement();
                Pair<String, String> bundleId = bsnMap.get(file);

                int index = cell.getColumnIndex();
                if (index == 0) {
                    if (bundleId == null) {
                        cell.setImage(errorImg);
                    } else {
                        cell.setImage(jarImg);
                    }
                    StyledString label = new StyledString(file.getName());
                    String parentPath = file.getParent();
                    if (parentPath != null) {
                        label.append(" (" + parentPath + ")", StyledString.QUALIFIER_STYLER);
                    }
                    cell.setText(label.getString());
                    cell.setStyleRanges(label.getStyleRanges());
                } else if (index == 1) {
                    if (bundleId == null) {
                        cell.setImage(errorImg);
                        cell.setText("Not a JAR file");
                    } else {
                        String bsn = bundleId.getFirst();
                        String version = bundleId.getSecond();
                        if (bsn == null) {
                            cell.setImage(warnImg);
                            cell.setText("Not a Bundle JAR");
                        } else {
                            cell.setImage(okayImg);
                            StyledString styledString = new StyledString(bsn);
                            if (version != null) {
                                styledString.append(" [" + version + "]", StyledString.COUNTER_STYLER);
                                cell.setText(styledString.getString());
                                cell.setStyleRanges(styledString.getStyleRanges());
                            }
                        }
                    }
                }
            }
        });
        viewer.setInput(files);
        validate();

        final Button btnAdd = new Button(composite, SWT.PUSH);
        btnAdd.setText("Add JARs...");

        final Button btnAddExternal = new Button(composite, SWT.PUSH);
        btnAddExternal.setText("Add External JARs...");

        final Button btnRemove = new Button(composite, SWT.NONE);
        btnRemove.setText("Remove");
        btnRemove.setEnabled(false);

        // LISTENERS
        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
            @Override
            public void selectionChanged(SelectionChangedEvent event) {
                btnRemove.setEnabled(!viewer.getSelection()
                    .isEmpty());
            }
        });
        btnAdd.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                doAdd();
            }
        });
        btnAddExternal.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                doAddExternal();
            }
        });
        btnRemove.addSelectionListener(new SelectionAdapter() {
            @Override
            public void widgetSelected(SelectionEvent e) {
                doRemove();
            }
        });

        // LAYOUT
        composite.setLayout(new GridLayout(2, false));
        table.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 4));
        btnAdd.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
        btnRemove.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));

        setControl(composite);
    }

    void doAdd() {
        ElementTreeSelectionDialog dialog = new ElementTreeSelectionDialog(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(true);
        dialog.setTitle("JAR File Selection");
        dialog.addFilter(new FileExtensionFilter("jar")); //$NON-NLS-1$
        dialog.setInput(ResourcesPlugin.getWorkspace()
            .getRoot());

        if (dialog.open() == Window.OK) {
            Object[] result = dialog.getResult();
            List<File> added = new ArrayList<File>(result.length);
            for (Object fileObj : result) {
                IFile ifile = (IFile) fileObj;
                File file = ifile.getLocation()
                    .toFile();
                analyseFile(file);
                files.add(file);
                added.add(file);
            }
            if (!added.isEmpty()) {
                viewer.add(added.toArray());
                validate();
            }
        }
    }

    void doAddExternal() {
        FileDialog dialog = new FileDialog(getShell(), SWT.OPEN | SWT.MULTI);
        dialog.setFilterExtensions(new String[] {
            "*.jar" //$NON-NLS-1$
        });
        String res = dialog.open();
        if (res != null) {
            IPath filterPath = new Path(dialog.getFilterPath());

            String[] fileNames = dialog.getFileNames();
            List<File> added = new ArrayList<File>(fileNames.length);
            for (String fileName : fileNames) {
                added.add(filterPath.append(fileName)
                    .toFile());
            }
            if (!added.isEmpty()) {
                for (File addedFile : added) {
                    analyseFile(addedFile);
                    files.add(addedFile);
                }
                viewer.add(added.toArray());
                validate();
            }
        }
    }

    void doRemove() {
        IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
        if (!selection.isEmpty()) {
            for (Iterator<?> iter = selection.iterator(); iter.hasNext();) {
                Object item = iter.next();
                files.remove(item);
                viewer.remove(item);
            }
            validate();
        }
    }

    void validate() {
        String error = null;
        String warning = null;

        for (File file : files) {
            Pair<String, String> pair = bsnMap.get(file);
            if (pair == null) {
                error = "One or more selected files is not a JAR.";
            } else {
                String bsn = pair.getFirst();
                if (bsn == null) {
                    warning = "One or more selected files is not a Bundle JAR";
                }
            }
        }

        setErrorMessage(error);
        setMessage(warning, WARNING);
        setPageComplete(!files.isEmpty() && error == null);
    }

    @Override
    public void dispose() {
        super.dispose();
        jarImg.dispose();
        warnImg.dispose();
        errorImg.dispose();
        okayImg.dispose();
    }
}