package com.redhat.ceylon.eclipse.imp.refine;

import static com.redhat.ceylon.eclipse.imp.editor.CeylonAutoEditStrategy.getDefaultIndent;
import static com.redhat.ceylon.eclipse.imp.editor.Util.getCurrentEditor;
import static com.redhat.ceylon.eclipse.imp.parser.CeylonSourcePositionLocator.findNode;
import static com.redhat.ceylon.eclipse.imp.proposals.CeylonContentProposer.getProposals;
import static com.redhat.ceylon.eclipse.imp.proposals.CeylonContentProposer.getRefinementTextFor;
import static com.redhat.ceylon.eclipse.imp.quickfix.CeylonQuickFixAssistant.getIndent;

import java.util.List;

import org.eclipse.core.commands.AbstractHandler;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.ltk.core.refactoring.PerformChangeOperation;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IFileEditorInput;

import com.redhat.ceylon.compiler.typechecker.model.ClassOrInterface;
import com.redhat.ceylon.compiler.typechecker.model.Declaration;
import com.redhat.ceylon.compiler.typechecker.model.DeclarationWithProximity;
import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.compiler.typechecker.tree.Tree;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.Statement;
import com.redhat.ceylon.eclipse.imp.editor.CeylonEditor;
import com.redhat.ceylon.eclipse.imp.editor.Util;
import com.redhat.ceylon.eclipse.imp.parser.CeylonParseController;

public class RefineFormalMembersHandler extends AbstractHandler {
    
    @Override
    public Object execute(ExecutionEvent event) throws ExecutionException {
        CeylonEditor editor = (CeylonEditor) getCurrentEditor();
        Tree.CompilationUnit cu = editor.getParseController().getRootNode();
        if (cu==null) return null;
        Node node = getSelectedNode(editor);
        TextFileChange change = new TextFileChange("Refine Formal Members", 
                Util.getFile(editor.getEditorInput()));
        IDocument doc;
        try {
            doc = change.getCurrentDocument(null);
        }
        catch (CoreException e) {
            throw new RuntimeException(e);
        }
        //TODO: copy/pasted from CeylonQuickFixAssisitant
        Tree.ClassBody body;
        int offset;
        if (node instanceof Tree.ClassDefinition) {
            body = ((Tree.ClassDefinition) node).getClassBody();
            offset = -1;
        }
        else if (node instanceof Tree.ClassBody) {
            body = (Tree.ClassBody) node;
            offset = editor.getSelectedRegion().getOffset();
        }
        else {
            //TODO run a visitor to find the containing body!
            return null;//TODO popup error dialog
        }
        List<Statement> statements = body.getStatements();
        String indent;
        String indentAfter;
        if (statements.isEmpty()) {
            indentAfter = "\n" + getIndent(body, doc);
            indent = indentAfter + getDefaultIndent();
            if (offset<0) offset = body.getStartIndex()+1;
        }
        else {
            Statement statement = statements.get(statements.size()-1);
            indent = "\n" + getIndent(statement, doc);
            indentAfter = "";
            if (offset<0) offset = statement.getStopIndex()+1;
        }
        StringBuilder result = new StringBuilder();
        for (DeclarationWithProximity dwp: getProposals(node, "", cu).values()) {
            Declaration d = dwp.getDeclaration();
            if (d.isFormal() && 
                    ((ClassOrInterface) node.getScope()).isInheritedFromSupertype(d)) {
                result.append(indent).append(getRefinementTextFor(d, indent)).append(indentAfter);
            }
        }
        change.setEdit(new InsertEdit(offset, result.toString()));
        change.initializeValidationData(null);
        try {
            ResourcesPlugin.getWorkspace().run(new PerformChangeOperation(change), 
                    new NullProgressMonitor());
        }
        catch (CoreException ce) {
            throw new ExecutionException("Error cleaning imports", ce);
        }
        return null;
    }
    
    //TODO: copy/pasted from AbstractFindAction
    private static Node getSelectedNode(CeylonEditor editor) {
        CeylonParseController cpc = editor.getParseController();
        return cpc.getRootNode()==null ? null : 
            findNode(cpc.getRootNode(), 
                (ITextSelection) editor.getSelectionProvider().getSelection());
    }

    //TODO: copy/pasted from CleanImportsHandler
    @Override
    public boolean isEnabled() {
        IEditorPart editor = getCurrentEditor();
        return super.isEnabled() && 
                editor instanceof CeylonEditor &&
                editor.getEditorInput() instanceof IFileEditorInput &&
                ( getSelectedNode((CeylonEditor) editor) instanceof Tree.ClassBody ||
                getSelectedNode((CeylonEditor) editor) instanceof Tree.ClassDefinition );
    }
}

@@ -2562,29 +2562,24 @@
        Tree.
-Class
 Body body;%0A 
@@ -2577,16 +2577,16 @@
 y body;%0A
+
         
@@ -2761,43 +2761,416 @@
     
-else if (node instanceof Tree.Class
+if (node instanceof Tree.InterfaceDefinition) %7B%0A            body = ((Tree.InterfaceDefinition) node).getInterfaceBody();%0A            offset = -1;%0A        %7D%0A        if (node instanceof Tree.ObjectDefinition) %7B%0A            body = ((Tree.ObjectDefinition) node).getClassBody();%0A            offset = -1;%0A        %7D%0A        else if (node instanceof Tree.ClassBody %7C%7C %0A                node instanceof Tree.Interface
 Body
@@ -3194,29 +3194,24 @@
 ody = (Tree.
-Class
 Body) node;%0A
@@ -5340,22 +5340,20 @@
         
-return
+if (
  super.i
@@ -5485,18 +5485,19 @@
 orInput 
-&&
+) %7B
 %0A       
@@ -5501,21 +5501,27 @@
         
-    (
+Node node =
  getSele
@@ -5543,32 +5543,57 @@
 nEditor) editor)
+;%0A            return node
  instanceof Tree
@@ -5626,46 +5626,198 @@
     
-getSelectedNode((CeylonEditor) editor)
+    node instanceof Tree.InterfaceBody %7C%7C%0A                    node instanceof Tree.ClassDefinition %7C%7C%0A                    node instanceof Tree.InterfaceDefinition %7C%7C%0A                    node
  ins
@@ -5821,37 +5821,38 @@
 instanceof Tree.
-Class
+Object
 Definition );%0A  
@@ -5849,11 +5849,70 @@
 tion
- );
+;%0A        %7D%0A        else %7B%0A            return false;%0A        %7D
 %0A   

support interfaces and objects