/**
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
 */

package net.sourceforge.pmd.lang.java.xpath;

import java.util.Arrays;
import java.util.List;

import org.jaxen.Context;
import org.jaxen.Function;
import org.jaxen.FunctionCallException;
import org.jaxen.SimpleFunctionContext;
import org.jaxen.XPathFunctionContext;

import net.sourceforge.pmd.lang.ast.Node;
import net.sourceforge.pmd.lang.ast.xpath.Attribute;
import net.sourceforge.pmd.lang.java.ast.TypeNode;

public class TypeOfFunction implements Function {

    public static void registerSelfInSimpleContext() {
        ((SimpleFunctionContext) XPathFunctionContext.getInstance()).registerFunction(null, "typeof",
                new TypeOfFunction());
    }

    public Object call(Context context, List args) throws FunctionCallException {

        String nodeTypeName = null;
        String fullTypeName = null;
        String shortTypeName = null;
        Attribute attr = null;
        for (int i = 0; i < args.size(); i++) {
            if (args.get(i) instanceof List) {
                if (attr == null) {
                    attr = ((List<Attribute>) args.get(i)).get(0);
                    nodeTypeName = attr.getStringValue();
                } else {
                    throw new IllegalArgumentException(
                            "typeof function can take only a single argument which is an Attribute.");
                }
            } else {
                if (fullTypeName == null) {
                    fullTypeName = (String) args.get(i);
                } else if (shortTypeName == null) {
                    shortTypeName = (String) args.get(i);
                } else {
                    break;
                }
            }
        }
        if (fullTypeName == null) {
            throw new IllegalArgumentException(
                    "typeof function must be given at least one String argument for the fully qualified type name.");
        }
        Node n = (Node) context.getNodeSet().get(0);
        return typeof(n, nodeTypeName, fullTypeName, shortTypeName);
    }

    // TEST //ClassOrInterfaceType[typeof(@Image, 'java.lang.String')]
    public static boolean typeof(Node n, String nodeTypeName, String fullTypeName, String shortTypeName) {
        if (n instanceof TypeNode) {
            Class<?> type = ((TypeNode) n).getType();
            if (type == null) {
                return nodeTypeName != null
                        && (nodeTypeName.equals(fullTypeName) || nodeTypeName.equals(shortTypeName));
            }
            if (type.getName().equals(fullTypeName)) {
                return true;
            }
            List<Class<?>> implementors = Arrays.asList(type.getInterfaces());
            if (implementors.contains(type)) {
                return true;
            }
            Class<?> superC = type.getSuperclass();
            while (superC != null && !superC.equals(Object.class)) {
                if (superC.getName().equals(fullTypeName)) {
                    return true;
                }
                superC = superC.getSuperclass();
            }
        } else {
            throw new IllegalArgumentException("typeof function may only be called on a TypeNode.");
        }
        return false;
    }
}
