contract DailyVesting {
    using SafeERC20Upgradeable for IERC20Upgradeable;
    using SafeMathUpgradeable for uint256;

    struct VestingPeriod {
        uint128 vestingDays;
        uint128 tokensPerDay;
    }

    struct VestingClaimInfo {
        uint128 lastClaim;
        uint128 daysClaimed;
    }

    //token to be distributed
    IERC20Upgradeable public token;
    //handles setup
    address public setupAdmin;
    //UTC timestamp from which first vesting period begins (i.e. tokens will first be released 30 days after this time)
    uint256 public startTime;
    //total token obligations from all unpaid vesting amounts
    uint256 public totalObligations;
    //keeps track of contract state
    bool public setupComplete;
    // how long to wait before unlocking admin access
    uint256 public adminAccessDelay;

    //list of all beneficiaries
    address[] public beneficiaries;

    //amount of tokens to be received by each beneficiary
    mapping(address => VestingPeriod) public vestingPeriods;
    mapping(address => VestingClaimInfo) public claimInfo;
    //tracks if addresses have already been added as beneficiaries or not
    mapping(address => bool) public beneficiaryAdded;

    event SetupCompleted();
    event BeneficiaryAdded(address indexed user, uint256 totalAmountToClaim);
    event TokensClaimed(address indexed user, uint256 amount); 

    modifier setupOnly() {
        require(!setupComplete, "setup already completed");
        _;
    }

    modifier claimAllowed() {
        require(setupComplete, "setup ongoing");
        _;
    }

    modifier onlyAdmin() {
        require(msg.sender == setupAdmin, "not admin");
        _;
    }

    modifier whenAdminAccessAvailable() {
        uint256 _startTime = startTime;

        require(_startTime < block.timestamp && block.timestamp - _startTime > adminAccessDelay, "admin access is not active");
        _;
    }

    constructor(
        IERC20Upgradeable _token,
        address _owner,
        uint256 _startTime,
        uint256 _adminAccessDelay
    ) public {
        adminAccessDelay = _adminAccessDelay;
        token = _token;
        setupAdmin = msg.sender;
        startTime = _startTime == 0 ? block.timestamp : _startTime;
    }

    // adds a list of beneficiaries
    function addBeneficiaries(address[] memory _beneficiaries, VestingPeriod[] memory _vestingPeriods)
        external
        onlyAdmin
        setupOnly
    {
        _setBeneficiaries(_beneficiaries, _vestingPeriods);
    }

    function tokensToClaim(address _beneficiary) public view returns(uint256) {        
        (uint256 tokensAmount,) = _tokensToClaim(_beneficiary, claimInfo[_beneficiary]);
        return tokensAmount;
    }

    /**
        @dev This function returns tokensAmount available to claim. Calculates it based on several vesting periods if applicable.
     */
    function _tokensToClaim(address _beneficiary, VestingClaimInfo memory claim) private view returns(uint256 tokensAmount, uint256 daysClaimed) {
        uint256 lastClaim = claim.lastClaim == 0 ? startTime : claim.lastClaim;

        if (lastClaim > block.timestamp) { // last claim was in the future (means startTime was set in the future)
            return (0, 0);
        }

        uint256 daysElapsed = (block.timestamp - lastClaim) / 1 days;
        VestingPeriod memory vestingPeriod = vestingPeriods[_beneficiary];

        uint256 daysInPeriodToClaim = vestingPeriod.vestingDays - claim.daysClaimed;
        if (daysInPeriodToClaim > daysElapsed) {
            daysInPeriodToClaim = daysElapsed;
        }

        tokensAmount = tokensAmount.add(
            uint256(vestingPeriod.tokensPerDay).mul(daysInPeriodToClaim)
        );

        daysElapsed -= daysInPeriodToClaim;
        daysClaimed += daysInPeriodToClaim;
    }

    // claims vested tokens for a given beneficiary
    function claimFor(address _beneficiary) external claimAllowed {
        _processClaim(_beneficiary);
    }

    // convenience function for beneficiaries to call to claim all of their vested tokens
    function claimForSelf() external claimAllowed {
        _processClaim(msg.sender);
    }

    // claims vested tokens for all beneficiaries
    function claimForAll() external claimAllowed {
        for (uint256 i = 0; i < beneficiaries.length; i++) {
            _processClaim(beneficiaries[i]);
        }
    }

    // complete setup once all obligations are met, to remove the ability to
    // reclaim tokens until vesting is complete, and allow claims to start
    function endSetup() external onlyAdmin setupOnly {
        uint256 tokenBalance = token.balanceOf(address(this));
        require(tokenBalance >= totalObligations, "obligations not yet met");
        setupComplete = true;
        setupAdmin = address(0);
        emit SetupCompleted();
    }

    // reclaim tokens if necessary prior to finishing setup. otherwise reclaim any
    // extra tokens after the end of vesting
    function reclaimTokens() external onlyAdmin setupOnly {
        token.transfer(setupAdmin, token.balanceOf(address(this)));
    }

    function emergencyReclaimTokens() external onlyAdmin whenAdminAccessAvailable {
        token.transfer(setupAdmin, token.balanceOf(address(this)));
    }

    function emergencyOverrideBenefeciaries(address[] memory _beneficiaries, VestingPeriod[] memory _vestingPeriods) external onlyAdmin whenAdminAccessAvailable {
        for (uint256 i = 0; i < _beneficiaries.length; i++) {
            beneficiaryAdded[_beneficiaries[i]] = false;
        }
        _setBeneficiaries(_beneficiaries, _vestingPeriods);
    }

    // Calculates the claimable tokens of a beneficiary and sends them.
    function _processClaim(address _beneficiary) internal {
        VestingClaimInfo memory claim = claimInfo[_beneficiary];
        (uint256 amountToClaim, uint256 daysClaimed) = _tokensToClaim(_beneficiary, claim);

        if (amountToClaim == 0) {
            return;
        }

        claim.daysClaimed += uint128(daysClaimed);
        claim.lastClaim = uint128(block.timestamp);
        claimInfo[_beneficiary] = claim;

        _sendTokens(_beneficiary, amountToClaim);

        emit TokensClaimed(_beneficiary, amountToClaim);
    }

    // send tokens to beneficiary and remove obligation
    function _sendTokens(address _beneficiary, uint256 _amountToSend) internal {
        totalObligations -= _amountToSend;
        token.transfer(_beneficiary, _amountToSend);
    }

    function _setBeneficiaries(address[] memory _beneficiaries, VestingPeriod[] memory _vestingPeriods) private {
        require(_beneficiaries.length == _vestingPeriods.length, "input length mismatch");

        uint256 _totalObligations;
        for (uint256 i = 0; i < _beneficiaries.length; i++) {
            address beneficiary = _beneficiaries[i];

            require(!beneficiaryAdded[beneficiary], "beneficiary already added");
            beneficiaryAdded[beneficiary] = true;

            uint256 amountToClaim;

            VestingPeriod memory period = _vestingPeriods[i];
            amountToClaim = amountToClaim.add(
                uint256(period.vestingDays).mul(
                    uint256(period.tokensPerDay)
                )
            );
            vestingPeriods[beneficiary] = period;
            beneficiaries.push(beneficiary);
            _totalObligations = _totalObligations.add(amountToClaim);

            emit BeneficiaryAdded(beneficiary, amountToClaim);
        }

        totalObligations = totalObligations.add(_totalObligations);
        token.safeTransferFrom(msg.sender, address(this), _totalObligations);
    }
}
function claimFor(address _beneficiary) external claimAllowed {
    _processClaim(_beneficiary);
}

// claims vested tokens for a given beneficiary