package com.redhat.ceylon.eclipse.code.correct;

import java.util.Collection;
import java.util.List;

import org.antlr.runtime.CommonToken;
import org.eclipse.core.resources.IFile;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.TextChange;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.text.edits.ReplaceEdit;

import com.redhat.ceylon.compiler.typechecker.model.Parameter;
import com.redhat.ceylon.compiler.typechecker.tree.NaturalVisitor;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.eclipse.code.editor.CeylonEditor;
import com.redhat.ceylon.eclipse.code.editor.EditorUtil;
import com.redhat.ceylon.eclipse.code.refactor.AbstractRefactoring;

class ConvertToPositionalArgumentsProposal extends CorrectionProposal {
    
    final int offset; 
    final IFile file;
    
    public ConvertToPositionalArgumentsProposal(int offset, IFile file, Change change) {
        super("Convert to positional arguments", change);
        this.offset=offset;
        this.file=file;
    }

    @Override
    public void apply(IDocument document) {
         super.apply(document);
         EditorUtil.gotoLocation(file, offset);
    }
    
    public static void addConvertToPositionalArgumentsProposal(Collection<ICompletionProposal> proposals, 
            IFile file, Tree.CompilationUnit cu, CeylonEditor editor, int currentOffset) {
        Tree.NamedArgumentList nal = 
                findNamedArgumentList(currentOffset, cu);
        if (nal==null) {
            return;
        }
        final TextChange tc = 
                new TextFileChange("Convert To Positional Arguments", file);
        Integer start = nal.getStartIndex();
        int length = nal.getStopIndex()-start+1;
        StringBuilder result = new StringBuilder().append("(");
        List<CommonToken> tokens = editor.getParseController().getTokens();
        List<Tree.NamedArgument> args = nal.getNamedArguments();
        Tree.SequencedArgument sa = nal.getSequencedArgument();
        for (Parameter p: nal.getNamedArgumentList().getParameterList()
                .getParameters()) {
            if (sa!=null) {
                Parameter param = sa.getParameter();
                if (param==null) {
                    return;
                }
                if (param.getModel().equals(p.getModel())) {
                    result.append("{ ");
                    result.append(AbstractRefactoring.toString(sa, tokens));
                    result.append(" }");
                }
            }
            for (Tree.NamedArgument na: args) {
                Parameter param = na.getParameter();
                if (param==null) {
                    return;
                }
                if (param.getModel().equals(p.getModel())) {
                    if (na instanceof Tree.SpecifiedArgument) {
                        Tree.SpecifiedArgument sna = (Tree.SpecifiedArgument) na;
                        Tree.SpecifierExpression se = sna.getSpecifierExpression();
                        if (se!=null && se.getExpression()!=null) {
                            result.append(AbstractRefactoring.toString(se.getExpression(), tokens));
                        }
                        break;
                    }
                    else if (na instanceof Tree.MethodArgument) {
                        Tree.MethodArgument ma = (Tree.MethodArgument) na;
                        if (ma.getDeclarationModel().isDeclaredVoid()) {
                            result.append("void ");
                            for (Tree.ParameterList pl: ma.getParameterLists()) {
                                result.append(AbstractRefactoring.toString(pl, tokens));
                            }
                            if (ma.getBlock()!=null) {
                                result.append(" ")
                                    .append(AbstractRefactoring.toString(ma.getBlock(), tokens));
                            }
                            if (ma.getSpecifierExpression()!=null) {
                                result.append(" ")
                                    .append(AbstractRefactoring.toString(ma.getSpecifierExpression(), tokens));
                            }
                        }
                    }
                    else {
                        return;
                    }
                }
            }
            result.append(", ");
        }
        if (result.length()>1) {
            result.setLength(result.length()-2);
        }
        result.append(")");
        tc.setEdit(new ReplaceEdit(start, length, result.toString()));
        int offset = start+result.toString().length();
        proposals.add(new ConvertToPositionalArgumentsProposal(offset, file, tc));
    }
    
    private static Tree.NamedArgumentList findNamedArgumentList(
            int currentOffset, Tree.CompilationUnit cu) {
        FindNamedArgumentsVisitor fpav = 
                new FindNamedArgumentsVisitor(currentOffset);
        fpav.visit(cu);
        return fpav.getArgumentList();
    }

    private static class FindNamedArgumentsVisitor 
        extends Visitor 
        implements NaturalVisitor {
        
        Tree.NamedArgumentList argumentList;
        int offset;
        
        private Tree.NamedArgumentList getArgumentList() {
            return argumentList;
        }

        private FindNamedArgumentsVisitor(int offset) {
            this.offset = offset;
        }
        
        @Override
        public void visit(Tree.NamedArgumentList that) {
            if (offset>=that.getStartIndex() && 
                    offset<=that.getStopIndex()+1) {
                argumentList = that;
            }
            super.visit(that); 
        }
    }
    
}
@@ -2318,24 +2318,59 @@
 meters()) %7B%0A
+            boolean found = false;%0A
             
@@ -2377,32 +2377,32 @@
 if (sa!=null) %7B%0A
-
                 
@@ -2572,32 +2572,66 @@
 .getModel())) %7B%0A
+                    found = true;%0A
                 
@@ -2653,17 +2653,16 @@
 nd(%22%7B %22)
-;
 %0A       
@@ -2666,38 +2666,36 @@
                 
-result
+    
 .append(Abstract
@@ -2719,33 +2719,32 @@
 ing(sa, tokens))
-;
 %0A               
@@ -2740,38 +2740,36 @@
                 
-result
+    
 .append(%22 %7D%22);%0A 
@@ -2973,32 +2973,32 @@
               %7D%0A
-
                 
@@ -3034,32 +3034,66 @@
 .getModel())) %7B%0A
+                    found = true;%0A
                 
@@ -4670,32 +4670,61 @@
 %7D%0A            %7D%0A
+            if (found) %7B%0A    
             resu
@@ -4736,24 +4736,38 @@
 pend(%22, %22);%0A
+            %7D%0A
         %7D%0A  

fix for defaulted arguments for #841