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

import static com.redhat.ceylon.compiler.typechecker.tree.Util.formatPath;
import static com.redhat.ceylon.eclipse.core.builder.CeylonBuilder.getProjectTypeChecker;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.internal.corext.refactoring.rename.RenamePackageProcessor;
import org.eclipse.ltk.core.refactoring.Change;
import org.eclipse.ltk.core.refactoring.CompositeChange;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.ltk.core.refactoring.TextFileChange;
import org.eclipse.ltk.core.refactoring.participants.CheckConditionsContext;
import org.eclipse.ltk.core.refactoring.participants.RefactoringProcessor;
import org.eclipse.ltk.core.refactoring.participants.RenameParticipant;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;

import com.redhat.ceylon.compiler.typechecker.TypeChecker;
import com.redhat.ceylon.compiler.typechecker.context.PhasedUnit;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.ImportPath;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;
import com.redhat.ceylon.eclipse.core.vfs.IFileVirtualFile;

public class RenamePackageRefactoringParticipant extends RenameParticipant {

    private IPackageFragment javaPackageFragment;
    
    private static Map<IPackageFragment,String> allPackagesMoving = new HashMap<IPackageFragment,String>();

    @Override
    protected boolean initialize(Object element) {
        javaPackageFragment= (IPackageFragment) element;
        final String newName = getArguments().getNewName();
        RefactoringProcessor processor = getProcessor();
        if (processor instanceof RenamePackageProcessor) {
            RenamePackageProcessor renamePackageProcessor = 
                    (RenamePackageProcessor) processor;
            String patterns = renamePackageProcessor.getFilePatterns();
            if (renamePackageProcessor.getUpdateQualifiedNames() &&
                    (patterns.equals("*") || patterns.contains("*.ceylon"))) {
                return false;
            }
        }
        else {
            return false;
        }
        allPackagesMoving.put(javaPackageFragment, newName);
        return getProjectTypeChecker(javaPackageFragment.getJavaProject().getProject())!=null;
    }

    @Override
    public String getName() {
        return "Rename participant for Ceylon source";
    }
    
    @Override
    public RefactoringStatus checkConditions(IProgressMonitor pm, 
            CheckConditionsContext context) {
        return new RefactoringStatus();
    }

    @Override
    public Change createChange(IProgressMonitor pm) throws CoreException {
        try {
            final String newName = getArguments().getNewName();
            final String oldName = javaPackageFragment.getElementName();
            final IProject project = javaPackageFragment.getJavaProject().getProject();

            final List<Change> changes = new ArrayList<Change>();
            TypeChecker tc = getProjectTypeChecker(project);
            if (tc==null) return null;
            for (PhasedUnit phasedUnit: tc.getPhasedUnits().getPhasedUnits()) {

                final List<ReplaceEdit> edits = new ArrayList<ReplaceEdit>();
                phasedUnit.getCompilationUnit().visit(new Visitor() {
                    @Override
                    public void visit(ImportPath that) {
                        super.visit(that);
                        if (formatPath(that.getIdentifiers()).equals(oldName)) {
                            edits.add(new ReplaceEdit(that.getStartIndex(), 
                                    oldName.length(), newName));
                        }
                    }
                });

                if (!edits.isEmpty()) {
                    try {
                        final IFile file = ((IFileVirtualFile) phasedUnit.getUnitFile()).getFile();
                        IFile movedFile = getMovedFile(file);
                        TextFileChange change = 
                                new MovingTextFileChange(movedFile.getName(), movedFile, file);
                        change.setEdit(new MultiTextEdit());
                        for (ReplaceEdit edit: edits) {
                            change.addEdit(edit);
                        }
                        changes.add(change);
                    }       
                    catch (Exception e) { 
                        e.printStackTrace(); 
                    }
                }

            }

            if (changes.isEmpty()) {
                return null;
            }
            else {
                CompositeChange result = new CompositeChange("Ceylon source changes") {
                    @Override
                    public Change perform(IProgressMonitor pm) throws CoreException {
                        allPackagesMoving.clear();
                        return super.perform(pm);
                    }
                };
                for (Change change: changes) {
                    result.add(change);
                }
                return result;
            }
        }
        catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
    
    private static IFile getMovedFile(IFile file) {
        for (Map.Entry<IPackageFragment,String> e: allPackagesMoving.entrySet()) {
            IPackageFragment javaPackageFragment = e.getKey();
            String newName = e.getValue();
            String oldPath = javaPackageFragment.getElementName().replace('.', '/');
            String newPath = newName.replace('.', '/');
            IPath pathInSourceFolder = file.getParent().getProjectRelativePath()
                    .removeFirstSegments(1); //TODO: lame, it assumes a the source folder belongs directly to the project
            if (pathInSourceFolder.toPortableString().equals(oldPath)) {
                return file.getProject().getFile(file.getParent().getProjectRelativePath()
                        .removeLastSegments(pathInSourceFolder.segmentCount())
                        .append(newPath).append(file.getName()));
            }
        }
        return file;
    }
}
@@ -1312,24 +1312,89 @@
 PhasedUnit;%0A
+import com.redhat.ceylon.compiler.typechecker.tree.Tree.DocLink;%0A
 import com.r
@@ -4115,32 +4115,972 @@
               %7D%0A
+                    @Override%0A                    public void visit(DocLink that) %7B%0A                        super.visit(that);%0A                        //TODO: fix copy/paste from RenameRefactoring%0A                        String text = that.getText();%0A                        int offset = that.getStartIndex();%0A                        %0A                        int pipeIndex = text.indexOf(%22%7C%22);%0A                        if (pipeIndex %3E -1) %7B%0A                            text = text.substring(pipeIndex + 1);%0A                            offset += pipeIndex + 1;%0A                        %7D%0A                        %0A                        int scopeIndex = text.indexOf(%22::%22);%0A                        if (scopeIndex%3E=0 && text.substring(scopeIndex).endsWith(oldName)) %7B%0A                            edits.add(new ReplaceEdit(offset, %0A                                    oldName.length(), newName));%0A                        %7D%0A                    %7D%0A
                 

handle doc links in Rename Package