ThemisUISourceEntry.java

/*
 * Themis: Java Project Framework
 * Copyright 2026. Tony Washer
 *
 * Licensed 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 io.github.tonywasher.joceanus.themis.gui.source;

import io.github.tonywasher.joceanus.tethys.api.base.TethysUIIconId;
import io.github.tonywasher.joceanus.themis.gui.base.ThemisUIResource;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisDeclarationInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisExpressionInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisNodeInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisStatementInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisTypeInstance;
import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclClassInterface;
import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclEnum;
import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclEnumValue;
import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclMethod;
import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclRecord;
import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclaration;
import io.github.tonywasher.joceanus.themis.parser.expr.ThemisExpression;
import io.github.tonywasher.joceanus.themis.parser.node.ThemisNode;
import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeModifier;
import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeParameter;
import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeSimpleName;
import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeVariable;
import io.github.tonywasher.joceanus.themis.parser.stmt.ThemisStatement;
import io.github.tonywasher.joceanus.themis.parser.stmt.ThemisStmtClass;
import io.github.tonywasher.joceanus.themis.parser.stmt.ThemisStmtRecord;
import io.github.tonywasher.joceanus.themis.parser.type.ThemisType;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Source Panel Tree Entry.
 */
public class ThemisUISourceEntry {
    /**
     * Entry name prefix.
     */
    private static final String ENTRY_PREFIX = "TreeItem";

    /**
     * The Next entryId.
     */
    private static final AtomicInteger NEXT_ENTRY_ID = new AtomicInteger(1);

    /**
     * The Parent.
     */
    private final ThemisUISourceEntry theParent;

    /**
     * The id of the entry.
     */
    private final int theId;

    /**
     * The Child List.
     */
    private List<ThemisUISourceEntry> theChildList;

    /**
     * The unique name of the entry.
     */
    private final String theUniqueName;

    /**
     * The display name of the entry.
     */
    private final String theDisplayName;

    /**
     * The icon of the entry.
     */
    private final TethysUIIconId theIcon;

    /**
     * The object for the entry.
     */
    private final ThemisInstance theObject;

    /**
     * Constructor.
     *
     * @param pElement the sourceElement
     */
    ThemisUISourceEntry(final ThemisInstance pElement) {
        this(null, pElement);
    }

    /**
     * Constructor.
     *
     * @param pParent  the parent entry
     * @param pElement the sourceElement
     */
    ThemisUISourceEntry(final ThemisUISourceEntry pParent,
                        final ThemisInstance pElement) {
        /* Store parameters */
        theParent = pParent;
        theObject = pElement;

        /* Allocate id and unique name */
        theId = NEXT_ENTRY_ID.getAndIncrement();
        theUniqueName = ENTRY_PREFIX + theId;
        theDisplayName = getElementDisplay(pElement);
        theIcon = ThemisUISourceIcon.getElementIcon(pElement);

        /* If we have a parent */
        if (pParent != null) {
            /* Add the entry to the child list */
            pParent.addChild(this);
        }
    }

    /**
     * Get parent.
     *
     * @return the parent
     */
    ThemisUISourceEntry getParent() {
        return theParent;
    }

    /**
     * Get unique name.
     *
     * @return the name
     */
    String getUniqueName() {
        return theUniqueName;
    }

    /**
     * Get object.
     *
     * @return the object
     */
    ThemisInstance getObject() {
        return theObject;
    }

    @Override
    public String toString() {
        return theDisplayName;
    }

    /**
     * Obtain the icon.
     *
     * @return the icon
     */
    TethysUIIconId getIcon() {
        return theIcon;
    }

    /**
     * Get child iterator.
     *
     * @return the iterator
     */
    Iterator<ThemisUISourceEntry> childIterator() {
        return theChildList == null
                ? Collections.emptyIterator()
                : theChildList.iterator();
    }

