/*******************************************************************************
 * This file is part of the Symfony eclipse plugin.
 *
 * (c) Robert Gruendler <r.gruendler@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 ******************************************************************************/
package com.dubture.symfony.ui.views;

import java.util.ArrayList;
import java.util.List;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.dltk.core.ISourceModule;
import org.eclipse.dltk.core.ModelException;
import org.eclipse.dltk.internal.core.ModelElement;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
import org.eclipse.jface.viewers.ViewerSorter;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.dialogs.FilteredTree;
import org.eclipse.ui.dialogs.PatternFilter;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.part.ViewPart;

import com.dubture.symfony.core.log.Logger;
import com.dubture.symfony.core.model.Bundle;
import com.dubture.symfony.core.model.Service;

@SuppressWarnings("restriction")
public class ServicePart extends ViewPart {

    private TreeViewer viewer;
    private List<ViewerFilter> filters = new ArrayList<ViewerFilter>();

    private PublicFilter publicFilter = new PublicFilter();
    private TagFilter tagFilter = new TagFilter();

    private Sorter sorter = new Sorter();

    private ServicesViewerActionGroup actionGroup;

    private IResourceChangeListener changeListener = new IResourceChangeListener() {

        @Override
        public void resourceChanged(IResourceChangeEvent event)
        {
            if (event.getType() == IResourceChangeEvent.POST_CHANGE ||
                    event.getType() == IResourceChangeEvent.PRE_DELETE) {

                updateViewer();
            }
        }
    };


    public ServicePart() {

    }

    @Override
    public void createPartControl(Composite parent)
    {

        parent.setLayout(new GridLayout(1, true));
        PatternFilter filter = new PatternFilter();
        filter.setIncludeLeadingWildcard(true);
        FilteredTree tree = new FilteredTree(parent, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL, filter, true);

        viewer = tree.getViewer();

        viewer.setContentProvider(new ServiceContentProviderFlatView());
        viewer.setLabelProvider(new ServiceLabelProvider());
        viewer.setSorter(sorter);
        
        Action openImplementation = new Action("Open implementation")
        {
            @Override
            public void run()
            {
                if(viewer.getSelection().isEmpty()) {
                    return;
                }
                
                if (viewer.getSelection() instanceof IStructuredSelection) {
                    IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
                    if (selection.getFirstElement() instanceof Service) {
                        Service service = (Service) selection.getFirstElement();
                        if (service.getSourceModule() != null) {
                            openServiceImplementation(service);
                        }
                    }                        
                }
            }
        };
        
        Action openDefinition = new Action("Open definition")
        {
            @Override
            public void run()
            {
                if(viewer.getSelection().isEmpty()) {
                    return;
                }
                
                if (viewer.getSelection() instanceof IStructuredSelection) {
                    IStructuredSelection selection = (IStructuredSelection) viewer.getSelection();
                    if (selection.getFirstElement() instanceof Service) {
                        Service service = (Service) selection.getFirstElement();
                        if (service.getSourceModule() != null) {
                            openServiceDefinition(service);
                        }
                    }                        
                }
            }
        };
        
        MenuManager mgr = new MenuManager();
        mgr.add(openImplementation);
        mgr.add(openDefinition);
        Menu menu = mgr.createContextMenu(viewer.getControl());
        
        mgr.addMenuListener(new IMenuListener()
        {
            
            @Override
            public void menuAboutToShow(IMenuManager manager)
            {
                if(viewer.getSelection().isEmpty()) {
                    return;
                }
            }
        });
        
        viewer.getControl().setMenu(menu);

//        viewer.addSelectionChangedListener(new ISelectionChangedListener() {
//            @Override
//            public void selectionChanged(SelectionChangedEvent event)
//            {
//                if (event.getSelection() instanceof IStructuredSelection) {
//                    IStructuredSelection selection = (IStructuredSelection) event.getSelection();
//
//                    if (selection.getFirstElement() instanceof Service) {
////                        detail.updateService((Service) (selection.getFirstElement()));
//                    }
//                }
//            }
//        });


        viewer.addDoubleClickListener(new IDoubleClickListener() {

            @Override
            public void doubleClick(DoubleClickEvent event)
            {
                if (event.getSelection() instanceof IStructuredSelection) {
                    IStructuredSelection selection = (IStructuredSelection) event.getSelection();
                    if (selection.getFirstElement() instanceof Service) {
                        Service service = (Service) selection.getFirstElement();
                        if (service.getSourceModule() != null) {
                            openServiceImplementation(service);
                        }
                    }
                }
            }
        });

//        detail = new ServiceDetailView(parent, SWT.NONE);
        update();

        actionGroup = new ServicesViewerActionGroup(this);
        IActionBars actionBar = getViewSite().getActionBars();
        actionGroup.fillActionBars(actionBar);
        actionBar.updateActionBars();

        ResourcesPlugin.getWorkspace().addResourceChangeListener(changeListener);


    }
    
