contract Vault is Ownable {
    address private dispatcherAddress;
    address private multiSigAddress;
    address[] private tokens;

    struct supportedToken {
        ERC20 token;
        bool active;
    }

    mapping(address => supportedToken) private tokensStore;

    constructor(address _multiSigAddress) Ownable() {
        require(_multiSigAddress != address(0), "Cannot set address to 0");
        multiSigAddress = _multiSigAddress;
    }

    /*****************  Getters **************** */

    function getDispatcherAddress() public view returns (address) {
        return dispatcherAddress;
    }

    function getMultiSigAddress() public view returns (address) {
        return multiSigAddress;
    }

    function getTokenAddresses() public view returns (address[] memory) {
        return tokens;
    }

    /***************** Calls **************** */
    function transferFunds(address _tokenAddress, address _recipient, uint256 _amount) external onlyDispatcher {
        require(tokensStore[_tokenAddress].active == true, "Token not supported");
        require(_amount > 0, "Cannot transfer 0 tokens");
        ERC20(_tokenAddress).transfer(_recipient, _amount);
        emit ReleasedFundsEvent(_recipient, _amount);
    }

    function newMultiSig(address _multiSigAddress) external onlyMultiSig {
        require(_multiSigAddress != address(0), "Cannot set address to 0");
        multiSigAddress = _multiSigAddress;
        emit NewMultiSigEvent(_multiSigAddress);
    }

    function newDispatcher(address _dispatcherAddress) external onlyMultiSig {
        require(_dispatcherAddress != address(0), "Can't set address to 0");
        dispatcherAddress = _dispatcherAddress;
        emit NewDispatcherEvent(dispatcherAddress);
    }

    function addToken(address _tokenAddress) external onlyMultiSig {
        require(tokensStore[_tokenAddress].active != true, "Token already supported");
        tokensStore[_tokenAddress].token = ERC20(_tokenAddress);
        tokensStore[_tokenAddress].active = true;
        tokens.push(_tokenAddress);
        emit AddTokenEvent(_tokenAddress);
    }

    function removeToken(address _tokenAddress) external onlyMultiSig {
        require(tokensStore[_tokenAddress].active == true, "Token not supported already");
        tokensStore[_tokenAddress].active = false;
        popTokenArray(_tokenAddress);
        emit RemoveTokenEvent(_tokenAddress);
    }

    /*****************  Internal **************** */

    function popTokenArray(address _tokenAddress) private {
        for(uint256 i = 0; i <= tokens.length; i++)
        {
            if(_tokenAddress == tokens[i])
            {
                tokens[i] = tokens[tokens.length - 1];
                tokens.pop();
                break;
            }
        }
    }

    /*****************  Modifiers **************** */

    modifier onlyDispatcher() {
        require(dispatcherAddress == msg.sender, "Not the disptacher");
        _;
    }

    modifier onlyMultiSig() {
        require(multiSigAddress == msg.sender, "Not the multisig");
        _;
    }

    /*****************  Events **************** */
    event NewMultiSigEvent(address newMultiSigAddress);
    event AddTokenEvent(address newTokenAddress);
    event RemoveTokenEvent(address removedTokenAddress);
    event NewDispatcherEvent(address newdDspatcherAddress);
    event ReleasedFundsEvent(address indexed recipient, uint256 amount);
}
function transferFunds(address _tokenAddress, address _recipient, uint256 _amount) external onlyDispatcher {
    require(tokensStore[_tokenAddress].active == true, "Token not supported");
    require(_amount > 0, "Cannot transfer 0 tokens");
    ERC20(_tokenAddress).transfer(_recipient, _amount);
    emit ReleasedFundsEvent(_recipient, _amount);
}

/***************** Calls **************** */