    /**
     * Add child.
     *
     * @param pChild the child to add
     */
    private void addChild(final ThemisUISourceEntry pChild) {
        if (theChildList == null) {
            theChildList = new ArrayList<>();
        }
        theChildList.add(pChild);
    }

    @Override
    public boolean equals(final Object pThat) {
        /* Handle trivial cases */
        if (this == pThat) {
            return true;
        }
        if (pThat == null) {
            return false;
        }

        /* Check class */
        if (!(pThat instanceof ThemisUISourceEntry myThat)) {
            return false;
        }

        /* Must have same id */
        return theId == myThat.theId;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(theId);
    }

    /**
     * Obtain the displayName for the element.
     *
     * @param pElement the element
     * @return the displayName
     */
    static String getElementDisplay(final ThemisInstance pElement) {
        /* Switch on the element type */
        return switch (pElement) {
            case ThemisDeclarationInstance myDecl -> getDeclarationDisplay(myDecl);
            case ThemisNodeInstance myNode -> getNodeDisplay(myNode);
            case ThemisExpressionInstance myExpr -> getExpressionDisplay(myExpr);
            case ThemisStatementInstance myStmt -> getStatementDisplay(myStmt);
            case ThemisTypeInstance myType -> getTypeDisplay(myType);
            default -> throw new IllegalArgumentException("Unknown ThemisInstance");
        };
    }

    /**
     * Obtain the displayName for the declaration.
     *
     * @param pElement the element
     * @return the icon
     */
    private static String getDeclarationDisplay(final ThemisDeclarationInstance pElement) {
        /* Switch on the id */
        return switch ((ThemisDeclaration) pElement.getId()) {
            case ANNOTATION -> ThemisUIResource.SOURCEDECL_ANNOT.getValue();
            case ANNOTATIONMEMBER -> ThemisUIResource.SOURCEDECL_ANNOTMEMBER.getValue();
            case CLASSINTERFACE -> ((ThemisDeclClassInterface) pElement).getName();
            case COMPACT -> ThemisUIResource.SOURCEDECL_COMPACT.getValue();
            case CONSTRUCTOR -> ThemisUIResource.SOURCEDECL_CONSTRUCT.getValue();
            case ENUM -> ((ThemisDeclEnum) pElement).getName();
            case ENUMVALUE -> ((ThemisDeclEnumValue) pElement).getNode().getNameAsString();
            case FIELD -> ThemisUIResource.SOURCEDECL_FIELD.getValue();
            case INITIALIZER -> ThemisUIResource.SOURCEDECL_INIT.getValue();
            case METHOD -> ((ThemisDeclMethod) pElement).getName();
            case RECORD -> ((ThemisDeclRecord) pElement).getName();
            default -> throw new IllegalArgumentException("Unknown ThemisDeclaration");
        };
    }

    /**
     * Obtain the displayName for the node.
     *
     * @param pElement the element
     * @return the icon
     */
    private static String getNodeDisplay(final ThemisNodeInstance pElement) {
        /* Switch on the id */
        return switch ((ThemisNode) pElement.getId()) {
            case ARRAYLEVEL -> ThemisUIResource.SOURCENODE_ARRAYLVL.getValue();
            case CASE -> ThemisUIResource.SOURCENODE_CASE.getValue();
            case CATCH -> ThemisUIResource.SOURCENODE_CATCH.getValue();
            case COMMENT -> ThemisUIResource.SOURCENODE_COMMENT.getValue();
            case COMPILATIONUNIT -> ThemisUIResource.SOURCENODE_COMPUNIT.getValue();
            case IMPORT -> ThemisUIResource.SOURCENODE_IMPORT.getValue();
            case MODIFIER -> ((ThemisNodeModifier) pElement).getKeyword().toString();
            case NAME -> ThemisUIResource.SOURCENODE_NAME.getValue();
            case PACKAGE -> ThemisUIResource.SOURCENODE_PACKAGE.getValue();
            case PARAMETER -> ((ThemisNodeParameter) pElement).getNode().getNameAsString();
            case SIMPLENAME -> ((ThemisNodeSimpleName) pElement).getName();
            case VALUEPAIR -> ThemisUIResource.SOURCENODE_VALUEPAIR.getValue();
            case VARIABLE -> ((ThemisNodeVariable) pElement).getNode().getNameAsString();
            default -> throw new IllegalArgumentException("Unknown ThemisNode");
        };
    }

