ThemisMapperNameState.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.solver.mapper;

import io.github.tonywasher.joceanus.oceanus.base.OceanusException;
import io.github.tonywasher.joceanus.themis.exc.ThemisDataException;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisClassInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisDeclarationInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisId;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisNodeInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisInstance.ThemisTypeInstance;
import io.github.tonywasher.joceanus.themis.parser.base.ThemisModifierList;
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.ThemisDeclField;
import io.github.tonywasher.joceanus.themis.parser.decl.ThemisDeclaration;
import io.github.tonywasher.joceanus.themis.parser.expr.ThemisExprTypePattern;
import io.github.tonywasher.joceanus.themis.parser.node.ThemisNodeParameter;
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.type.ThemisTypeClassInterface;
import io.github.tonywasher.joceanus.themis.solver.reflect.ThemisReflectExternal;

import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.Map;

/**
 * Name state.
 */
public class ThemisMapperNameState {
    /**
     * The nameState stack.
     */
    private final Deque<ThemisMapperNameMap> theStack;

    /**
     * The active nameState.
     */
    private ThemisMapperNameMap theName;

    /**
     * Constructor.
     */
    ThemisMapperNameState() {
        /* create stack */
        theStack = new ArrayDeque<>();

        /* Create initial Name Map */
        theName = new ThemisMapperNameMap();
    }

    /**
     * Reset.
     */
    void reset() {
        theStack.clear();
        theName = new ThemisMapperNameMap();
    }

    /**
     * Process the start of an instance.
     *
     * @param pInstance the instance
     * @return should cleanUp be called after processing this instance?
     * @throws OceanusException on error
     */
    boolean processInstance(final ThemisInstance pInstance) throws OceanusException {
        /* Determine whether we should bump the stack */
        final boolean doBump = bumpStack(pInstance);
        if (doBump) {
            bumpStack();
        }

        /* Process interesting elements */
        if (pInstance instanceof ThemisClassInstance myClass) {
            processClass(myClass);
        }
        if (pInstance instanceof ThemisDeclEnum myEnum) {
            return processEnum(myEnum);
        }
        if (pInstance instanceof ThemisNodeVariable myVar) {
            return processVariable(myVar);
        }
        if (pInstance instanceof ThemisNodeParameter myParam) {
            return processParameter(myParam);
        }
        if (pInstance instanceof ThemisExprTypePattern myPattern) {
            return processPattern(myPattern);
        }

        /* Return bump indication */
        return doBump;
    }

    /**
     * Bump the stack.
     */
    private void bumpStack() {
        theStack.push(theName);
        theName = new ThemisMapperNameMap(theName);
    }

    /**
     * cleanUp after stacked instance.
     */
    void cleanUpAfterInstance() {
        theName = theStack.pop();
    }

    /**
     * Look up type.
     *
     * @param pName the name of the type
     * @return the type
     */
    ThemisTypeInstance lookUpName(final String pName) {
        return theName.lookUpName(pName);
    }

    /**
     * Process enumValue.
     *
     * @param pElement the element
     * @return false
     */
    private boolean processEnum(final ThemisDeclEnum pElement) {
        final ThemisMapperTypeRef myRef = new ThemisMapperTypeRef(pElement);
        for (ThemisDeclarationInstance myInstance : pElement.getValues()) {
            final ThemisDeclEnumValue myValue = (ThemisDeclEnumValue) myInstance;
            theName.declareEnum(myRef, myValue);
        }
        return false;
    }

    /**
     * Process variableDecl element.
     *
     * @param pElement the element
     * @return false
     */
    private boolean processVariable(final ThemisNodeVariable pElement) {
        theName.declareVariable(pElement);
        return false;
    }

    /**
     * Process parameter.
     *
     * @param pElement the parameter
     * @return false
     */
    private boolean processParameter(final ThemisNodeParameter pElement) {
        theName.declareParameter(pElement);
        return false;
    }

    /**
     * Process pattern element.
     *
     * @param pElement the element
     * @return false
     */
    private boolean processPattern(final ThemisExprTypePattern pElement) {
        theName.declarePattern(pElement);
        return false;
    }

