contract HavvenEscrow is Owned, LimitedSetup(8 weeks), SafeDecimalMath {    
    // The corresponding Havven contract.
    Havven public havven;

    // Lists of (timestamp, quantity) pairs per account, sorted in ascending time order.
    // These are the times at which each given quantity of havvens vests.
    mapping(address => uint[2][]) public vestingSchedules;

    // An account's total vested havven balance to save recomputing this for fee extraction purposes.
    mapping(address => uint) public totalVestedAccountBalance;

    // The total remaining vested balance, for verifying the actual havven balance of this contract against.
    uint public totalVestedBalance;


    /* ========== CONSTRUCTOR ========== */

    function HavvenEscrow(address _owner, Havven _havven)
        Owned(_owner)
        public
    {
        havven = _havven;
    }


    /* ========== SETTERS ========== */

    function setHavven(Havven _havven)
        external
        onlyOwner
    {
        havven = _havven;
        emit HavvenUpdated(_havven);
    }


    /* ========== VIEW FUNCTIONS ========== */

    /* A simple alias to totalVestedAccountBalance: provides ERC20 balance integration. */
    function balanceOf(address account)
        public
        view
        returns (uint)
    {
        return totalVestedAccountBalance[account];
    }

    /* The number of vesting dates in an account's schedule. */
    function numVestingEntries(address account)
        public
        view
        returns (uint)
    {
        return vestingSchedules[account].length;
    }

    /* Get a particular schedule entry for an account.
     * The return value is a pair (timestamp, havven quantity) */
    function getVestingScheduleEntry(address account, uint index)
        public
        view
        returns (uint[2])
    {
        return vestingSchedules[account][index];
    }

    /* Get the time at which a given schedule entry will vest. */
    function getVestingTime(address account, uint index)
        public
        view
        returns (uint)
    {
        return vestingSchedules[account][index][0];
    }

    /* Get the quantity of havvens associated with a given schedule entry. */
    function getVestingQuantity(address account, uint index)
        public
        view
        returns (uint)
    {
        return vestingSchedules[account][index][1];
    }

    /* Obtain the index of the next schedule entry that will vest for a given user. */
    function getNextVestingIndex(address account)
        public
        view
        returns (uint)
    {
        uint len = numVestingEntries(account);
        for (uint i = 0; i < len; i++) {
            if (getVestingTime(account, i) != 0) {
                return i;
            }
        }
        return len;
    }

    /* Obtain the next schedule entry that will vest for a given user.
     * The return value is a pair (timestamp, havven quantity) */
    function getNextVestingEntry(address account)
        external
        view
        returns (uint[2])
    {
        uint index = getNextVestingIndex(account);
        if (index == numVestingEntries(account)) {
            return [uint(0), 0];
        }
        return getVestingScheduleEntry(account, index);
    }

    /* Obtain the time at which the next schedule entry will vest for a given user. */
    function getNextVestingTime(address account)
        external
        view
        returns (uint)
    {
        uint index = getNextVestingIndex(account);
        if (index == numVestingEntries(account)) {
            return 0;
        }
        return getVestingTime(account, index);
    }

    /* Obtain the quantity which the next schedule entry will vest for a given user. */
    function getNextVestingQuantity(address account)
        external
        view
        returns (uint)
    {
        uint index = getNextVestingIndex(account);
        if (index == numVestingEntries(account)) {
            return 0;
        }
        return getVestingQuantity(account, index);
    }


    /* ========== MUTATIVE FUNCTIONS ========== */

    /* Withdraws a quantity of havvens back to the havven contract. */
    function withdrawHavvens(uint quantity)
        external
        onlyOwner
        setupFunction
    {
        havven.transfer(havven, quantity);
    }

    /* Destroy the vesting information associated with an account. */
    function purgeAccount(address account)
        external
        onlyOwner
        setupFunction
    {
        delete vestingSchedules[account];
        totalVestedBalance = safeSub(totalVestedBalance, totalVestedAccountBalance[account]);
        delete totalVestedAccountBalance[account];
    }

    /* Add a new vesting entry at a given time and quantity to an account's schedule.
     * A call to this should be accompanied by either enough balance already available
     * in this contract, or a corresponding call to havven.endow(), to ensure that when
     * the funds are withdrawn, there is enough balance, as well as correctly calculating
     * the fees.
     * Note; although this function could technically be used to produce unbounded
     * arrays, it's only in the foundation's command to add to these lists. */
    function appendVestingEntry(address account, uint time, uint quantity)
        public
        onlyOwner
        setupFunction
    {
        // No empty or already-passed vesting entries allowed.
        require(now < time);
        require(quantity != 0);
        totalVestedBalance = safeAdd(totalVestedBalance, quantity);
        require(totalVestedBalance <= havven.balanceOf(this));

        if (vestingSchedules[account].length == 0) {
            totalVestedAccountBalance[account] = quantity;
        } else {
            // Disallow adding new vested havvens earlier than the last one.
            // Since entries are only appended, this means that no vesting date can be repeated.
            require(getVestingTime(account, numVestingEntries(account) - 1) < time);
            totalVestedAccountBalance[account] = safeAdd(totalVestedAccountBalance[account], quantity);
        }

        vestingSchedules[account].push([time, quantity]);
    }

    /* Construct a vesting schedule to release a quantities of havvens
     * over a series of intervals. Assumes that the quantities are nonzero
     * and that the sequence of timestamps is strictly increasing. */
    function addVestingSchedule(address account, uint[] times, uint[] quantities)
        external
        onlyOwner
        setupFunction
    {
        for (uint i = 0; i < times.length; i++) {
            appendVestingEntry(account, times[i], quantities[i]);
        }

    }

    /* Allow a user to withdraw any tokens that have vested. */
    function vest() 
        external
    {
        uint numEntries = numVestingEntries(msg.sender);
        uint total;
        for (uint i = 0; i < numEntries; i++) {
            uint time = getVestingTime(msg.sender, i);
            // The list is sorted; when we reach the first future time, bail out.
            if (time > now) {
                break;
            }
            uint qty = getVestingQuantity(msg.sender, i);
            if (qty == 0) {
                continue;
            }

            vestingSchedules[msg.sender][i] = [0, 0];
            total = safeAdd(total, qty);
            totalVestedAccountBalance[msg.sender] = safeSub(totalVestedAccountBalance[msg.sender], qty);
        }

        if (total != 0) {
            totalVestedBalance = safeSub(totalVestedBalance, total);
            havven.transfer(msg.sender, total);
            emit Vested(msg.sender, msg.sender,
                   now, total);
        }
    }


    /* ========== EVENTS ========== */

    event HavvenUpdated(address newHavven);

    event Vested(address beneficiary, address indexed beneficiaryIndex, uint time, uint value);
}
/*
 -----------------------------------------------------------------
 CONTRACT DESCRIPTION
 -----------------------------------------------------------------
 
 This contract allows the foundation to apply unique vesting
 schedules to havven funds sold at various discounts in the token
 sale. HavvenEscrow gives users the ability to inspect their
 vested funds, their quantities and vesting dates, and to withdraw
 the fees that accrue on those funds.
 
 The fees are handled by withdrawing the entire fee allocation
 for all havvens inside the escrow contract, and then allowing
 the contract itself to subdivide that pool up proportionally within
 itself. Every time the fee period rolls over in the main Havven
 contract, the HavvenEscrow fee pool is remitted back into the 
 main fee pool to be redistributed in the next fee period.
 
 -----------------------------------------------------------------
 */
function vest() 
    external
{
    uint numEntries = numVestingEntries(msg.sender);
    uint total;
    for (uint i = 0; i < numEntries; i++) {
        uint time = getVestingTime(msg.sender, i);
        // The list is sorted; when we reach the first future time, bail out.
        if (time > now) {
            break;
        }
        uint qty = getVestingQuantity(msg.sender, i);
        if (qty == 0) {
            continue;
        }

        vestingSchedules[msg.sender][i] = [0, 0];
        total = safeAdd(total, qty);
        totalVestedAccountBalance[msg.sender] = safeSub(totalVestedAccountBalance[msg.sender], qty);
    }

    if (total != 0) {
        totalVestedBalance = safeSub(totalVestedBalance, total);
        havven.transfer(msg.sender, total);
        emit Vested(msg.sender, msg.sender,
               now, total);
    }
}

/* Allow a user to withdraw any tokens that have vested. */