    /**
     * Obtain the displayName for the expression.
     *
     * @param pElement the element
     * @return the icon
     */
    private static String getExpressionDisplay(final ThemisExpressionInstance pElement) {
        /* Switch on the id */
        return switch ((ThemisExpression) pElement.getId()) {
            case ARRAYACCESS -> ThemisUIResource.SOURCEEXPR_ARRAYACCESS.getValue();
            case MARKER -> ThemisUIResource.SOURCEEXPR_MARKER.getValue();
            case NORMAL -> ThemisUIResource.SOURCEEXPR_NORMAL.getValue();
            case SINGLEMEMBER -> ThemisUIResource.SOURCEEXPR_SINGLE.getValue();
            case ARRAYCREATION -> ThemisUIResource.SOURCEEXPR_ARRAYCREATE.getValue();
            case CAST -> ThemisUIResource.SOURCEEXPR_CAST.getValue();
            case CHAR -> ThemisUIResource.SOURCEEXPR_CHAR.getValue();
            case ARRAYINIT -> ThemisUIResource.SOURCEEXPR_ARRAYINIT.getValue();
            case INTEGER -> ThemisUIResource.SOURCEEXPR_INTEGER.getValue();
            case ASSIGN -> ThemisUIResource.SOURCEEXPR_ASSIGN.getValue();
            case BINARY -> ThemisUIResource.SOURCEEXPR_BINARY.getValue();
            case BOOLEAN -> ThemisUIResource.SOURCEEXPR_BOOLEAN.getValue();
            case CLASS -> ThemisUIResource.SOURCEEXPR_CLASS.getValue();
            case CONDITIONAL -> ThemisUIResource.SOURCEEXPR_CONDITIONAL.getValue();
            case NULL -> ThemisUIResource.SOURCEEXPR_NULL.getValue();
            case DOUBLE -> ThemisUIResource.SOURCEEXPR_DOUBLE.getValue();
            case ENCLOSED -> ThemisUIResource.SOURCEEXPR_ENCLOSED.getValue();
            case FIELDACCESS -> ThemisUIResource.SOURCEEXPR_FIELDACCESS.getValue();
            case INSTANCEOF -> ThemisUIResource.SOURCEEXPR_INSTANCEOF.getValue();
            case LAMBDA -> ThemisUIResource.SOURCEEXPR_LAMBDA.getValue();
            case LONG -> ThemisUIResource.SOURCEEXPR_LONG.getValue();
            case METHODCALL -> ThemisUIResource.SOURCEEXPR_METHODCALL.getValue();
            case METHODREFERENCE -> ThemisUIResource.SOURCEEXPR_METHODREF.getValue();
            case NAME -> ThemisUIResource.SOURCEEXPR_NAME.getValue();
            case OBJECTCREATE -> ThemisUIResource.SOURCEEXPR_OBJCREATE.getValue();
            case STRING -> ThemisUIResource.SOURCEEXPR_STRING.getValue();
            case SUPER -> ThemisUIResource.SOURCEEXPR_SUPER.getValue();
            case SWITCH -> ThemisUIResource.SOURCEEXPR_SWITCH.getValue();
            case TEXTBLOCK -> ThemisUIResource.SOURCEEXPR_TEXT.getValue();
            case THIS -> ThemisUIResource.SOURCEEXPR_THIS.getValue();
            case TYPE -> ThemisUIResource.SOURCEEXPR_TYPE.getValue();
            case TYPEPATTERN -> ThemisUIResource.SOURCEEXPR_TYPEPATTERN.getValue();
            case UNARY -> ThemisUIResource.SOURCEEXPR_UNARY.getValue();
            case VARIABLE -> ThemisUIResource.SOURCEEXPR_VARIABLE.getValue();
            default -> throw new IllegalArgumentException("Unknown ThemisExpression");
        };
    }

