"""
symbolite.ops._substitute
~~~~~~~~~~~~~~~~~~~~~~~~~

Replace symbols, functions, values, etc by others.

:copyright: 2023 by Symbolite Authors, see AUTHORS for more details.
:license: BSD, see LICENSE for more details.
"""

from collections.abc import Mapping
from functools import singledispatch
from typing import Any

from ..core.call import Call
from ..core.symbolite_object import get_symbolite_info
from ..core.value import Name, Value


@singledispatch
def substitute(obj: Any, replacements: Mapping[Any, Any]) -> Any:
    """Replace symbols, functions, values, etc by others.

    Parameters
    ----------
    obj
        symbolic expression.
    replacements
        replacement dictionary.
    """
    return replacements.get(obj, obj)


@substitute.register(Value)
def substitute_value[R: Value[Any]](obj: R, mapper: Mapping[Any, Any]) -> R:
    info = get_symbolite_info(obj)
    if isinstance(info.value, Name):
        return mapper.get(obj, obj)
    return obj.__class__(substitute(info.value, mapper))


@substitute.register
def substitue_call(obj: Call, mapper: Mapping[Any, Any]) -> Call:
    info = get_symbolite_info(obj)
    func = mapper.get(info.func, info.func)
    args = tuple(substitute(arg, mapper) for arg in info.args)
    kwargs = {k: substitute(arg, mapper) for k, arg in info.kwargs_items}

    return Call(func, args, tuple(kwargs.items()))
