contract TokenState is Permissions, ILocker {

    /**
     * @dev Emitted when a contract is added to the locker.
     */
    event LockerWasAdded(
        string locker
    );

    /**
     * @dev Emitted when a contract is removed from the locker.
     */
    event LockerWasRemoved(
        string locker
    );

    string[] private _lockers;

    /**
     *  @dev Return and update the total locked amount of a given `holder`.
     *
     *  @param holder address of the token holder
     *  @return total locked amount
    */
    function getAndUpdateLockedAmount(address holder) external override returns (uint) {
        uint locked = 0;
        for (uint i = 0; i < _lockers.length; ++i) {
            ILocker locker = ILocker(contractManager.getContract(_lockers[i]));
            locked = locked.add(locker.getAndUpdateLockedAmount(holder));
        }
        return locked;
    }

    /**
     * @dev Return and update the total locked and un-delegatable amount of a given `holder`.
     *
     * @param holder address of the token holder
     * @return amount total slashed amount (non-transferable and non-delegatable)
    */
    function getAndUpdateForbiddenForDelegationAmount(address holder) external override returns (uint amount) {
        uint forbidden = 0;
        for (uint i = 0; i < _lockers.length; ++i) {
            ILocker locker = ILocker(contractManager.getContract(_lockers[i]));
            forbidden = forbidden.add(locker.getAndUpdateForbiddenForDelegationAmount(holder));
        }
        return forbidden;
    }

    /**
     * @dev Allows the Owner to remove a contract from the locker.
     *
     * Emits a LockerWasRemoved event.
     *
     * @param locker string name of contract to remove from locker
     */
    function removeLocker(string calldata locker) external onlyOwner {
        uint index;
        bytes32 hash = keccak256(abi.encodePacked(locker));
        for (index = 0; index < _lockers.length; ++index) {
            if (keccak256(abi.encodePacked(_lockers[index])) == hash) {
                break;
            }
        }
        if (index < _lockers.length) {
            if (index < _lockers.length.sub(1)) {
                _lockers[index] = _lockers[_lockers.length.sub(1)];
            }
            delete _lockers[_lockers.length.sub(1)];
            _lockers.pop();
            emit LockerWasRemoved(locker);
        }
    }

    function initialize(address contractManagerAddress) public override initializer {
        Permissions.initialize(contractManagerAddress);
        addLocker("DelegationController");
        addLocker("Punisher");
        addLocker("TokenLaunchLocker");
    }

    /**
     * @dev Allows the Owner to add a contract to the Locker.
     *
     * Emits a LockerWasAdded event.
     *
     * @param locker string name of contract to add to locker
     */
    function addLocker(string memory locker) public onlyOwner {
        _lockers.push(locker);
        emit LockerWasAdded(locker);
    }
}
/**
 * @title Token State
 * @dev This contract manages lockers to control token transferability.
 *
 * See ILocker.
 */
function getAndUpdateLockedAmount(address holder) external override returns (uint) {
    uint locked = 0;
    for (uint i = 0; i < _lockers.length; ++i) {
        ILocker locker = ILocker(contractManager.getContract(_lockers[i]));
        locked = locked.add(locker.getAndUpdateLockedAmount(holder));
    }
    return locked;
}

/**
 *  @dev Return and update the total locked amount of a given `holder`.
 *
 *  @param holder address of the token holder
 *  @return total locked amount
 */