/**
 * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v2.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v20.html
 */
package org.eclipse.emf.ecore.xcore.ui.contentassist;


import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.xcore.XAnnotationDirective;
import org.eclipse.emf.ecore.xcore.XClassifier;
import org.eclipse.emf.ecore.xcore.XImportDirective;
import org.eclipse.emf.ecore.xcore.XPackage;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.ITextViewerExtension;
import org.eclipse.swt.custom.StyledText;
import org.eclipse.xtext.common.types.xtext.ui.JdtTypesProposalProvider;
import org.eclipse.xtext.conversion.IValueConverter;
import org.eclipse.xtext.conversion.impl.QualifiedNameValueConverter;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.naming.QualifiedName;
import org.eclipse.xtext.nodemodel.ICompositeNode;
import org.eclipse.xtext.nodemodel.util.NodeModelUtils;
import org.eclipse.xtext.resource.IEObjectDescription;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal;
import org.eclipse.xtext.ui.editor.contentassist.ConfigurableCompletionProposal.IReplacementTextApplier;
import org.eclipse.xtext.ui.editor.contentassist.ContentAssistContext;

import com.google.inject.Inject;


/**
 * @author Sebastian Zarnekow - Initial contribution and API
 */
public class ImportingTypesProposalProvider extends JdtTypesProposalProvider
{
  @Inject
  private QualifiedNameValueConverter qualifiedNameValueConverter;

  @Override
  protected IReplacementTextApplier createTextApplier(ContentAssistContext context, IScope typeScope, IQualifiedNameConverter qualifiedNameConverter, IValueConverter<String> valueConverter)
  {
    if (context.getCurrentModel() instanceof XImportDirective)
    {
      return super.createTextApplier(context, typeScope, qualifiedNameConverter, valueConverter);
    }
    return 
      new FQNImporter
        (context.getResource(),
         context.getViewer(),
         typeScope,
         qualifiedNameConverter,
         valueConverter,
         qualifiedNameValueConverter);
  }

  public static class FQNImporter extends FQNShortener
  {
    private final ITextViewer viewer;

    private final QualifiedNameValueConverter importConverter;

    public FQNImporter(Resource context, ITextViewer viewer, IScope scope, IQualifiedNameConverter qualifiedNameConverter, IValueConverter<String> valueConverter, QualifiedNameValueConverter importConverter)
    {
      super(context, scope, qualifiedNameConverter, valueConverter);
      this.viewer = viewer;
      this.importConverter = importConverter;
    }

    @Override
    public void apply(IDocument document, ConfigurableCompletionProposal proposal) throws BadLocationException
    {
      String proposalReplacementString = proposal.getReplacementString();
      String typeName = proposalReplacementString;
      if (valueConverter != null)
      {
        typeName = valueConverter.toValue(proposalReplacementString, null);
      }
      String replacementString = getActualReplacementString(proposal);
      // there is an import statement - apply computed replacementString
      if (!proposalReplacementString.equals(replacementString))
      {
        String shortTypeName = replacementString;
        if (valueConverter != null)
        {
          shortTypeName = valueConverter.toValue(replacementString, null);
        }
        QualifiedName shortQualifiedName = qualifiedNameConverter.toQualifiedName(shortTypeName);
        if (shortQualifiedName.getSegmentCount() == 1)
        {
          proposal.setCursorPosition(replacementString.length());
          document.replace(proposal.getReplacementOffset(), proposal.getReplacementLength(), replacementString);
          return;
        }
      }

      QualifiedName qualifiedName = qualifiedNameConverter.toQualifiedName(typeName);
      if (qualifiedName.getSegmentCount() == 1)
      {
        // type resides in default package - no need to hassle with imports
        proposal.setCursorPosition(proposalReplacementString.length());
        document.replace(proposal.getReplacementOffset(), proposal.getReplacementLength(), proposalReplacementString);
        return;
      }

      IEObjectDescription description = scope.getSingleElement(qualifiedName.skipFirst(qualifiedName.getSegmentCount() - 1));
      if (description != null)
      {
        // there exists a conflict - insert fully qualified name
        proposal.setCursorPosition(proposalReplacementString.length());
        document.replace(proposal.getReplacementOffset(), proposal.getReplacementLength(), proposalReplacementString);
        return;
      }

      // Import does not introduce ambiguities - add import and insert short name
      String shortName = qualifiedName.getLastSegment();
      int topPixel = -1;
      // store the pixel coordinates to prevent the ui from flickering
      StyledText widget = viewer.getTextWidget();
      if (widget != null)
        topPixel = widget.getTopPixel();
      ITextViewerExtension viewerExtension = null;
      if (viewer instanceof ITextViewerExtension)
      {
        viewerExtension = (ITextViewerExtension)viewer;
        viewerExtension.setRedraw(false);
      }
      try
      {
        // compute import statement's offset
        int offset = 0;
        boolean startWithLineBreak = true;
        boolean endWithLineBreak = false;
        XPackage file = (XPackage)context.getContents().get(0);
        EList<XImportDirective> importDirectives = file.getImportDirectives();
        if (importDirectives.isEmpty())
        {
          startWithLineBreak = false;
          EList<XAnnotationDirective> annotationDirectives = file.getAnnotationDirectives();
          if (annotationDirectives.isEmpty())
          {
            EList<XClassifier> classifiers = file.getClassifiers();
            if (classifiers.isEmpty())
            {
              offset = document.getLength();
            }
            else
            {
              ICompositeNode node = NodeModelUtils.getNode(classifiers.get(0));
              offset = node.getOffset();
              endWithLineBreak = true;
            }
          }
          else
          {
            ICompositeNode node = NodeModelUtils.getNode(annotationDirectives.get(0));
            offset = node.getOffset();
            endWithLineBreak = true;
          }
        }
        else
        {
          ICompositeNode node = NodeModelUtils.getNode(importDirectives.get(importDirectives.size() - 1));
          offset = node.getOffset() + node.getLength();
        }
        offset = Math.min(proposal.getReplacementOffset(), offset);

        // apply short proposal
        String escapedShortname = shortName;
        if (valueConverter != null)
        {
          escapedShortname = valueConverter.toString(shortName);
        }
        proposal.setCursorPosition(escapedShortname.length());
        document.replace(proposal.getReplacementOffset(), proposal.getReplacementLength(), escapedShortname);

        // add import statement
        int lineOfOffset = document.getLineOfOffset(offset);
        String lineDelimiter = document.getLineDelimiter(lineOfOffset);
        if (lineDelimiter == null && lineOfOffset > 0)
        {
          lineDelimiter = document.getLineDelimiter(lineOfOffset - 1);
        }
        String importStatement = (startWithLineBreak ? lineDelimiter : "") + "import "+ importConverter.toString(typeName);
        if (endWithLineBreak)
          importStatement += lineDelimiter + lineDelimiter;
        document.replace(offset, 0, importStatement.toString());
        proposal.setCursorPosition(proposal.getCursorPosition() + importStatement.length());

        // set the pixel coordinates
        if (widget != null)
        {
          int additionalTopPixel = 0;
          if (startWithLineBreak)
            additionalTopPixel += widget.getLineHeight();
          if (endWithLineBreak)
            additionalTopPixel += 2 * widget.getLineHeight();
          widget.setTopPixel(topPixel + additionalTopPixel);
        }
      }
      finally
      {
        if (viewerExtension != null)
        {
          viewerExtension.setRedraw(true);
        }
      }
    }
  }
}
