package bndtools.model.repo;

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Collections;
import java.util.List;

import org.bndtools.api.ILogger;
import org.bndtools.api.Logger;
import org.bndtools.core.ui.icons.Icons;
import org.bndtools.utils.jface.HyperlinkStyler;
import org.bndtools.utils.repos.RepoUtils;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.StyledCellLabelProvider;
import org.eclipse.jface.viewers.StyledString;
import org.eclipse.jface.viewers.ViewerCell;
import org.eclipse.swt.graphics.Image;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.plugin.AbstractUIPlugin;

import aQute.bnd.build.Project;
import aQute.bnd.service.Actionable;
import aQute.bnd.service.IndexProvider;
import aQute.bnd.service.RepositoryPlugin;
import bndtools.Plugin;

public class RepositoryTreeLabelProvider extends StyledCellLabelProvider implements ILabelProvider {
    private static final ILogger logger = Logger.getLogger(RepositoryTreeLabelProvider.class);

    final Image arrowImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "icons/arrow_down.png")
        .createImage();
    final Image localRepoImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "icons/database.png")
        .createImage();
    final Image remoteRepoImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "icons/database_link.png")
        .createImage();
    final Image bundleImg = Icons.desc("bundle")
        .createImage();
    final Image matchImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "icons/star-small.png")
        .createImage();
    final Image projectImg = PlatformUI.getWorkbench()
        .getSharedImages()
        .getImage(IDE.SharedImages.IMG_OBJ_PROJECT);
    final Image loadingImg = AbstractUIPlugin.imageDescriptorFromPlugin(Plugin.PLUGIN_ID, "icons/loading_16x16.gif")
        .createImage();

    private final boolean showRepoId;

    public RepositoryTreeLabelProvider(boolean showRepoId) {
        this.showRepoId = showRepoId;
    }

    @Override
    public void update(ViewerCell cell) {
        Object element = cell.getElement();
        int index = cell.getColumnIndex();

        if (element instanceof RepositoryPlugin) {
            if (index == 0) {
                RepositoryPlugin repo = (RepositoryPlugin) element;
                cell.setText(repo.getName());

                Image image;
                if (RepoUtils.isWorkspaceRepo(repo))
                    image = projectImg;
                else if (isRemoteRepo((RepositoryPlugin) element))
                    image = remoteRepoImg;
                else
                    image = localRepoImg;

                cell.setImage(image);
            }
        } else if (element instanceof Project) {
            if (index == 0) {
                @SuppressWarnings("resource")
                Project project = (Project) element;
                StyledString label = new StyledString(project.getName());
                if (showRepoId)
                    label.append("  [Workspace]", StyledString.QUALIFIER_STYLER);

                cell.setText(label.getString());
                cell.setStyleRanges(label.getStyleRanges());
                cell.setImage(projectImg);
            }
        } else if (element instanceof ProjectBundle) {
            if (index == 0) {
                StyledString label = new StyledString(((ProjectBundle) element).getBsn());
                if (showRepoId)
                    label.append("  [Workspace]", StyledString.QUALIFIER_STYLER);

                cell.setText(label.getString());
                cell.setStyleRanges(label.getStyleRanges());
                cell.setImage(bundleImg);
            }
        } else if (element instanceof RepositoryBundle) {
            if (index == 0) {
                RepositoryBundle bundle = (RepositoryBundle) element;
                StyledString label = new StyledString(bundle.getText());
                if (showRepoId)
                    label.append("  [" + bundle.getRepo()
                        .getName() + "]", StyledString.QUALIFIER_STYLER);
                cell.setText(label.getString());
                cell.setStyleRanges(label.getStyleRanges());
                cell.setImage(bundleImg);
            }
        } else if (element instanceof RepositoryBundleVersion) {
            if (index == 0) {
                RepositoryBundleVersion bundleVersion = (RepositoryBundleVersion) element;
                String versionText = bundleVersion.getText();

                if (versionText.contains(" \u21E9")) {
                    versionText = versionText.replaceAll(" \u21E9", "");
                    cell.setImage(arrowImg);
                }

                StyledString styledString = new StyledString(versionText, StyledString.COUNTER_STYLER);
                cell.setText(styledString.getString());
                cell.setStyleRanges(styledString.getStyleRanges());
            }
        } else if (element instanceof RepositoryResourceElement) {
            RepositoryResourceElement resourceElem = (RepositoryResourceElement) element;
            StyledString label = new StyledString();
            label.append(resourceElem.getIdentity())
                .append(" ");
            label.append(resourceElem.getVersionString(), StyledString.COUNTER_STYLER);

            cell.setText(label.getString());
            cell.setStyleRanges(label.getStyleRanges());
            cell.setImage(matchImg);
        } else if (element instanceof ContinueSearchElement) {
            StyledString label = new StyledString("Continue Search on repository...", new HyperlinkStyler());
            cell.setText(label.getString());
            cell.setStyleRanges(label.getStyleRanges());
        } else if (element instanceof LoadingContentElement) {
            cell.setText(element.toString());
            cell.setImage(loadingImg);
        } else if (element != null) {
            // Catch-all
            cell.setText(element.toString());
        }
    }

    private static boolean isRemoteRepo(RepositoryPlugin repository) {
        List<?> locations = Collections.emptyList();
        if (repository instanceof IndexProvider) {
            try {
                locations = ((IndexProvider) repository).getIndexLocations();
            } catch (Exception e) {
                logger.logError("Unable to get repository index list", e);
            }
        }

        for (Object locationObj : locations) {
            try {
                URI location;
                if (locationObj instanceof URI)
                    location = (URI) locationObj;
                else if (locationObj instanceof URL)
                    location = ((URL) locationObj).toURI();
                else
                    return false;
                String protocol = location.getScheme();
                if ("http".equals(protocol) || "https".equals(protocol))
                    return true;
            } catch (URISyntaxException e) {
                return false;
            }
        }

        return false;
    }

    @Override
    public void dispose() {
        super.dispose();
        arrowImg.dispose();
        localRepoImg.dispose();
        remoteRepoImg.dispose();
        bundleImg.dispose();
        matchImg.dispose();
    }

    @Override
    public Image getImage(Object element) {
        Image img = null;
        if (element instanceof RepositoryPlugin) {
            RepositoryPlugin repo = (RepositoryPlugin) element;
            if (RepoUtils.isWorkspaceRepo(repo))
                img = projectImg;
            else
                img = isRemoteRepo(repo) ? remoteRepoImg : localRepoImg;
        } else if (element instanceof Project) {
            img = projectImg;
        } else if (element instanceof ProjectBundle) {
            img = bundleImg;
        } else if (element instanceof RepositoryBundle) {
            img = bundleImg;
        } else if (element instanceof LoadingContentElement) {
            img = loadingImg;
        }

        return img;
    }

    @Override
    public String getText(Object element) {
        try {
            if (element instanceof Actionable) {
                return ((Actionable) element).title();
            } else if (element instanceof RepositoryPlugin) {
                return ((RepositoryPlugin) element).getName();
            } else if (element instanceof Project) {
                Project project = (Project) element;
                return project.getName();
            } else if (element instanceof ProjectBundle) {
                return ((ProjectBundle) element).getBsn();
            }
        } catch (Exception e) {
            // just take the default
        }
        return null;
    }

    /**
     * Return the text to be shown as a tooltip.
     * <p/>
     * TODO allow markdown to be used. Not sure how to create a rich text tooltip though. Would also be nice if we could
     * copy/paste from the tooltip like in the JDT.
     */
    @Override
    public String getToolTipText(Object element) {
        try {
            if (element instanceof Actionable)
                return ((Actionable) element).tooltip();
        } catch (Exception e) {
            // ignore, use default
        }
        return null;
    }
}
