"use strict";
// // Lifted from from https://github.com/facebook/react/blob/b76103d66fdb7396cbfcc66a032b31a0cd8ad342/packages/react-reconciler/src/ReactFiberTreeReflection.js
Object.defineProperty(exports, "__esModule", { value: true });
const HostRoot = 3; // Root of a host tree. Could be nested inside another node.
const HostPortal = 4; // A subtree. Could be an entry point to a different renderer.
const HostComponent = 5;
const HostText = 6;
const Placement = 0b0000000000000000000000010;
const Hydrating = 0b0000000000001000000000000;
const NoFlags = 0b0000000000000000000000000;
function getNearestMountedFiber(fiber) {
    let node = fiber;
    let nearestMounted = fiber;
    if (!fiber.alternate) {
        // If there is no alternate, this might be a new tree that isn't inserted
        // yet. If it is, then it will have a pending insertion effect on it.
        let nextNode = node;
        do {
            node = nextNode;
            if ((node.flags & (Placement | Hydrating)) !== NoFlags) {
                // This is an insertion or in-progress hydration. The nearest possible
                // mounted fiber is the parent but we need to continue to figure out
                // if that one is still mounted.
                nearestMounted = node.return;
            }
            nextNode = node.return;
        } while (nextNode);
    }
    else {
        while (node.return) {
            node = node.return;
        }
    }
    if (node.tag === HostRoot) {
        // TODO: Check if this was a nested HostRoot when used with
        // renderContainerIntoSubtree.
        return nearestMounted;
    }
    // If we didn't hit the root, that means that we're in an disconnected tree
    // that has been unmounted.
    return null;
}
function findCurrentFiberUsingSlowPath(fiber) {
    const alternate = fiber.alternate;
    if (!alternate) {
        // If there is no alternate, then we only need to check if it is mounted.
        const nearestMounted = getNearestMountedFiber(fiber);
        if (nearestMounted === null) {
            throw new Error('Unable to find node on an unmounted component.');
        }
        if (nearestMounted !== fiber) {
            return null;
        }
        return fiber;
    }
    // If we have two possible branches, we'll walk backwards up to the root
    // to see what path the root points to. On the way we may hit one of the
    // special cases and we'll deal with them.
    let a = fiber;
    let b = alternate;
    while (true) {
        const parentA = a.return;
        if (parentA === null) {
            // We're at the root.
            break;
        }
        const parentB = parentA.alternate;
        if (parentB === null) {
            // There is no alternate. This is an unusual case. Currently, it only
            // happens when a Suspense component is hidden. An extra fragment fiber
            // is inserted in between the Suspense fiber and its children. Skip
            // over this extra fragment fiber and proceed to the next parent.
            const nextParent = parentA.return;
            if (nextParent !== null) {
                a = b = nextParent;
                continue;
            }
            // If there's no parent, we're at the root.
            break;
        }
        // If both copies of the parent fiber point to the same child, we can
        // assume that the child is current. This happens when we bailout on low
        // priority: the bailed out fiber's child reuses the current child.
        if (parentA.child === parentB.child) {
            let child = parentA.child;
            while (child) {
                if (child === a) {
                    // We've determined that A is the current branch.
                    //  assertIsMounted(parentA);
                    return fiber;
                }
                if (child === b) {
                    // We've determined that B is the current branch.
                    //  assertIsMounted(parentA);
                    return alternate;
                }
                child = child.sibling;
            }
            // We should never have an alternate for any mounting node. So the only
            // way this could possibly happen is if this was unmounted, if at all.
            throw new Error('Unable to find node on an unmounted component.');
        }
        if (a.return !== b.return) {
            // The return pointer of A and the return pointer of B point to different
            // fibers. We assume that return pointers never criss-cross, so A must
            // belong to the child set of A.return, and B must belong to the child
            // set of B.return.
            a = parentA;
            b = parentB;
        }
        else {
            // The return pointers point to the same fiber. We'll have to use the
            // default, slow path: scan the child sets of each parent alternate to see
            // which child belongs to which set.
            //
            // Search parent A's child set
            let didFindChild = false;
            let child = parentA.child;
            while (child) {
                if (child === a) {
                    didFindChild = true;
                    a = parentA;
                    b = parentB;
                    break;
                }
                if (child === b) {
                    didFindChild = true;
                    b = parentA;
                    a = parentB;
                    break;
                }
                child = child.sibling;
            }
            if (!didFindChild) {
                // Search parent B's child set
                child = parentB.child;
                while (child) {
                    if (child === a) {
                        didFindChild = true;
                        a = parentB;
                        b = parentA;
                        break;
                    }
                    if (child === b) {
                        didFindChild = true;
                        b = parentB;
                        a = parentA;
                        break;
                    }
                    child = child.sibling;
                }
                if (!didFindChild) {
                    throw new Error('Child was not found in either parent set. This indicates a bug ' +
                        'in React related to the return pointer. Please file an issue.');
                }
            }
        }
        if (a.alternate !== b) {
            throw new Error('Return fibers should always be each others\' alternates. ' +
                'This error is likely caused by a bug in React. Please file an issue.');
        }
    }
    // If the root is not a host container, we're in a disconnected tree. I.e.
    // unmounted.
    if (a.tag !== HostRoot) {
        throw new Error('Unable to find node on an unmounted component.');
    }
    if (a.stateNode.current === a) {
        // We've determined that A is the current branch.
        return fiber;
    }
    // Otherwise B has to be current branch.
    return alternate;
}
function findCurrentHostFiberImpl(node) {
    // Next we'll drill down this component to find the first HostComponent/Text.
    if (node.tag === HostComponent || node.tag === HostText) {
        return node;
    }
    let child = node.child;
    while (child !== null) {
        const match = findCurrentHostFiberImpl(child);
        if (match !== null) {
            return match;
        }
        child = child.sibling;
    }
    return null;
}
function findCurrentHostFiberWithNoPortalsImpl(node) {
    // Next we'll drill down this component to find the first HostComponent/Text.
    if (node.tag === HostComponent || node.tag === HostText) {
        return node;
    }
    let child = node.child;
    while (child !== null) {
        if (child.tag !== HostPortal) {
            const match = findCurrentHostFiberWithNoPortalsImpl(child);
            if (match !== null) {
                return match;
            }
        }
        child = child.sibling;
    }
    return null;
}
exports.default = findCurrentFiberUsingSlowPath;
