# --------------------------------------------------------------------
# SPDX-License-Identifier: AGPL-3.0-or-later
# © Copyright 2008-2024 José Manuel Rodríguez de la Rosa and contributors.
# See the file CONTRIBUTORS.md for copyright details.
# See https://www.gnu.org/licenses/agpl-3.0.html for details.
# --------------------------------------------------------------------
from collections.abc import Callable
from typing import Self

from src.api import check

from .number import SymbolNUMBER
from .symbol_ import Symbol
from .type_ import SymbolTYPE


class SymbolBUILTIN(Symbol):
    """Defines a BUILTIN function e.g. INKEY$(), RND() or LEN"""

    def __init__(self, lineno, fname, type_=None, *operands):
        assert isinstance(lineno, int)
        assert type_ is None or isinstance(type_, SymbolTYPE)
        super().__init__(*operands)
        self.lineno = lineno
        self.fname = fname
        self.type_ = type_
        self.discard_result = False  # Whether to discard the return value of the function

    @property
    def type_(self):
        if self._type is not None:
            return self._type
        return self.operand.type_

    @type_.setter
    def type_(self, value):
        assert value is None or isinstance(value, SymbolTYPE)
        self._type = value

    @property
    def operand(self):
        return self.children[0] if self.children else None

    @operand.setter
    def operand(self, value):
        assert isinstance(value, Symbol)
        self.children[0] = value

    @property
    def operands(self):
        return self.children

    @operands.setter
    def operands(self, value):
        for x in value:
            assert isinstance(x, Symbol)
        self.children = list(value)

    @property
    def size(self):
        """sizeof(type)"""
        if self.type_ is None:
            return 0
        return self.type_.size

    @classmethod
    def make_node(
        cls,
        lineno: int,
        fname: str,
        func: Callable | None = None,
        type_: SymbolTYPE | None = None,
        *operands,
    ) -> Self | SymbolNUMBER:
        """Creates a node for a unary operation. E.g. -x or LEN(a$)

        Parameters:
            -func: function used on constant folding when possible
            -type_: the resulting type (by default, the same as the argument).
                For example, for LEN (str$), result type is 'u16'
                and arg type is 'string'
        """
        if func is not None and len(operands) == 1:  # Try constant-folding
            if check.is_number(operands[0]) or check.is_string(operands[0]):  # e.g. ABS(-5)
                return SymbolNUMBER(func(operands[0].value), type_=type_, lineno=lineno)

        return cls(lineno, fname, type_, *operands)
