contract RebalancingFailure is
    RebalancingSetState,
    RebalancingSettlement
{
    using SafeMath for uint256;
    using AddressArrayUtils for address[];

    /* ============ Internal Functions ============ */

    /*
     * Validations for failRebalance:
     *  - State is Rebalance
     *  - Either liquidator recognizes failure OR fail period breached on RB Set
     *
     * @param _quantity                 The amount of currentSet to be rebalanced
     */
    function validateFailRebalance()
        internal
        view
    {
        // Token must be in Rebalance State
        validateRebalanceStateIs(RebalancingLibrary.State.Rebalance);

        // Failure triggers must be met
        require(
            liquidatorBreached() || failPeriodBreached(),
            "Triggers not breached"
        );
    }

    /*
     * Determine the new Rebalance State. If there has been a bid, then we put it to
     * Drawdown, where the Set is effectively killed. If no bids, we reissue the currentSet.
     */
    function getNewRebalanceState()
        internal
        view
        returns (RebalancingLibrary.State)
    {
        return hasBidded ? RebalancingLibrary.State.Drawdown : RebalancingLibrary.State.Default;
    }

    /*
     * Update state based on new Rebalance State.
     *
     * @param  _newRebalanceState      The new State to transition to
     */
    function transitionToNewState(
        RebalancingLibrary.State _newRebalanceState
    )
        internal
    {
        reissueSetIfRevertToDefault(_newRebalanceState);

        setWithdrawComponentsIfDrawdown(_newRebalanceState);

        rebalanceState = _newRebalanceState;
        rebalanceIndex = rebalanceIndex.add(1);
        lastRebalanceTimestamp = block.timestamp;

        nextSet = ISetToken(address(0));
        hasBidded = false;
    }

    /* ============ Private Functions ============ */

    /*
     * Returns whether the liquidator believes the rebalance has failed.
     *
     * @return        If liquidator thinks rebalance failed
     */
    function liquidatorBreached()
        private
        view
        returns (bool)
    {
        return liquidator.hasRebalanceFailed(address(this));
    }

    /*
     * Returns whether the the fail time has elapsed, which means that a period
     * of time where the auction should have succeeded has not.
     *
     * @return        If fail period has passed on Rebalancing Set Token
     */
    function failPeriodBreached()
        private
        view
        returns(bool)
    {
        uint256 rebalanceFailTime = rebalanceStartTime.add(rebalanceFailPeriod);

        return block.timestamp >= rebalanceFailTime;
    }

    /*
     * If the determination is Default State, reissue the Set.
     */
    function reissueSetIfRevertToDefault(
        RebalancingLibrary.State _newRebalanceState
    )
        private
    {
        if (_newRebalanceState ==  RebalancingLibrary.State.Default) {
            uint256 issueQuantity = calculateSetIssueQuantity(currentSet);

            // If bid not placed, reissue current Set
            core.issueInVault(
                address(currentSet),
                issueQuantity
            );
        }
    }

    /*
     * If the determination is Drawdown State, set the drawdown components which is the union of
     * the current and next Set components.
     */
    function setWithdrawComponentsIfDrawdown(
        RebalancingLibrary.State _newRebalanceState
    )
        private
    {
        if (_newRebalanceState ==  RebalancingLibrary.State.Drawdown) {
            address[] memory currentSetComponents = currentSet.getComponents();
            address[] memory nextSetComponents = nextSet.getComponents();

            failedRebalanceComponents = currentSetComponents.union(nextSetComponents);
        }
    }
}
/**
 * @title RebalancingFailure
 * @author Set Protocol
 *
 */
function validateFailRebalance()
    internal
    view
{
    // Token must be in Rebalance State
    validateRebalanceStateIs(RebalancingLibrary.State.Rebalance);

    // Failure triggers must be met
    require(
        liquidatorBreached() || failPeriodBreached(),
        "Triggers not breached"
    );
}

/*
 * Validations for failRebalance:
 *  - State is Rebalance
 *  - Either liquidator recognizes failure OR fail period breached on RB Set
 *
 * @param _quantity                 The amount of currentSet to be rebalanced
 */