    /**
     * Process class element.
     *
     * @param pClass the element
     * @throws OceanusException on error
     */
    private void processClass(final ThemisClassInstance pClass) throws OceanusException {
        for (ThemisTypeInstance myClass : pClass.getExtends()) {
            final ThemisTypeClassInterface myExtend = (ThemisTypeClassInterface) myClass;
            ThemisClassInstance myClassInstance = myExtend.getClassInstance();
            if (myClassInstance instanceof ThemisReflectExternal myExternal) {
                myClassInstance = myExternal.getClassInstance();
            }
            if (myClassInstance == null) {
                throw new ThemisDataException("Class Instance not found: " + pClass.getFullName());
            }

            /* Loop through all the field declarations */
            for (ThemisInstance myChild : myClassInstance.getBody()) {
                if (myChild instanceof ThemisDeclField myField) {
                    processField(myField);
                }
            }

            /* Follow the chain */
            processClass(myClassInstance);
        }
    }

    /**
     * Process field element.
     *
     * @param pField the element
     */
    private void processField(final ThemisDeclField pField) {
        /* If the field is public or protected */
        final ThemisModifierList myModifiers = pField.getModifiers();
        if (myModifiers.isProtected() || myModifiers.isPublic()) {
            for (ThemisNodeInstance myVarNode : pField.getVariables()) {
                final ThemisNodeVariable myVar = (ThemisNodeVariable) myVarNode;
                processVariable(myVar);
            }
        }
    }

    /**
     * Should an instance bump the name stack?
     *
     * @param pInstance the instance
     * @return true/false
     */
    private boolean bumpStack(final ThemisInstance pInstance) {
        /* Process interesting ids */
        final ThemisId myId = pInstance.getId();
        if (myId instanceof ThemisDeclaration myDeclType) {
            return bumpStack(myDeclType);
        }
        if (myId instanceof ThemisStatement myStmtType) {
            return bumpStack(myStmtType);
        }
        return false;
    }

    /**
     * Should a declaration bump the name stack?
     *
     * @param pDeclType the declaration type
     * @return true/false
     */
    private boolean bumpStack(final ThemisDeclaration pDeclType) {
        return switch (pDeclType) {
            case CLASSINTERFACE, ENUM, RECORD, METHOD, CONSTRUCTOR -> true;
            default -> false;
        };
    }

    /**
     * Should a statement bump the name stack?
     *
     * @param pStmtType the declaration type
     * @return true/false
     */
    private boolean bumpStack(final ThemisStatement pStmtType) {
        return switch (pStmtType) {
            case BLOCK, FOR, FOREACH, TRY -> true;
            default -> false;
        };
    }

    /**
     * The cascading map of names.
     */
    private static class ThemisMapperNameMap {
        /**
         * The parent map.
         */
        private final ThemisMapperNameMap theParent;

        /**
         * The type variables map.
         */
        private final Map<String, ThemisTypeInstance> theNames;

        /**
         * Constructor.
         */
        ThemisMapperNameMap() {
            this(null);
        }

        /**
         * Constructor.
         *
         * @param pParent the parent
         */
        ThemisMapperNameMap(final ThemisMapperNameMap pParent) {
            theParent = pParent;
            theNames = new HashMap<>();
        }

        /**
         * Declare enumValue.
         *
         * @param pRef  the reference
         * @param pEnum the enumValue
         */
        void declareEnum(final ThemisMapperTypeRef pRef,
                         final ThemisDeclEnumValue pEnum) {
            theNames.put(pEnum.getName().toString(), pRef);
        }

        /**
         * Declare variable.
         *
         * @param pVar the variable declarator
         */
        void declareVariable(final ThemisNodeVariable pVar) {
            theNames.put(pVar.getName().toString(), pVar.getType());
        }

        /**
         * Declare parameter.
         *
         * @param pParam the parameter
         */
        void declareParameter(final ThemisNodeParameter pParam) {
            theNames.put(pParam.getName().toString(), pParam.getType());
        }

        /**
         * Declare pattern.
         *
         * @param pPattern the pattern
         */
        void declarePattern(final ThemisExprTypePattern pPattern) {
            theNames.put(pPattern.getName().toString(), pPattern.getType());
        }

        /**
         * Look up name.
         *
         * @param pName the name of the variable
         * @return the type
         */
        ThemisTypeInstance lookUpName(final String pName) {
            /* Look up name in map */
            final ThemisTypeInstance myType = theNames.get(pName);
            if (myType != null) {
                return myType;
            }

            /* If we did not find the name, try the parent map */
            return theParent == null ? null : theParent.lookUpName(pName);
        }
    }
}