# C++ types
#
# Author: Malte J. Ziebarth (malte.ziebarth@tum.de)
#
# Copyright (C) 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: 2026 Technical University of Munich
# SPDX-License-Identifier: EUPL-1.2
# SPDX-FileContributor: Malte J. Ziebarth <malte.ziebarth@tum.de>

from .fundamental import (
    FundamentalBaseType, try_fundamental_base_type, is_fundamental_base_type
)
from typing import Literal
from .arrays import NDArray, QuantityArray
from .abstract import Const, Pointer, Struct, Input, Output, PureInput, PureOutput
#
# The fundamental type is a union of the fundamental base type
# and all its possible pointer / const modifications:
#
FundamentalType = FundamentalBaseType | Const["FundamentalType"] | Pointer["FundamentalType"]



#
# Structs can also have Struct members, so we need
# another recursive definition. Also, here we need
# const and pointer modifiers again:
#
L1Type = FundamentalBaseType | Struct["L1Type"] | Const["L1Type"] \
    | Pointer["L1Type"]


#
# To this recursive type, we add the NDArray and
# QuantityArray auto-generated wrappers, which shall
# be parameterized only by fundamental types:
#
L2Type = L1Type | NDArray | QuantityArray | Literal["std::string"]


#
# Arguments can then finally be Inputs or Outputs of
# the L2Type:
#
CppArgumentType = Input[L2Type] | Output[L2Type] \
    | PureInput[L1Type] | PureOutput[L1Type]



def assert_l1_type(t: L2Type) -> L1Type:
    """
    Ensures that a `CppArgumentType` is part of the `L1Type`
    subset.
    """
    t0 = try_fundamental_base_type(t)
    if t0:
        return t0

    raise TypeError(
        f"Argument '{t}' is not an L1Type."
    )


def is_cpp_type(t: CppArgumentType | L2Type) -> bool:
    if is_fundamental_base_type(t):
        return True
    if isinstance(t, (Input, Output, Const, Pointer)):
        return is_cpp_type(t.val)
    if isinstance(t, Struct):
        return all(is_cpp_type(mem) for mem,_ in t.members)
    if isinstance(t, (NDArray, QuantityArray)):
        return True
    if isinstance(t, str) and t == "std::string": # pyright: ignore[reportUnnecessaryIsInstance]
        return True
    return False