contract ClaimDF is Ownable {
    using SafeMath for uint256;

    /// @notice Token address to be allocated
    IERC20 public token;

    /// @notice Unlocked amount per stage
    uint256 public stageAmount;

    /// @notice Unlocked stage index
    uint256 public unlockedStageIndex;

    /// @notice Fix the unlock time of each stage
    uint256[] public unlockStage = [
        1624982400, // 2021-06-30
        1632931200, // 2021-09-30
        1640880000, // 2021-12-31
        1648569600, // 2022-03-30
        1656518400, // 2022-06-30
        1664467200, // 2022-09-30
        1672416000, // 2022-12-31
        1680105600 // 2023-03-30
    ];

    /// @dev Emitted when `token` is changed.
    event ClaimToken(IERC20 token);

    /// @dev Emitted when `stageAmount` is changed.
    event ClaimStageAmount(uint256 stageAmount);

    /// @dev Emitted when `unlockedStageIndex` is changed.
    event ClaimUnlockedStageIndex(uint256 oldUnlockedStageIndex, uint256 unlockedStageIndex);

    /// @param _token token address to be allocated
    /// @param _owner owner address
    /// @param _stageAmount unlocked amount per stage
    constructor(IERC20 _token, address _owner, uint256 _stageAmount) public {
        __Ownable_init(_owner);

        token = _token;
        emit ClaimToken(_token);

        stageAmount = _stageAmount;
        emit ClaimStageAmount(_stageAmount);
    }

    /**
     * @notice Get amount and stage index.
     * @dev Calculation unlocked amount and stage index by timestamp.
     * @param _timestamp Greenwich timestamp.
     * @return Unlocked amount; Unlocked stage index.
     */
    function _getClaimAmount(uint256 _timestamp) internal view returns (uint256, uint256) {

        uint256[] memory _unlockStage = unlockStage;
        uint256 _unlockedStageNum = unlockedStageIndex;
        while (_unlockedStageNum < _unlockStage.length && _timestamp > _unlockStage[_unlockedStageNum])
            _unlockedStageNum++;

        return (_unlockedStageNum.sub(unlockedStageIndex).mul(stageAmount), _unlockedStageNum);
    }

    /**
     * @notice Receive unlocked token.
     * @dev It must be the owner address to call.
     */
    function claim() external onlyOwner {
        (uint256 _amount, uint256 _unlockedStageIndex) = _getClaimAmount(block.timestamp);
        require(_unlockedStageIndex > unlockedStageIndex, "Not unlocked!");

        uint256 _oldUnlockedStageIndex = unlockedStageIndex;
        unlockedStageIndex = _unlockedStageIndex;
        emit ClaimUnlockedStageIndex(_oldUnlockedStageIndex, _unlockedStageIndex);

        token.transfer(owner, _amount);
    }

    /**
     * @notice Get claim info.
     * @dev Get unlocked amount and information for the next unlocking stage by timestamp.
     * @param _timestamp Greenwich timestamp.
     * @return  _amount : unlocked amount,
     *          _unlockedStageIndex : unlocking stage index,
     *          _stageTimestamp : unlocking stage timestamp.
     */
    function getClaimInfo(uint256 _timestamp) public view returns (uint256 _amount, uint256 _unlockedStageIndex, uint256 _stageTimestamp) {
        (_amount, _unlockedStageIndex) = _getClaimAmount(_timestamp);
        _stageTimestamp = _unlockedStageIndex == unlockStage.length ? unlockStage[_unlockedStageIndex - 1] : unlockStage[_unlockedStageIndex];
    }

    /**
     * @notice Get current claim info.
     * @dev Get unlocked amount and information for the next unlocking stage by block timestamp.
     * @return unlocked amount; unlocking stage index; unlocking stage timestamp.
     */
    function getClaimInfo() external view returns (uint256, uint256, uint256) {
        return getClaimInfo(block.timestamp);
    }

    /**
     * @dev Get the token balance of the contract
     * @return  Contract token balance.
     */
    function getTokenBalance() external view returns (uint256) {
        return token.balanceOf(address(this));
    }
}
function getClaimInfo() external view returns (uint256, uint256, uint256) {
    return getClaimInfo(block.timestamp);
}

/**
 * @notice Get current claim info.
 * @dev Get unlocked amount and information for the next unlocking stage by block timestamp.
 * @return unlocked amount; unlocking stage index; unlocking stage timestamp.
 */