from . import current
from . import mosaic
from . import components
from uuid import UUID

"""
This file will be used to declare the functions used inside the __add__ method from component, and can be further used in
the __add__ methods from other classes to simplifly and modularize the code in the library
"""


def has_available_ports(component_obj) -> tuple[int, int]:
    """
    This function should be able to verify if a component has avaliable ports
    """
    if isinstance(component_obj, components.Component):
        pass
    else:
        raise TypeError(
            f"The object passed to this function isn't an object from component"
        )

    free_inputs: int = len(
        [
            port
            for port in component_obj.plugged_inputs
            if components.Port.is_generic_port(port) == True
        ]
    )

    free_outputs: int = len(
        [
            port
            for port in component_obj.plugged_outputs
            if components.Port.is_generic_port(port) == True
        ]
    )

    return free_inputs, free_outputs


def has_generic_ports(component_obj) -> bool:
    """
    This function is used to verify if a component has generic_ports
    """
    from . import components

    if isinstance(component_obj, components.Component):
        pass
    else:
        raise TypeError(
            f"The object passed to this function isn't an object from component"
        )

    bool = all(
        list(map(components.Port.is_generic_port, component_obj.component_ports))
    )
    return bool


"""
import components
def valid_location(arg_1: components.Component | current.Current, arg_2) -> bool:
    
    # This function shoulf be used in the __add__ method from component for organization purpouses of the code.
    # As soon as it's ready, this function must take the place of the statements inside the __add__ method used to check
    # if the location of both objects is valid.
    
    if isinstance(arg_1, components.Component) == True and isinstance(arg_2, components.Component) == True:
        if arg_1.location == None and arg_2.location == None:
            return True
        
        elif (arg_1.location == None and arg_2.location != None or arg_1.location != None and arg_2.location == None):
            return True
        
        elif (arg_1.location != None and arg_2.location != None):
            if arg_1.location == arg_2.location:
                return True
            else:
                print(f'\n\nThe locations of the component objects ({arg_1 = }, {arg_2 = }) doesn\'t match!\n\n')
                return False
            
    # elif isinstance(arg_1, current.Current) == True or isinstance(arg_2, current.Current):
    #     if (isinstance(arg_1, current.Current) == True and \
    #         isinstance(arg_2, components.Component) == True and \
    #         arg_2.location in arg_1.location):
    #         return True

    #     elif (isinstance(arg_2, current.Current) == True and\
    #         isinstance(arg_1, components.Component) == True and \
    #         arg_1.location in arg_1.location):
                return True
        
        elif isinstance(arg_1, current.Current) == True and isinstance(arg_2, current.Current):
            print(f'\n\nThe two objects being added ({arg_1 = }, {arg_2 = }) are Currents, and can\'t be added!') 
            return False
"""


def is_iterable(obj):
    "if the object is an iterable, the function returns True, if it doesn't it returns False"
    try:
        iter(obj)
        return True

    except TypeError:
        return False


def comp_in_mosaic(comp_id: UUID, mosaic_id: UUID) -> bool:
    for m in mosaic.Mosaic.mosaic_objects:
        if m.id == mosaic_id:
            for joint in m.joints_list:
                for obj in joint:
                    if (is_iterable(obj) == True) and (
                        all(isinstance(thing, UUID) == True for thing in obj)
                    ):
                        for some_id in obj:
                            if some_id == comp_id:
                                return True

        elif m.id != mosaic_id:
            pass

        else:
            raise AttributeError(
                f"\n The id of the mosaic was not found inside the list of all mosaics created on this run of the code"
            )
    else:
        return False


def comp_is_first_in_mosaic(comp_id: UUID, mosaic_id: UUID) -> bool:
    for m in mosaic.Mosaic.mosaic_objects:
        if m.id == mosaic_id:
            for joint_index, joint in enumerate(m.joints_list):
                for obj_index, obj in enumerate(joint):
                    if (
                        (is_iterable(obj) == True)
                        and (all(isinstance(thing, UUID) == True for thing in obj))
                        and (joint_index == 0)
                        and (obj_index == 0)
                    ):
                        for some_id in obj:
                            if some_id == comp_id:
                                return True

        elif m.id != mosaic_id:
            pass

        else:
            raise AttributeError(f"\n The id of the mosaic does not exist")

    else:
        return False

