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

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

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.TextFileChange;
import org.eclipse.text.edits.DeleteEdit;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;

import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Term;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;

class ConvertToInterpolationProposal extends CorrectionProposal {

    private ConvertToInterpolationProposal(String name, Change change) {
        super(name, change, null);
    }
    
    private static List<Tree.Term> flatten(Tree.SumOp sum) {
        Tree.Term lt = sum.getLeftTerm();
        Tree.Term rt = sum.getRightTerm();
        List<Tree.Term> result;
        if (lt instanceof Tree.SumOp) {
            result = flatten((Tree.SumOp) lt);
            result.add(rt);
        }
        else {
            result = new ArrayList<Tree.Term>();
            result.add(lt);
            result.add(rt);
        }
        return result;
    }

    private static boolean isConcatenation(Tree.SumOp sum) {
        boolean expectingLiteral = true;
        for (Tree.Term term: flatten(sum)) {
            if (expectingLiteral) {
                if (!(term instanceof Tree.StringLiteral ||
                      term instanceof Tree.StringTemplate)) {
                    return false;
                }
                expectingLiteral = false;
            }
            else {
                expectingLiteral = true;
            }
        }
        return !expectingLiteral;
    }
    
    static class ConcatenationVisitor extends Visitor {
        Node node;
        Tree.SumOp result;
        ConcatenationVisitor(Node node) {
            this.node = node;
        }
        @Override
        public void visit(Tree.SumOp that) {
            if (that.getStartIndex()<=node.getStartIndex() &&
                that.getEndIndex()>=node.getEndIndex()) {
                if (isConcatenation(that)) {
                    result = that;
                    return;
                }
            }
            super.visit(that);
        }
    }
    
    static void addConvertToInterpolationProposal(
            Collection<ICompletionProposal> proposals,
            IFile file, Tree.CompilationUnit cu, Node node, 
            IDocument doc) {
        ConcatenationVisitor tv = 
                new ConcatenationVisitor(node);
        tv.visit(cu);
        Tree.SumOp sum = tv.result;
        if (sum!=null) {
            TextFileChange change = 
                    new TextFileChange(
                            "Convert to Interpolation", 
                            file);
            change.setEdit(new MultiTextEdit());
            List<Term> terms = flatten(sum);
            for (int i=0; i<terms.size(); i++) {
                Tree.Term term = terms.get(i);
                if (i>0) {
                    Tree.Term previous = terms.get(i-1);
                    int from = previous.getEndIndex();
                    int to = term.getStartIndex();
                    change.addEdit(new DeleteEdit(from, to-from));
                }
                if (i%2==0) {
                    if (i>0) {
                        change.addEdit(new ReplaceEdit(term.getStartIndex(), 1, "``"));
                    }
                    if (i<terms.size()-1) {
                        change.addEdit(new ReplaceEdit(term.getEndIndex()-1, 1, "``"));
                    }
                }
                else {
                    if (term instanceof Tree.QualifiedMemberExpression) {
                        Tree.QualifiedMemberExpression lrt = 
                                (Tree.QualifiedMemberExpression) term;
                        if (lrt.getDeclaration().getName().equals("string")) {
                            int from = lrt.getMemberOperator().getStartIndex();
                            int to = lrt.getIdentifier().getEndIndex();
                            change.addEdit(new DeleteEdit(from, to-from));
                        }
                    }
                }
            }
            proposals.add(new ConvertToInterpolationProposal(
                    "Convert to string interpolation", change));
        }
    }
    
    
}
@@ -412,24 +412,66 @@
 DeleteEdit;%0A
+import org.eclipse.text.edits.InsertEdit;%0A
 import org.e
@@ -499,24 +499,24 @@
 tiTextEdit;%0A
-
 import org.e
@@ -665,70 +665,8 @@
 ee;%0A
-import com.redhat.ceylon.compiler.typechecker.tree.Tree.Term;%0A
 impo
@@ -1444,38 +1444,218 @@
     
-boolean expectingLiteral = tru
+List%3CTree.Term%3E terms = flatten(sum);%0A        Tree.Term lt = terms.get(0);%0A        boolean expectingLiteral =%0A                lt instanceof Tree.StringLiteral %7C%7C%0A                lt instanceof Tree.StringTemplat
 e;%0A 
@@ -1682,28 +1682,21 @@
 m term: 
-flatten(sum)
+terms
 ) %7B%0A    
@@ -2060,25 +2060,12 @@
 urn 
-!expectingLiteral
+true
 ;%0A  
@@ -3229,16 +3229,21 @@
    List%3C
+Tree.
 Term%3E te
@@ -3254,32 +3254,404 @@
 = flatten(sum);%0A
+            Tree.Term lt = terms.get(0);%0A            Tree.Term rt = terms.get(terms.size()-1);%0A            boolean expectingLiteral =%0A                    lt instanceof Tree.StringLiteral %7C%7C%0A                    lt instanceof Tree.StringTemplate;%0A            if (!expectingLiteral) %7B%0A                change.addEdit(new InsertEdit(lt.getStartIndex(), %22%5C%22%60%60%22));%0A            %7D%0A
             for 
@@ -4029,14 +4029,24 @@
 if (
-i%252==0
+expectingLiteral
 ) %7B%0A
@@ -4332,32 +4332,78 @@
               %7D%0A
+                    expectingLiteral = false;%0A
                 
@@ -4996,33 +4996,202 @@
                 
-%7D
+    expectingLiteral = true;%0A                %7D%0A            %7D%0A            if (expectingLiteral) %7B%0A                change.addEdit(new InsertEdit(rt.getEndIndex(), %22%60%60%5C%22%22));
 %0A            %7D%0A 

handle concatenations not terminated by literals