class_name Set extends BST._Node

var data: Variant

func _init(data: Variant = null) -> void:
    super(_get_value(data))
    self.data = data

static func _get_value(value: Variant) -> int:
    match typeof(value):
        TYPE_NIL: return -1
        TYPE_INT: return value
        _: return hash(value)

static func empty() -> Set:
    return Set.new(null)

static func from_iter(iter: Variant) -> Set:
    var nodes: Array[_Node] = []
    for value in iter:
        nodes.append(Set.new(value))
    return BST._build_balanced_tree(nodes).unwrap()

static func compare(a: Set, b: Set) -> bool:
    return a.data < b.data

func balance() -> Set:
    var nodes = BST._store_in_order(Option.some(self))
    nodes.sort_custom(compare)
    return BST._build_balanced_tree(nodes).unwrap()


func is_empty() -> bool:
    return value == -1 and not has_children()

func contains(data: Variant) -> bool:
    return BST.search(self, _get_value(data)).is_some()

func insert(data: Variant) -> _Node:
    if is_empty():
        self.value = _get_value(data)
        self.data = data
        return self
    elif not contains(data):
        return BST._insert(Option.some(self), Set.new(data))
    else:
        return self

func remove(data: Variant) -> Option:
    return BST.remove(self, _get_value(data))

func get(data: Variant) -> Option:
    return BST.search(self, _get_value(data)).map(func(x: Set) -> Variant: return x.data)

func equals(other: Set) -> bool:
    if not size() == other.size(): return false

    for value in iter():
        if not other.contains(value): return false

    return true

func union(other: Set) -> Set:
    var u = Set.from_iter(iter())
    for value in other.iter():
        u.insert(value)
    return u.balance()

func intersect(other: Set) -> Set:
    var i = Set.empty()
    for value in iter():
        if other.contains(value):
            i.insert(value)
    return i.balance()

func difference(other: Set) -> Set:
    if self.is_empty(): return Set.empty()
    elif other.is_empty(): return Set.from_iter(iter())
    var diff = Set.empty()
    for value in iter():
        if not other.contains(value):
            diff.insert(value)
    return diff.balance()

static func _iter(_set: Set) -> Iterator:
    var iters = Option.collect_some([
        _set.left.map(_iter),
        Option.none if _set.value == -1 else Option.some(SingletonIterator.new(_set.data)),
        _set.right.map(_iter)
    ], true).unwrap()

    var iter_arr: Array[Iterator]
    iter_arr.assign(iters)

    return FusedIterator.new(iter_arr)

func iter() -> Iterator:
    return _iter(self)
