/*
 *
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 */

package org.apache.flex.compiler.internal.codegen.js.jx;

import org.apache.flex.compiler.codegen.ISubEmitter;
import org.apache.flex.compiler.codegen.js.IJSEmitter;
import org.apache.flex.compiler.definitions.IDefinition;
import org.apache.flex.compiler.definitions.IFunctionDefinition.FunctionClassification;
import org.apache.flex.compiler.internal.codegen.as.ASEmitterTokens;
import org.apache.flex.compiler.internal.codegen.js.JSSubEmitter;
import org.apache.flex.compiler.internal.codegen.js.goog.JSGoogEmitterTokens;
import org.apache.flex.compiler.internal.codegen.js.utils.EmitterUtils;
import org.apache.flex.compiler.internal.definitions.AccessorDefinition;
import org.apache.flex.compiler.internal.definitions.FunctionDefinition;
import org.apache.flex.compiler.tree.ASTNodeID;
import org.apache.flex.compiler.tree.as.IASNode;
import org.apache.flex.compiler.tree.as.IExpressionNode;
import org.apache.flex.compiler.tree.as.IFunctionCallNode;
import org.apache.flex.compiler.tree.as.IFunctionObjectNode;
import org.apache.flex.compiler.tree.as.IIdentifierNode;
import org.apache.flex.compiler.tree.as.IMemberAccessExpressionNode;
import org.apache.flex.compiler.tree.as.IVariableNode;
import org.apache.flex.compiler.utils.NativeUtils;

public class IdentifierEmitter extends JSSubEmitter implements
        ISubEmitter<IIdentifierNode>
{

    public IdentifierEmitter(IJSEmitter emitter)
    {
        super(emitter);
    }

    @Override
    public void emit(IIdentifierNode node)
    {
        IDefinition nodeDef = ((IIdentifierNode) node).resolve(getProject());

        IASNode parentNode = node.getParent();
        ASTNodeID parentNodeId = parentNode.getNodeID();

        boolean identifierIsAccessorFunction = nodeDef instanceof AccessorDefinition;
        boolean identifierIsPlainFunction = nodeDef instanceof FunctionDefinition
                && !identifierIsAccessorFunction;

        boolean emitName = true;

        if (nodeDef != null && nodeDef.isStatic())
        {
            String sname = nodeDef.getParent().getQualifiedName();
            if (sname.length() > 0)
            {
                write(getEmitter().formatQualifiedName(sname));
                write(ASEmitterTokens.MEMBER_ACCESS);
            }
        }
        else if (!NativeUtils.isNative(node.getName()))
        {
            // an instance method as a parameter or
        	// an instance method assigned to a variable or
            // a local function
            boolean useGoogBind = (parentNodeId == ASTNodeID.ContainerID
                    && identifierIsPlainFunction && ((FunctionDefinition) nodeDef)
                    .getFunctionClassification() == FunctionClassification.CLASS_MEMBER)
                    || (identifierIsPlainFunction && ((FunctionDefinition) nodeDef)
                    .getFunctionClassification() == FunctionClassification.CLASS_MEMBER &&
                       isBeingAssignedToVariable(node))
                    || (identifierIsPlainFunction && ((FunctionDefinition) nodeDef)
                            .getFunctionClassification() == FunctionClassification.LOCAL);

            if (useGoogBind)
            {
                write(JSGoogEmitterTokens.GOOG_BIND);
                write(ASEmitterTokens.PAREN_OPEN);
            }

            if (EmitterUtils.writeThis(getProject(), getModel(), node))
            {
                IFunctionObjectNode functionObjectNode = (IFunctionObjectNode) node
                        .getParent().getAncestorOfType(
                                IFunctionObjectNode.class);

                if (functionObjectNode != null)
                    write(JSGoogEmitterTokens.SELF);
                else
                    write(ASEmitterTokens.THIS);

                write(ASEmitterTokens.MEMBER_ACCESS);
            }

            if (useGoogBind)
            {
                write(node.getName());

                writeToken(ASEmitterTokens.COMMA);
                write(ASEmitterTokens.THIS);
                write(ASEmitterTokens.PAREN_CLOSE);

                emitName = false;
            }
        }

        //IDefinition parentDef = (nodeDef != null) ? nodeDef.getParent() : null;
        //boolean isNative = (parentDef != null)
        //        && NativeUtils.isNative(parentDef.getBaseName());
        if (emitName)
        {
            if (nodeDef != null)
                write(getEmitter().formatQualifiedName(nodeDef.getQualifiedName()));
            else
                write(node.getName());
        }
    }
    
    private boolean isBeingAssignedToVariable(IIdentifierNode node)
    {
    	IVariableNode varNode = (IVariableNode) node
        .getParent().getAncestorOfType(
        		IVariableNode.class);
    	if (varNode == null) return false;
    	
    	IExpressionNode avnode = varNode.getAssignedValueNode();
    	IASNode parent = node.getParent();
    	IMemberAccessExpressionNode parentMAE = null;
        IFunctionCallNode fnNode = null;
    	while (parent != varNode)
    	{
    		if (parent instanceof IMemberAccessExpressionNode)
            {
                parentMAE = (IMemberAccessExpressionNode) parent;
            }
    		else if (parent instanceof IFunctionCallNode)
            {
                fnNode = (IFunctionCallNode) parent;
            }
    		if (parent == avnode)
    		{
    			if (parentMAE != null)
    			{
                    // do one final check that this is the right node in
                    // a member access expression
    				return (node == parentMAE.getRightOperandNode());
    			}
    			return fnNode == null;
    		}
    		else     		
    			parent = parent.getParent();
    	}
    	return false;
    }

}

