/*******************************************************************************
 * Copyright (c) 2009 IBM Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *    Robert Fuhrer (rfuhrer@watson.ibm.com) - initial API and implementation
 *******************************************************************************/
package com.redhat.ceylon.eclipse.code.outline;

import static com.redhat.ceylon.eclipse.code.outline.CeylonOutlineNode.DEFAULT_CATEGORY;
import static com.redhat.ceylon.eclipse.code.outline.CeylonOutlineNode.IMPORT_LIST_CATEGORY;
import static com.redhat.ceylon.eclipse.code.outline.CeylonOutlineNode.ROOT_CATEGORY;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

import org.eclipse.compare.IEditableContent;
import org.eclipse.compare.IEncodedStreamContentAccessor;
import org.eclipse.compare.ISharedDocumentAdapter;
import org.eclipse.compare.IStreamContentAccessor;
import org.eclipse.compare.ResourceNode;
import org.eclipse.compare.structuremergeviewer.DocumentRangeNode;
import org.eclipse.compare.structuremergeviewer.IStructureComparator;
import org.eclipse.compare.structuremergeviewer.StructureCreator;
import org.eclipse.compare.structuremergeviewer.StructureRootNode;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.text.IDocument;

import com.redhat.ceylon.compiler.typechecker.tree.Node;
import com.redhat.ceylon.eclipse.code.parse.CeylonParseController;

/**
 * @author rfuhrer
 */
public class CeylonStructureCreator extends StructureCreator {
    
    @Override
    public String getName() {
        return "Ceylon Structure Compare";
    }

    @Override
    protected IStructureComparator createStructureComparator(Object input, 
            IDocument document, ISharedDocumentAdapter sharedDocumentAdapter, 
            IProgressMonitor monitor) 
                    throws CoreException {
        
        if (input instanceof CeylonDocumentRangeNode) {
            return (CeylonDocumentRangeNode) input;
        }
        
        final boolean isEditable = input instanceof IEditableContent ?
                ((IEditableContent) input).isEditable() : false;
        
        StructureRootNode structureRootNode = new StructureRootNode(document, 
                    input, this, sharedDocumentAdapter) {
            @Override
            public boolean isEditable() {
                return isEditable;
            }
        };
        
        CeylonParseController pc = new CeylonParseController();
        if (input instanceof ResourceNode) {
            IResource file = ((ResourceNode) input).getResource();
            pc.initialize(file.getProjectRelativePath(), file.getProject(), null);
        }
        else {
            pc.initialize(null, null, null);
        }
        
        pc.parse(document, monitor, null);
        
        Node rootNode = pc.getRootNode();
        
        if (rootNode!=null) {
            // now visit the model, creating TreeCompareNodes for each ModelTreeNode
            buildCompareTree(new CeylonOutlineBuilder().buildTree(pc), 
                    structureRootNode, document);
        }
        
        return structureRootNode;

    }

    private void buildCompareTree(CeylonOutlineNode outlineNode, 
            DocumentRangeNode parent, IDocument document) {
        CeylonDocumentRangeNode compareNode;
        switch (outlineNode.getCategory()) {
        case ROOT_CATEGORY: //attach children of the unit node directly to our root
            compareNode = new CeylonDocumentRangeNode(parent, 
                    "@root", outlineNode, document) {
                @Override
                public String getName() {
                    return "Ceylon Source File";
                }
            };
            break;
        case IMPORT_LIST_CATEGORY:
            compareNode = new CeylonDocumentRangeNode(parent, 
                    "@importlist", outlineNode, document);
            break;
        case DEFAULT_CATEGORY:
            compareNode = new CeylonDocumentRangeNode(parent, 
                    outlineNode.getIdentifier(), outlineNode, document);
            break;
        default:
            // The outline view has some extra nodes 
            // we don't care about so just do nothing
            return;
        }
        for (CeylonOutlineNode treeChild: outlineNode.getChildren()) {            
            buildCompareTree(treeChild, compareNode, document);
        }
        parent.addChild(compareNode);
    }
    
    @Override
    public String getContents(Object node, boolean ignoreWhitespace) {
        if (node instanceof IStreamContentAccessor) {
            IStreamContentAccessor sca = (IStreamContentAccessor) node;
            try {
                String contents = readString(sca);
                return ignoreWhitespace ? 
                        contents.replaceAll("\\p{javaWhitespace}+", " ") : 
                        contents;
            } 
            catch (CoreException ex) {
                ex.printStackTrace();
            }
        }
        return null;
    }

    private static String readString(InputStream is, String encoding) {
        if (is == null) {
            return null;
        }
        BufferedReader reader = null;
        try {
            StringBuffer buffer= new StringBuffer();
            char[] part = new char[2048];
            int read = 0;
            reader = new BufferedReader(new InputStreamReader(is, encoding));
            while ((read= reader.read(part)) != -1) {
                buffer.append(part, 0, read);
            }
            return buffer.toString();

        }
        catch (IOException ex) {
            // NeedWork
        }
        finally {
            if (reader != null) {
                try {
                    reader.close();
                }
                catch (IOException ex) {
                    // silently ignored
                }
            }
        }
        return null;
    }

    public static String readString(IStreamContentAccessor sa) 
            throws CoreException {
        InputStream is= sa.getContents();
        if (is != null) {
            String encoding = null;
            if (sa instanceof IEncodedStreamContentAccessor) {
                try {
                    encoding = ((IEncodedStreamContentAccessor) sa).getCharset();
                }
                catch (Exception e) {}
            }
            if (encoding == null)
                encoding = ResourcesPlugin.getEncoding();
            return readString(is, encoding);
        }
        return null;
    }
    
    @Override
    protected String[] getPath(Object element, Object input) {
        return super.getPath(element, input);
    }

    @Override
    protected String getDocumentPartitioning() {
        return IDocument.DEFAULT_CONTENT_TYPE;
    }

}

@@ -1783,24 +1783,81 @@
 .tree.Node;%0A
+import com.redhat.ceylon.compiler.typechecker.tree.Tree;%0A
 import com.r
@@ -3528,44 +3528,258 @@
 er()
-.buildTree(pc), %0A                   
+ %7B%0A                //don't create nodes for shortcut refinement%0A                //because we can't distinguish them w/o a%0A                //full typecheck%0A                public void visit(Tree.SpecifierStatement that) %7B%7D%0A            %7D.buildTree(pc),
  str

fix bug in structure compare overview