contract MultipleTokenVesting is Pausable, RBAC {
    using SafeMath for uint256;

    event Released(address indexed beneficiary, uint256 indexed amount);
    event Vested(address indexed beneficiary, uint256 indexed amount);
    event Activated();
    event VestingFinished();
    event Airdrop(address indexed beneficiary);

    // beneficiaries of tokens with respective amounts of tokens to be released
    mapping (address => uint256) public beneficiaries;
    // beneficiaries of tokens with respective amounts of tokens already released
    mapping (address => uint256) public released;

    ZerochainToken public token;

    uint256 public cliff;
    uint256 public start;
    uint256 public duration;
    bool public isActive = false;
    bool public canIssueIndividual = true;

    uint256 public constant AIRDROP_AMOUNT = 10 * (10 ** 10);
    string public constant UTILITY_ROLE = "utility";
    address public utilityAccount;
    uint256 public hardCap;

    /**
     * @dev Creates a vesting contract and a token.
     * @param _start timestamp for the moment when vesting should be considered as started
     * @param _cliff duration in seconds of the cliff in which tokens will begin to vest
     * @param _duration duration in seconds of the period in which the tokens will vest
     */
    function MultipleTokenVesting(
        uint256 _start,
        uint256 _cliff,
        uint256 _duration,
        address _utilityAccount,
        uint256 _hardCap
    )
    public
    {
        require(_cliff <= _duration);
        require(_utilityAccount != address(0));
        require(_hardCap > 0);

        token = new ZerochainToken();
        duration = _duration;
        cliff = _start.add(_cliff);
        start = _start;
        utilityAccount = _utilityAccount;
        addRole(_utilityAccount, UTILITY_ROLE);
        hardCap = _hardCap;
    }

    function changeUtilityAccount (
        address newAddress
    ) public onlyOwner {
        require(newAddress != address(0));
        removeRole(utilityAccount, UTILITY_ROLE);
        utilityAccount = newAddress;
        addRole(utilityAccount, UTILITY_ROLE);
    }

    function activate() public onlyOwner {
        require(!isActive);
        isActive = true;
        Activated();
    }

    function finalise() public onlyOwner {
        token.finishMinting();
        VestingFinished();
    }

    function stopIssuingIndividualTokens() public onlyOwner {
        require(canIssueIndividual);
        canIssueIndividual = false;
    }

    function issueIndividualTokens(
        address beneficiary,
        uint256 amount
    ) public onlyOwner {
        require(beneficiary != address(0));
        require(amount != 0);
        require(canIssueIndividual);
        require(token.totalSupply().add(amount) <= hardCap);

        token.mint(beneficiary, amount);
    }

    /**
     * @dev Main means of adding information about beneficiaries who should have tokens vested for them.
     * We add users in array to save gas for such operation.
     * @param _beneficiaries user addresses array whose tokens need to be vested.
     * @param _amounts amount of tokens for vesting array which is related to the user with same idx.
     **/
    function addVestingForBeneficiaries(
        address[] _beneficiaries,
        uint256[] _amounts
    ) public onlyRole(UTILITY_ROLE) whenNotPaused {
        require(_beneficiaries.length == _amounts.length);
        for (uint i = 0; i < _beneficiaries.length; i++) {
            addVestingForBeneficiary(_beneficiaries[i], _amounts[i]);
        }
    }

    function releaseMultiple(
        address[] _beneficiaries
    ) public onlyRole(UTILITY_ROLE) whenNotPaused {
        require(isActive);
        for (uint i = 0; i < _beneficiaries.length; i++) {
            release(_beneficiaries[i]);
        }
    }

    function airdropMultiple(
        address[] _beneficiaries
    ) public onlyRole(UTILITY_ROLE) whenNotPaused {
        require(isActive);
        require(block.timestamp >= cliff);
        for (uint i = 0; i < _beneficiaries.length; i++) {
            require(_beneficiaries[i] != address(0));
            airdrop(_beneficiaries[i]);
        }
    }

    /**
     * @dev Calculates the amount that has already vested but hasn't been released yet.
     */
    function releasableAmount() public view returns (uint256) {
        return _releasableAmount(msg.sender);
    }

    /**
     * @dev Calculates the amount that has already vested.
     */
    function vestedAmount() public view returns (uint256) {
        return _vestedAmount(msg.sender);
    }

    /**
     * @notice Transfers vested tokens to beneficiary.
     */
    function release(
        address beneficiary
    ) internal {

        uint256 unreleased = _releasableAmount(beneficiary);

        require(unreleased > 0);

        released[beneficiary] = released[beneficiary].add(unreleased);

        token.transfer(beneficiary, unreleased);

        Released(beneficiary, unreleased);
    }

    function _releasableAmount(
        address beneficiary
    ) internal view returns (uint256) {
        return _vestedAmount(beneficiary).sub(released[beneficiary]);
    }

    function addVestingForBeneficiary(
        address _beneficiary,
        uint256 _amount
    ) internal {
        require(_beneficiary != address(0));
        require(_amount > 0);
        require(beneficiaries[_beneficiary] == 0);
        require(token.totalSupply().add(_amount) <= hardCap);

        beneficiaries[_beneficiary] = _amount;
        token.mint(this, _amount);
        Vested(_beneficiary, _amount);
    }

    function airdrop(
        address _beneficiary
    ) internal {
        require(token.totalSupply().add(AIRDROP_AMOUNT) <= hardCap);

        token.mint(_beneficiary, AIRDROP_AMOUNT);
        Airdrop(_beneficiary);
    }

    function _vestedAmount(
        address beneficiary
    ) internal view returns (uint256) {
        uint256 totalBalance = beneficiaries[beneficiary];

        if (block.timestamp < cliff) {
            return 0;
        } else if (block.timestamp >= start.add(duration)) {
            return totalBalance;
        } else {
            return totalBalance.mul(block.timestamp.sub(start)).div(duration);
        }
    }
}
/**
 * @title MultipleTokenVesting
 * @dev A token holder contract that can release its token balance gradually like a
 * typical vesting scheme, for multiple users with a cliff and vesting period.
 */
function releasableAmount() public view returns (uint256) {
    return _releasableAmount(msg.sender);
}

/**
 * @dev Calculates the amount that has already vested but hasn't been released yet.
 */