class_name KCUtils

static func remove_children(node: Node, delete: bool = true):
    for i in range(node.get_child_count() - 1, -1, -1):
        var child = node.get_child(i)
        node.remove_child(child)
        if delete:
            child.queue_free()

static func get_class_name(value: Object) -> String:
    match value.get_script():
        var script when script != null:
            match script.get_global_name():
                var name when name != null: return name
                _: return script.get_instance_base_type()
        _: return value.get_class()

static func is_string_like(value: Variant) -> bool:
    match typeof(value):
        TYPE_STRING: return true
        TYPE_STRING_NAME: return true
        TYPE_NODE_PATH: return true
        _: return false

static func to_str(value: Variant) -> String:
    match typeof(value):
        TYPE_OBJECT:
            if value.has_method('_to_string'):
                return value.to_string()
            else:
                var name = get_class_name(value)
                value = value as Object
                var props: Dictionary = inst_to_dict(value)
                return "%s %s" % [name, to_str(props)]
        _: return str(value)

static func _always_pass(_any: Variant) -> bool:
    return true

static  func notify(node: Node, notification: int, up: bool = false):
    notify_while(node, notification, _always_pass, up)

static func notify_while(node: Node, notification: int, predicate: Callable = _always_pass, up: bool = false):
    node.notification(notification)

    if up:
        var parent = node.get_parent()
        if parent != null and predicate.call(parent):
            notify_while(parent, notification, predicate, up)
    else:
        for child in node.get_children():
            if predicate.call(child):
                notify_while(child, notification, predicate)

static func propagate(node: Node, fn: StringName, args: Array, call_on_self: bool = true):
    if call_on_self and node.has_method(fn):
        node.callv(fn, args)

    for child in node.get_children():
        propagate(child, fn, args)

static func propagate_input_event(node: Node, fn: StringName, event: InputEvent, call_on_self: bool = true):
    if node.get_viewport().is_input_handled() or event.is_canceled():
        return

    if call_on_self and node.has_method(fn):
        node.callv(fn, [event])

    for child in node.get_children():
        propagate_input_event(child, fn, event)

static func get_size(value: Variant) -> Result:
    match typeof(value):
        TYPE_STRING | TYPE_VECTOR2 | TYPE_VECTOR2I | TYPE_VECTOR3 | TYPE_VECTOR3I | TYPE_VECTOR4 | TYPE_VECTOR4I | TYPE_QUATERNION:
            return Result.ok(value.length())
        var x when x >= 27 and x <= 38:
            return Result.ok(value.size())
        _: return Result.err(Error.NotImplemented.new('"length" or "size"'))

static func len(value: Variant) -> int:
    return get_size(value).unwrap()
