/**
 * Copyright (C) 2012 BonitaSoft S.A.
 * BonitaSoft, 31 rue Gustave Eiffel - 38000 Grenoble
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2.0 of the License, or
 * (at your option) any later version.
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package org.bonitasoft.studio.data.provider;

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

import org.bonitasoft.studio.common.ExpressionConstants;
import org.bonitasoft.studio.common.log.BonitaStudioLog;
import org.bonitasoft.studio.common.repository.RepositoryManager;
import org.bonitasoft.studio.expression.editor.provider.IOperatorEditor;
import org.bonitasoft.studio.model.expression.Expression;
import org.bonitasoft.studio.model.expression.Operator;
import org.bonitasoft.studio.model.process.JavaObjectData;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.internal.corext.template.java.SignatureUtil;
import org.eclipse.jdt.internal.ui.viewsupport.JavaUILabelProvider;
import org.eclipse.jface.layout.GridDataFactory;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ITreeSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;

/**
 * @author Romain Bioteau
 */
public class JavaSetterOperatorEditor implements IOperatorEditor {

    private TreeViewer javaTreeviewer;
    private final List<ISelectionChangedListener> listeners = new ArrayList<ISelectionChangedListener>();

    /*
     * (non-Javadoc)
     * @see org.bonitasoft.studio.expression.editor.provider.IOperatorEditor#appliesTo(java.lang.String)
     */
    @Override
    public boolean appliesTo(final String operatorType) {
        return ExpressionConstants.JAVA_METHOD_OPERATOR.equals(operatorType);
    }

    @Override
    public boolean appliesTo(final Expression expression) {
        return expression != null
                && expression.getContent() != null
                && !expression.getContent().isEmpty()
                && ExpressionConstants.VARIABLE_TYPE.equals(expression.getType())
                && !expression.getReferencedElements().isEmpty()
                && expression.getReferencedElements().get(0) instanceof JavaObjectData;
    }

    /*
     * (non-Javadoc)
     * @see org.bonitasoft.studio.expression.editor.provider.IOperatorEditor#canFinish()
     */
    @Override
    public boolean canFinish() {
        return isSetterOrDataSelected((ITreeSelection) javaTreeviewer.getSelection());
    }

    /*
     * (non-Javadoc)
     * @see org.bonitasoft.studio.expression.editor.provider.IOperatorEditor#createOpeartorEditor(org.eclipse.swt.widgets.Composite,
     * org.bonitasoft.studio.model.expression.Operator, org.bonitasoft.studio.model.expression.Expression)
     */
    @Override
    public Composite createOpeartorEditor(final Composite parent, final Operator operator, final Expression sourceExpression) {
        final Composite client = new Composite(parent, SWT.NONE);
        client.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
        client.setLayout(GridLayoutFactory.fillDefaults().numColumns(1).margins(0, 0).spacing(0, 0).create());

        final JavaObjectData data = (JavaObjectData) sourceExpression.getReferencedElements().get(0);

        javaTreeviewer = new TreeViewer(client, SWT.SINGLE | SWT.BORDER);
        javaTreeviewer.getControl().setLayoutData(GridDataFactory.fillDefaults().grab(true, true).hint(SWT.DEFAULT, 200).create());
        javaTreeviewer.setContentProvider(new JavaSetterContentProvider());
        javaTreeviewer.setLabelProvider(new JavaUILabelProvider() {

            @Override
            public String getText(final Object item) {
                if (item instanceof IMethod) {
                    try {
                        return super.getText(item) + " - " + SignatureUtil.stripSignatureToFQN(((IMethod) item).getReturnType());
                    } catch (final JavaModelException e) {
                        BonitaStudioLog.error(e);
                        return null;
                    }
                } else {
                    return super.getText(item);
                }
            }
        });

        javaTreeviewer.addSelectionChangedListener(new ISelectionChangedListener() {

            @Override
            public void selectionChanged(final SelectionChangedEvent event) {
                final ITreeSelection selection = (ITreeSelection) event.getSelection();
                if (!selection.isEmpty()) {
                    final boolean isValid = isSetterOrDataSelected(selection);
                    if (isValid) {
                        final TreePath path = selection.getPaths()[0];
                        final IMethod item = (IMethod) path.getSegment(path.getSegmentCount() - 1);
                        operator.setExpression(item.getElementName());
                        operator.getInputTypes().clear();
                        for (final String type : item.getParameterTypes()) {
                            final String qualifiedType = retrieveQualifiedType(item, type);
                            operator.getInputTypes().add(qualifiedType);
                        }
                    }
                }
                fireSelectionChange(event);
            }

            protected String retrieveQualifiedType(final IMethod item, final String type) {
                final String typeErasure = Signature.getTypeErasure(type);
                final IType declaringType = item.getDeclaringType();
                return JDTMethodHelper.retrieveQualifiedType(typeErasure, declaringType);
            }
        });
        
        String className = null;
        if (data.isMultiple()) {
            className = List.class.getName();
        } else if (data instanceof JavaObjectData) {
            className = data.getClassName();
        }
        if (className != null) {
            javaTreeviewer.setInput(className);
            javaTreeviewer.getTree().setFocus();
            javaTreeviewer.expandAll();
            final IMethod selectedMethod = getJavaSelectionFromContent(data, operator);
            if (selectedMethod != null) {
                javaTreeviewer.setSelection(new StructuredSelection(selectedMethod));
            }
        }
        return client;
    }
    