@@ -1698,319 +1698,79 @@
 as.I
-ExpressionNode;%0Aimport org.apache.flex.compiler.tree.as.IFunctionCallNode;%0Aimport org.apache.flex.compiler.tree.as.IFunctionObjectNode;%0Aimport org.apache.flex.compiler.tree.as.IIdentifierNode;%0Aimport org.apache.flex.compiler.tree.as.IMemberAccessExpressionNode;%0Aimport org.apache.flex.compiler.tree.as.IVariable
+FunctionObjectNode;%0Aimport org.apache.flex.compiler.tree.as.IIdentifier
 Node
@@ -2954,65 +2954,8 @@
  or%0A
-        %09// an instance method assigned to a variable or%0A
     
@@ -3315,239 +3315,8 @@
 ef)%0A
-                    .getFunctionClassification() == FunctionClassification.CLASS_MEMBER &&%0A                       isBeingAssignedToVariable(node))%0A                    %7C%7C (identifierIsPlainFunction && ((FunctionDefinition) nodeDef)%0A
     
@@ -4809,1160 +4809,8 @@
    %7D
-%0A    %0A    private boolean isBeingAssignedToVariable(IIdentifierNode node)%0A    %7B%0A    %09IVariableNode varNode = (IVariableNode) node%0A        .getParent().getAncestorOfType(%0A        %09%09IVariableNode.class);%0A    %09if (varNode == null) return false;%0A    %09%0A    %09IExpressionNode avnode = varNode.getAssignedValueNode();%0A    %09IASNode parent = node.getParent();%0A    %09IMemberAccessExpressionNode parentMAE = null;%0A        IFunctionCallNode fnNode = null;%0A    %09while (parent != varNode)%0A    %09%7B%0A    %09%09if (parent instanceof IMemberAccessExpressionNode)%0A            %7B%0A                parentMAE = (IMemberAccessExpressionNode) parent;%0A            %7D%0A    %09%09else if (parent instanceof IFunctionCallNode)%0A            %7B%0A                fnNode = (IFunctionCallNode) parent;%0A            %7D%0A    %09%09if (parent == avnode)%0A    %09%09%7B%0A    %09%09%09if (parentMAE != null)%0A    %09%09%09%7B%0A                    // do one final check that this is the right node in%0A                    // a member access expression%0A    %09%09%09%09return (node == parentMAE.getRightOperandNode());%0A    %09%09%09%7D%0A    %09%09%09return fnNode == null;%0A    %09%09%7D%0A    %09%09else     %09%09%0A    %09%09%09parent = parent.getParent();%0A    %09%7D%0A    %09return false;%0A    %7D
 %0A%0A%7D%0A

Revert "FLEX-34897 maintain scope when assigning methods to vars"