contract DZCustomZap1_Calavera is Initializable, ReentrancyGuard {
    using SafeMath for uint256;
    using Roles for Roles.Role;

    // state variables
    bool private stopped;
    address payable public owner;
    address public RevShareAddress;
    address public UniPool_Zap_Address;
    address public dzConvert2TokenAddress;
    uint16 public goodwill;
    mapping(address => uint256) public UserBalance;
    address payable[4] public defaultTokenAddress;
    uint16[4] public defaultAllocation;
    uint256 public ETHBalance;

    // events
    event LPTokensIssued(uint256);

    // circuit breaker modifiers
    modifier stopInEmergency {
        if (stopped) {
            revert("Temporarily Paused");
        } else {
            _;
        }
    }

    modifier onlyOwner() {
        require(isOwner(), "you are not authorised to call this function");
        _;
    }

    function initialize(
        address _RevShareAddress,
        address _UniPool_Zap_Address,
        address _dzConvert2TokenAddress,
        uint16 _goodwill
    ) public initializer {
        ReentrancyGuard.initialize();
        stopped = false;
        owner = msg.sender;
        RevShareAddress = _RevShareAddress;
        UniPool_Zap_Address = _UniPool_Zap_Address;
        dzConvert2TokenAddress = _dzConvert2TokenAddress;
        goodwill = _goodwill;
        defaultTokenAddress = [
            0x06AF07097C9Eeb7fD685c692751D5C66dB49c215, // ChaiToken
            0xC011a73ee8576Fb46F5E1c5751cA3B9Fe0af2a6F, // SNX
            0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2, // MKR
            0x514910771AF9Ca656af840dff83E8264EcF986CA
        ]; // ChainLink
        defaultAllocation = [2500, 2000, 1500, 1500];
        ETHBalance = 0;
    }

    function set_new_goodwill(uint16 _new_goodwill) public onlyOwner {
        require(
            _new_goodwill >= 0 && _new_goodwill <= 10000,
            "wrong goodwill number"
        );
        goodwill = _new_goodwill;
    }

    function set_new_UniPool_Zap_Address(address _new_UniPool_Zap_Address)
        public
        onlyOwner
    {
        UniPool_Zap_Address = _new_UniPool_Zap_Address;
    }

    function LetsInvest(
        address payable _toWhomToIssue,
        address ERC20_for_UniPoolZap_address,
        uint16 _sETHZapAllocation,
        address payable[4] memory _tokenAddresses,
        uint16[4] memory _allocations
    ) public payable returns (bool) {
        uint16 _num1 = 0;
        for (uint256 i = 0; i < 4; ++i) {
            _num1 = _num1 + _allocations[i];
        }
        require(
            SafeMath.add(_num1, _sETHZapAllocation) == 10000,
            "Error in Allocations"
        );
        uint256 goodwillAmt = SafeMath.div(
            SafeMath.mul(msg.value, goodwill),
            10000
        );
        require(
            (
                (
                    RevShare1_Calavera(RevShareAddress).allocation.value(
                        goodwillAmt
                    )()
                )
            ),
            "cannot allocate goodwill"
        );
        uint256 invstAmt = SafeMath.sub(msg.value, goodwillAmt);
        uint256 balInvstAmt = SafeMath.sub(
            invstAmt,
            SafeMath.div(SafeMath.mul(invstAmt, _sETHZapAllocation), 10000)
        );
        for (uint256 i = 0; i < 4; ++i) {
            uint256 investPortionFromBal = SafeMath.div(
                SafeMath.mul(balInvstAmt, _allocations[i]),
                10000
            );
            idzConvert2token(dzConvert2TokenAddress).convertToToken.value(
                investPortionFromBal
            )(_toWhomToIssue, 2, _tokenAddresses[i]);
        }
        UniPool_Zap(UniPool_Zap_Address).LetsInvest.value(
            SafeMath.div(SafeMath.mul(invstAmt, _sETHZapAllocation), 10000)
        )(ERC20_for_UniPoolZap_address, _toWhomToIssue);
        return (true);
    }

    function inCaseTokengetsStuck(IERC20 _TokenAddress) public onlyOwner {
        uint256 qty = _TokenAddress.balanceOf(address(this));
        _TokenAddress.transfer(owner, qty);
    }

    // - fallback function let you / anyone send ETH to this wallet without the need to call any function
    function() external payable {
        LetsInvest(
            msg.sender,
            0x5e74C9036fb86BD7eCdcb084a0673EFc32eA31cb,
            2500,
            defaultTokenAddress,
            defaultAllocation
        );
    }

    function defaultValues(
        address payable _TokenAddress1,
        address payable _TokenAddress2,
        address payable _TokenAddress3,
        address payable _TokenAddress4,
        uint16 _allocation_1_InBPS,
        uint16 _allocation_2_InBPS,
        uint16 _allocation_3_InBPS,
        uint16 _allocation_4_InBPS
    ) public onlyOwner returns (bool) {
        defaultTokenAddress[0] = _TokenAddress1;
        defaultTokenAddress[1] = _TokenAddress2;
        defaultTokenAddress[2] = _TokenAddress3;
        defaultTokenAddress[3] = _TokenAddress4;
        defaultAllocation[0] = _allocation_1_InBPS;
        defaultAllocation[1] = _allocation_2_InBPS;
        defaultAllocation[2] = _allocation_3_InBPS;
        defaultAllocation[3] = _allocation_4_InBPS;
        return (true);
    }

    // - to Pause the contract
    function toggleContractActive() public onlyOwner {
        stopped = !stopped;
    }

    function depositETH() public payable onlyOwner {
        ETHBalance = ETHBalance + msg.value;
    }

    // - to withdraw any ETH balance sitting in the contract
    function withdraw() public onlyOwner {
        address payable OwnerAddress = owner;
        uint256 balance = address(this).balance;
        OwnerAddress.transfer(balance);
    }

    // - to kill the contract
    function destruct() public onlyOwner {
        selfdestruct(owner);
    }

    /**
     * @return true if `msg.sender` is the owner of the contract.
     */
    function isOwner() public view returns (bool) {
        return msg.sender == owner;
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address payable newOwner) public onlyOwner {
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     */
    function _transferOwnership(address payable newOwner) internal {
        require(
            newOwner != address(0),
            "Ownable: new owner is the zero address"
        );
        owner = newOwner;
    }

}
function _transferOwnership(address payable newOwner) internal {
    require(
        newOwner != address(0),
        "Ownable: new owner is the zero address"
    );
    owner = newOwner;
}

/**
 * @dev Transfers ownership of the contract to a new account (`newOwner`).
 */