    @Override
    public StructuredViewer getViewer() {
        return javaTreeviewer;
    }

    protected void fireSelectionChange(final SelectionChangedEvent event) {
        for (final ISelectionChangedListener l : listeners) {
            l.selectionChanged(event);
        }
    }

    protected String generateJavaAdditionalPath(final ITreeSelection selection) {
        if (selection == null) {
            return "";
        }
        final TreePath path = selection.getPaths()[0];
        if (path.getSegmentCount() == 1) {
            return "";
        }
        final StringBuilder res = new StringBuilder();
        final Object item = path.getSegment(path.getSegmentCount() - 1);
        res.append(((IJavaElement) item).getElementName());
        return res.toString();
    }

    private boolean isSetterOrDataSelected(final ITreeSelection selection) {
        if (selection.getFirstElement() instanceof IMethod) {
            final IMethod method = (IMethod) selection.getFirstElement();
            try {
                return method.getParameterNames().length == 1;
            } catch (final Exception ex) {
                BonitaStudioLog.error(ex);
                return false;
            }
        } else if (selection.getFirstElement() instanceof String) {
            return true;
        } else {
            return false;
        }
    }

    private IMethod getJavaSelectionFromContent(final JavaObjectData data, final Operator operator) {
        String className = null;
        if (data != null) {
            if (data.isMultiple()) {
                className = List.class.getName();
            } else if (data instanceof JavaObjectData) {
                className = data.getClassName();
            }
            if (className != null) {
                final String content = operator.getExpression();
                if (content != null && !content.isEmpty()) {

                    final IJavaProject project = RepositoryManager.getInstance().getCurrentRepository().getJavaProject();
                    IType type = null;
                    try {
                        type = project.findType(className);
                        for (final IMethod m : type.getMethods()) {
                            final String method = m.getElementName();
                            if (method.equals(content)) {
                                return m;
                            }
                        }
                    } catch (final JavaModelException e) {
                        BonitaStudioLog.error(e);
                    }

                }
            }
        }
        return null;
    }

    @Override
    public void addSelectionChangedListener(final ISelectionChangedListener listener) {
        listeners.add(listener);
    }

    @Override
    public ISelection getSelection() {
        return javaTreeviewer.getSelection();
    }

    @Override
    public void removeSelectionChangedListener(final ISelectionChangedListener listener) {
        listeners.remove(listener);
    }

    @Override
    public void setSelection(final ISelection selection) {
        javaTreeviewer.setSelection(selection);
    }

}