    /**
     * Obtain the icon for the statement.
     *
     * @param pElement the element
     * @return the icon
     */
    private static String getStatementDisplay(final ThemisStatementInstance pElement) {
        /* Switch on the id */
        return switch ((ThemisStatement) pElement.getId()) {
            case ASSERT -> ThemisUIResource.SOURCESTMT_ASSERT.getValue();
            case BLOCK -> ThemisUIResource.SOURCESTMT_BLOCK.getValue();
            case BREAK -> ThemisUIResource.SOURCESTMT_BREAK.getValue();
            case LOCALCLASS -> getDeclarationDisplay(((ThemisStmtClass) pElement).getBody());
            case CONSTRUCTOR -> ThemisUIResource.SOURCESTMT_CONSTRUCT.getValue();
            case CONTINUE -> ThemisUIResource.SOURCESTMT_CONTINUE.getValue();
            case DO -> ThemisUIResource.SOURCESTMT_DO.getValue();
            case EMPTY -> ThemisUIResource.SOURCESTMT_EMPTY.getValue();
            case EXPRESSION -> ThemisUIResource.SOURCESTMT_EXPR.getValue();
            case FOR -> ThemisUIResource.SOURCESTMT_FOR.getValue();
            case FOREACH -> ThemisUIResource.SOURCESTMT_FOREACH.getValue();
            case IF -> ThemisUIResource.SOURCESTMT_IF.getValue();
            case LABELED -> ThemisUIResource.SOURCESTMT_LABELED.getValue();
            case LOCALRECORD -> getDeclarationDisplay(((ThemisStmtRecord) pElement).getBody());
            case RETURN -> ThemisUIResource.SOURCESTMT_RETURN.getValue();
            case SWITCH -> ThemisUIResource.SOURCESTMT_SWITCH.getValue();
            case SYNCHRONIZED -> ThemisUIResource.SOURCESTMT_SYNC.getValue();
            case THROW -> ThemisUIResource.SOURCESTMT_THROW.getValue();
            case TRY -> ThemisUIResource.SOURCESTMT_TRY.getValue();
            case WHILE -> ThemisUIResource.SOURCESTMT_WHILE.getValue();
            case YIELD -> ThemisUIResource.SOURCESTMT_YIELD.getValue();
            default -> throw new IllegalArgumentException("Unknown ThemisStatement");
        };
    }

    /**
     * Obtain the icon for the type.
     *
     * @param pElement the element
     * @return the icon
     */
    private static String getTypeDisplay(final ThemisTypeInstance pElement) {
        /* Switch on the id */
        return switch ((ThemisType) pElement.getId()) {
            case ARRAY -> ThemisUIResource.SOURCETYPE_ARRAY.getValue();
            case CLASSINTERFACE -> ThemisUIResource.SOURCETYPE_CLASS.getValue();
            case INTERSECTION -> ThemisUIResource.SOURCETYPE_INTERSECT.getValue();
            case PARAMETER -> ThemisUIResource.SOURCETYPE_PARAMETER.getValue();
            case PRIMITIVE -> ThemisUIResource.SOURCETYPE_PRIMITIVE.getValue();
            case UNION -> ThemisUIResource.SOURCETYPE_UNION.getValue();
            case UNKNOWN -> ThemisUIResource.SOURCETYPE_UNKNOWN.getValue();
            case VAR -> ThemisUIResource.SOURCETYPE_VAR.getValue();
            case VOID -> ThemisUIResource.SOURCETYPE_VOID.getValue();
            case WILDCARD -> ThemisUIResource.SOURCETYPE_WILDCARD.getValue();
            default -> throw new IllegalArgumentException("Unknown ThemisType");
        };
    }
}