package com.redhat.ceylon.compiler.js;

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

import com.redhat.ceylon.compiler.typechecker.model.Annotation;
import com.redhat.ceylon.compiler.typechecker.model.ProducedType;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AnyAttribute;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.AnyMethod;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.BaseType;
import com.redhat.ceylon.compiler.typechecker.tree.Tree.MemberOrTypeExpression;
import com.redhat.ceylon.compiler.typechecker.tree.Visitor;

/** A Visitor that gathers ceylondoc info for each node's declaration.
 * 
 * @author Enrique Zamudio
 */
public class DocVisitor extends Visitor {

    /** Here we store all the docs, as they are encountered */
    private List<String> docs = new ArrayList<String>();
    /** Here we store all the refs to the docs (index in the 'docs' list), keyed by location. */
    private Map<String, Integer> locs = new HashMap<String, Integer>();

    private boolean retrieveDocs(List<Annotation> annotations, String location) {
        boolean rval = false;
        for (Annotation ann : annotations) {
            if ("doc".equals(ann.getName()) && !ann.getPositionalArguments().isEmpty()) {
                String doc = ann.getPositionalArguments().get(0);
                if (doc.charAt(0) == '"' && doc.charAt(doc.length()-1) == '"') {
                    doc = doc.substring(1, doc.length()-1);
                }
                int idx = docs.indexOf(doc);
                if (idx < 0) {
                    idx = docs.size();
                    docs.add(doc);
                }
                locs.put(location, idx);
                rval = true;
            }
        }
        return rval;
    }

    //This catches function calls, method/attribute calls, object refs, constructors.
    @Override
    public void visit(MemberOrTypeExpression that) {
        //Can't get location for this
        if (that.getTypeModel() != null && !that.getTypeModel().getTypeArgumentList().isEmpty()) {
            for (ProducedType pt : that.getTypeModel().getTypeArgumentList()) {
                retrieveDocs(pt.getDeclaration().getAnnotations(), that.getLocation());
            }
        }
        if (that.getDeclaration() != null && that.getLocation() != null) {
            retrieveDocs(that.getDeclaration().getAnnotations(), that.getLocation());
        }
        super.visit(that);
    }
    @Override
    public void visit(AnyAttribute that) {
        //local vars
        retrieveDocs(that.getType().getTypeModel().getDeclaration().getAnnotations(), that.getType().getLocation());
        super.visit(that);
    }

    @Override
    public void visit(AnyMethod that) {
        retrieveDocs(that.getDeclarationModel().getType().getDeclaration().getAnnotations(), that.getType().getLocation());
        super.visit(that);
    }
    @Override
    public void visit(BaseType that) {
        retrieveDocs(that.getDeclarationModel().getType().getDeclaration().getAnnotations(), that.getLocation());
        super.visit(that);
    }

    @Override
    public void visit(com.redhat.ceylon.compiler.typechecker.tree.Tree.Annotation that) {
        super.visit(that);
    }

    /** Returns a list of all the doc values found, in the order they were found. */
    public List<String> getDocs() {
        return docs;
    }
    /** Returns a map of the references to the docs; the keys are the locations in the source code
     * and the values are the index of the doc in the docs list. */
    public Map<String, Integer> getLocations() {
        return locs;
    }

}

@@ -542,24 +542,113 @@
 Expression;%0A
+import com.redhat.ceylon.compiler.typechecker.tree.Tree.QualifiedMemberOrTypeExpression;%0A
 import com.r
@@ -2391,74 +2391,331 @@
     
-if (that.getDeclaration() != null && that.getLocation() != null) %7B
+String loc = that.getLocation();%0A        if (that.getDeclaration() != null && loc != null) %7B%0A            if (that instanceof QualifiedMemberOrTypeExpression) %7B%0A                QualifiedMemberOrTypeExpression qme = (QualifiedMemberOrTypeExpression)that;%0A                loc = qme.getIdentifier().getLocation();%0A            %7D
 %0A   
@@ -2776,34 +2776,19 @@
 ions(), 
-that.getLocation()
+loc
 );%0A     

Use correct location for member expressions