    protected void openServiceDefinition(Service service) 
    {
        ISourceModule source = service.getSourceModule();
        IPath path = service.getPath();
        
        if (source == null || path == null) {
            Logger.log(Logger.WARNING, "Unable to open service definition " + service.getId());
            return;
        }
        
        openFile((IFile) source.getScriptProject().getProject().getFile(service.getPath().removeFirstSegments(1)));
    }
    
    protected void openFile(IFile file)
    {
        if (file.exists()) {
            try {
                IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
                IEditorDescriptor desc = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(file.getName());
                page.openEditor(new FileEditorInput(file), desc.getId());
            } catch (PartInitException e) {
                Logger.logException(e);
            }
        }
    }
    
    protected void openServiceImplementation(Service service)
    {
        try {
            openFile((IFile) service.getSourceModule().getUnderlyingResource());
        } catch (ModelException e) {
            Logger.logException(e);
        }
    }

    protected void updateTags() {

//        List<String> tags = SymfonyModelAccess.getDefault().findServiceTags(new Path("/"));
//        ServicePart.this.tags.removeAll();
//        for (String tag : tags) {
//            ServicePart.this.tags.add(tag);
//        }
//
//        ServicePart.this.tags.redraw();

    }

    protected void updateViewer() {

        Display.getDefault().asyncExec(new Runnable(){
            public void run() {
                if (viewer.getControl().isDisposed()) {
                    return;
                }

                IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
                viewer.setInput(projects);
                viewer.refresh();
            }
        });

    }

    protected void update() {

        updateTags();
        updateViewer();
    }

    @Override
    public void setFocus()
    {
        viewer.getControl().setFocus();
    }

    @Override
    public void dispose()
    {
        super.dispose();

        if (changeListener != null)
            ResourcesPlugin.getWorkspace().removeResourceChangeListener(changeListener);

    }

    public void switchFlatView()
    {
        viewer.setContentProvider(new ServiceContentProviderFlatView());
        updateViewer();
    }

    public void switchBundleView()
    {

        viewer.setContentProvider(new ServiceContentProviderBundleView());
        updateViewer();

    }

    public void setPublicFilter(boolean doFilter) {

        if (doFilter) {
            if (!filters.contains(publicFilter)) {
                filters.add(publicFilter);
            }
        } else {
            filters.remove(publicFilter);
        }

        viewer.setFilters((ViewerFilter[]) filters.toArray(new ViewerFilter[filters.size()]));
        viewer.refresh();

    }

    /**
    * true = ascending, false = descending
    * @param order
    */
    public void setSort(boolean order)
    {

        sorter.setOrder(order);
        viewer.setSorter(sorter);
        viewer.refresh();

    }

    private class PublicFilter extends ViewerFilter {

        @Override
        public boolean select(Viewer viewer, Object parentElement,
                Object element)
        {
            if (element instanceof Service) {
                Service service = (Service) element;
                return service.isPublic();
            }

            return true;
        }
    }

    private class TagFilter extends ViewerFilter {

        private String tag = "";


        @Override
        public boolean select(Viewer viewer, Object parentElement,
                Object element)
        {

            if (element instanceof Service) {
                Service service = (Service) element;
                return service.getStringTags().contains(tag);
            }

            return true;
        }

        public void setTag(String tag) {

            this.tag = tag;
        }
    }

    private class Sorter extends ViewerSorter {

        private boolean ascending = true;

        @SuppressWarnings("unchecked")
        @Override
        public int compare(Viewer viewer, Object e1, Object e2)
        {
            ModelElement left = null;
            ModelElement right = null;
            if (e1 instanceof Service && e2 instanceof Service) {
                left = (Service) e1;
                right = (Service) e2;
            } else if (e1 instanceof Bundle && e2 instanceof Bundle) {
                left = (Bundle) e1;
                right = (Bundle) e2;
            }

            if (left != null && right != null) {
                int result = getComparator().compare(left.getElementName(), right.getElementName());

                if (!ascending) {
                    result *= -1;
                }
                return result;
            }

            return super.compare(viewer, e1, e2);
        }

        public void setOrder(boolean order) {

            ascending = order;
        }
    }

    public void filterByTag(String text)
    {

        if (text != null && text.length() > 0) {

            if (filters.contains(tagFilter))
                filters.remove(tagFilter);

            tagFilter.setTag(text);
            filters.add(tagFilter);

        } else {
            filters.remove(tagFilter);
        }

        viewer.setFilters((ViewerFilter[]) filters.toArray(new ViewerFilter[filters.size()]));
        viewer.refresh();


    }
}
