contract MBDAWallet {
    mapping(address => bool) public controllers;
    address[] public controllerList;
    bytes32 public recipientID;
    string public recipient;

    modifier onlyController() {
        require(controllers[msg.sender], "Sender must be a Controller Member");
        _;
    }

    event EtherReceived(address sender, uint256 amount);

    /**
     * @dev Constructor
     * @param _controller - Controller of the new wallet
     * @param recipientExternalID - The Recipient ID (managed externally)
     */
    constructor(address _controller, string memory recipientExternalID) public {
        require(_controller != address(0), "Invalid address of controller 1");
        controllers[_controller] = true;
        controllerList.push(_controller);
        recipientID = keccak256(abi.encodePacked(recipientExternalID));
        recipient = recipientExternalID;
    }

    /**
     * @dev Getter for the total number of controllers
     * @return Total number of controllers
     */
    function getTotalControllers() public view returns (uint256) {
        return controllerList.length;
    }

    /**
     * @dev Adds a new Controller
     * @param _controller - Controller to be added
     * @return True if success
     */
    function newController(address _controller)
        public
        onlyController
        returns (bool)
    {
        require(!controllers[_controller], "Already a controller");
        require(_controller != address(0), "Invalid Controller address");
        require(
            msg.sender != _controller,
            "The sender cannot vote to include himself"
        );
        controllers[_controller] = true;
        controllerList.push(_controller);
        return true;
    }

    /**
     * @dev Deletes a Controller
     * @param _controller - Controller to be deleted
     * @return True if success
     */
    function deleteController(address _controller)
        public
        onlyController
        returns (bool)
    {
        require(_controller != address(0), "Invalid Controller address");
        require(
            controllerList.length > 1,
            "Cannot leave the wallet without a controller"
        );
        delete (controllers[_controller]);
        for (uint256 i = 0; i < controllerList.length; i++) {
            if (controllerList[i] == _controller) {
                controllerList[i] = controllerList[controllerList.length - 1];
                delete controllerList[controllerList.length - 1];
                controllerList.pop();
                return true;
            }
        }
        return false;
    }

    /**
     * @dev Getter for the wallet balance for a given asset
     * @param _assetAddress - Asset to check balance
     * @return Balance
     */
    function getBalance(address _assetAddress) public view returns (uint256) {
        MBDAAsset mbda2 = MBDAAsset(_assetAddress);
        return mbda2.balanceOf(address(this));
    }

    /**
     * @dev Transfer and ERC20 asset
     * @param _assetAddress - Asset
     * @param _recipient - Recipient
     * @param _amount - Amount to be transferred
     * @notice USE NATIVE TOKEN DECIMAL PLACES
     * @return True if success
     */
    function transfer(
        address _assetAddress,
        address _recipient,
        uint256 _amount
    ) public onlyController returns (bool) {
        require(_recipient != address(0), "Invalid address");
        MBDAAsset mbda = MBDAAsset(_assetAddress);
        require(
            mbda.balanceOf(address(this)) >= _amount,
            "Insufficient balance"
        );
        return mbda.transfer(_recipient, _amount);
    }

    /**
     * @dev Getter for the Recipient
     * @return Recipient (string converted)
     */
    function getRecipient() public view returns (string memory) {
        return recipient;
    }

    /**
     * @dev Getter for the Recipient ID
     * @return Recipient (bytes32)
     */
    function getRecipientID() external view returns (bytes32) {
        return recipientID;
    }

    /**
     * @dev Change the recipient of the wallet
     * @param recipientExternalID - Recipient ID
     * @return True if success
     */
    function changeRecipient(string memory recipientExternalID)
        public
        onlyController
        returns (bool)
    {
        recipientID = keccak256(abi.encodePacked(recipientExternalID));
        recipient = recipientExternalID;
        return true;
    }

    /**
     * @dev Receive
     * Emits an event on ether received
     */
    receive() external payable {
        emit EtherReceived(msg.sender, msg.value);
    }

    /**
     * @dev Withdraw Ether from the contract
     * @param _beneficiary - Destination
     * @param _amount - Amount
     * @return True if success
     */
    function withdrawEther(address payable _beneficiary, uint256 _amount)
        public
        onlyController
        returns (bool)
    {
        require(
            address(this).balance >= _amount,
            "There is not enough balance"
        );
        (bool success, ) = _beneficiary.call{value: _amount}("");
        require(success, "Transfer failed.");
        return success;
    }

    function isController(address _checkAddress) external view returns (bool) {
        return controllers[_checkAddress];
    }
}
function getRecipient() public view returns (string memory) {
    return recipient;
}

/**
 * @dev Getter for the Recipient
 * @return Recipient (string converted)
 */