package com.android.commands.monkey.ape.jev;

import com.android.commands.monkey.ape.tree.GUITreeNode;

/**
 * Stable identity of one concrete action. Scope such as Activity or State is
 * deliberately supplied by the caller and is never folded into this value.
 */
public final class ConcreteActionIdentity {
    private final String actionType;
    private final String canonicalFullName;

    public ConcreteActionIdentity(String actionType, String canonicalFullName) {
        if (actionType == null || actionType.length() == 0)
            throw new IllegalArgumentException("actionType must not be empty");
        if (canonicalFullName == null || canonicalFullName.length() == 0)
            throw new IllegalArgumentException("canonicalFullName must not be empty");
        this.actionType = actionType;
        this.canonicalFullName = canonicalFullName;
    }

    public static ConcreteActionIdentity of(String actionType, GUITreeNode node) {
        if (node == null) throw new IllegalArgumentException("node must not be null");
        return new ConcreteActionIdentity(actionType, canonical(node));
    }

    /**
     * Production nodes use the exact native fullName.  Small JVM fixtures may
     * omit optional accessibility strings that the legacy Name renderer
     * assumes are non-null; use a deterministic attribute chain there rather
     * than making identity lookup crash or guessing from bounds.
     */
    public static String canonical(final GUITreeNode node) {
        // Observing an identity returns a string, never a native Name. Do not
        // assign global Name order to controls merely inspected by review.
        long start=JevReviewProfile.start();
        try {return com.android.commands.monkey.ape.naming.NameManager.withoutInterning(
            new java.util.function.Supplier<String>() {public String get(){return canonicalReadOnly(node);}});
        } finally {JevReviewProfile.end("canonical_render",start);}
    }

    private static String canonicalReadOnly(GUITreeNode node) {
        try { return JevIdentity.fullName(node).toXPath(); }
        catch (RuntimeException unavailable) {
            StringBuilder out=new StringBuilder("fallback:");
            for(GUITreeNode current=node;current!=null;current=current.getParent()) {
                out.append('[').append(String.valueOf(current.getClassName())).append('|')
                    .append(String.valueOf(current.getResourceID())).append('|').append(current.getIndex()).append('|')
                    .append(String.valueOf(JevIdentity.text(current))).append('|').append(String.valueOf(current.getContentDesc()))
                    .append('|').append(current.isClickable()).append('|').append(current.isLongClickable()).append('|')
                    .append(current.isCheckable()).append('|').append(current.isScrollable()).append(']');
            }
            return out.toString();
        }
    }

    public String actionType() { return actionType; }
    public String canonicalFullName() { return canonicalFullName; }

    @Override public int hashCode() {
        return 31 * actionType.hashCode() + canonicalFullName.hashCode();
    }

    @Override public boolean equals(Object other) {
        if (!(other instanceof ConcreteActionIdentity)) return false;
        ConcreteActionIdentity value = (ConcreteActionIdentity) other;
        return actionType.equals(value.actionType)
            && canonicalFullName.equals(value.canonicalFullName);
    }

    @Override public String toString() {
        return actionType + "|" + canonicalFullName;
    }
}
