# Wrap output parameters.
#
# Author: Malte J. Ziebarth (malte.ziebarth@tum.de)
#
# Copyright (C) 2025-2026 Technische Universität München
#
# Licensed under the EUPL, Version 1.2 or – as soon they will be approved by
# the European Commission - subsequent versions of the EUPL (the "Licence");
# You may not use this work except in compliance with the Licence.
# You may obtain a copy of the Licence at:
#
# https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the Licence is distributed on an "AS IS" basis,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the Licence for the specific language governing permissions and
# limitations under the Licence.
#
# SPDX-FileCopyrightText: 2025-2026 Technical University of Munich
# SPDX-License-Identifier: EUPL-1.2
# SPDX-FileContributor: Malte J. Ziebarth <malte.ziebarth@tum.de>

from ..language.abstract import Struct, Output, PureOutput
from ..language.fundamental import FLOAT_TYPES, INT_TYPES
from ..language.conversion import equivalent_ctypes_type
from ..language.python import try_ctype_base_type
from .parameter import PythonOutputParameter, CTypeParameterByRef, \
    CppParameter
from ..language.cpp import L1Type
from .variablegraph import VariableGraph, VariableGraphNode




def wrap_output_parameter(
        name: str,
        argument_type: Output[L1Type] | PureOutput[L1Type],
        vargraph: VariableGraph,
        root: VariableGraphNode | None,
        cpp_argument_id: int
    ):
    is_pure = isinstance(argument_type, PureOutput)
    cpp_type = argument_type.val
    ctype_type = equivalent_ctypes_type(cpp_type)
    depends_on: tuple[VariableGraphNode] \
        = tuple[VariableGraphNode]() if root is None else (root,)

    # 1. Handle floats and integers.
    is_float = ctype_type in FLOAT_TYPES
    is_int = ctype_type in INT_TYPES
    if is_float or is_int:
        ctype_base_type = try_ctype_base_type(ctype_type)
        if is_pure:
            rtype = ctype_base_type
        elif is_float:
            rtype = "float"
        else:
            rtype = "int"

        cout_name = vargraph.get_temporary_name(name)

        c_out = CTypeParameterByRef(
            cout_name,
            ctype_base_type,
            [
                f"    {cout_name} = {ctype_base_type}(0)"
            ]
        )
        vgn = vargraph.insert(c_out, *depends_on)

        # The C++ parameter:
        cpp_out = CppParameter.forward_output(c_out, cpp_argument_id)
        vgn = vargraph.insert(cpp_out, vgn)

        py_out = PythonOutputParameter(
            name,
            Output(rtype),
            [],
            [
                f"    {name} = {cout_name}.value"
            ]
        )
        vargraph.insert(py_out, vgn)

        return

    # 2. Structs.
    if isinstance(cpp_type, Struct):
        #
        # We create structs first as a Python output parameter.
        # Then we forward them, by reference, to the CDLL (since
        # the structs are native ctypes types).
        #
        assert isinstance(ctype_type, Struct)
        cout_name = vargraph.get_temporary_name(name)

        py_out = PythonOutputParameter(
            name,
            Output(ctype_type),
            [
                f"    {name} = {cpp_type.name}()"
            ],
            []
        )

        c_out = CTypeParameterByRef.forward(py_out)
        vgn = vargraph.insert(py_out, *depends_on)
        vgn = vargraph.insert(c_out, vgn)

        # The C++ parameter:
        cpp_out = CppParameter.forward_output(c_out, cpp_argument_id)
        vgn = vargraph.insert(cpp_out, vgn)


        return




    # 3. NotImplemented.
    raise NotImplementedError(
        f"Wrapping output of type '{cpp_type}' not implemented."
    )
