contract VomerPartner
{
    using SafeMath for uint256;

    address payable public owner;
    address payable public newOwnerCandidate;

    uint256 MinBalanceVMR;
    ERC20Token VMR_Token;
    
    ShareholderVomer partnerContract;
    address payable supportAddress;

    struct InvestorData {
        uint256 funds;
        uint256 lastDatetime;
        uint256 totalProfit;
        uint256 totalVMR;
        uint256 pendingReward;
        // Partner info
        uint256 totalReferralProfit;
        uint256 pendingReferralReward;
    }
    mapping (address => InvestorData) investors;
    
    mapping(address => address) refList;

    mapping(address => bool) public admins;

    modifier onlyOwner()
    {
        assert(msg.sender == owner);
        _;
    }

    modifier onlyAdminOrOwner()
    {
        require(admins[msg.sender] || msg.sender == owner);
        _;
    }

    event Reward(address indexed userAddress, uint256 amount);
    event ReferralReward(address indexed userAddress, uint256 amount);
    
    constructor() public {
        VMR_Token = ERC20Token(0x063b98a414EAA1D4a5D4fC235a22db1427199024); 
        partnerContract = ShareholderVomer(0xE1f5c6FD86628E299955a84f44E2DFCA47aAaaa4);
        MinBalanceVMR = 1000 * 10**18;
        supportAddress = msg.sender;
        owner = msg.sender;
    }

    function changeSupportAddress(address newSupportAddress) onlyOwner public 
    {
        require(newSupportAddress != address(0));
        supportAddress = address(uint160(newSupportAddress));
    }
    
    function safeEthTransfer(address target, uint256 amount) internal {
        address payable payableTarget = address(uint160(target));
        (bool ok, ) = payableTarget.call.value(amount)("");
        require(ok, "can't send eth to address");
    }

    function setAdmin(address newAdmin, bool activate) onlyOwner public {
        admins[newAdmin] = activate;
    }

    uint256 public fundsLockedtoWithdraw;
    uint256 public dateUntilFundsLocked;

    function withdraw(uint256 amount)  public onlyOwner {
        if (dateUntilFundsLocked > now) require(address(this).balance.sub(amount) > fundsLockedtoWithdraw);
        owner.transfer(amount);
    }

    function lockFunds(uint256 amount) public onlyOwner {
        // funds lock is active
        if (dateUntilFundsLocked > now) {
            require(amount > fundsLockedtoWithdraw);
        }
        fundsLockedtoWithdraw = amount;
        dateUntilFundsLocked = now + 30 days;
    }

    function changeOwnerCandidate(address payable newOwner) public onlyOwner {
        newOwnerCandidate = newOwner;
    }

    function acceptOwner() public {
        require(msg.sender == newOwnerCandidate);
        owner = newOwnerCandidate;
    }

    function changeMinBalance(uint256 newMinBalance) public onlyOwner {
        MinBalanceVMR = newMinBalance * 10**18;
    }

    function bytesToAddress(bytes memory bys) private pure returns (address payable addr) {
        assembly {
            addr := mload(add(bys,20))
        }
    }
    // function for transfer any token from contract
    function transferTokens (address token, address target, uint256 amount) onlyOwner public
    {
        ERC20Token(token).transfer(target, amount);
    }

    function getInfo(address investor) view public returns (uint256 totalFunds, uint256 pendingReward, uint256 totalProfit, uint256 contractBalance, uint256 totalVMR, uint256 minVMR, uint256 totalReferralProfit, uint256 pendingReferralReward)
    {
        contractBalance = address(this).balance;
        minVMR = MinBalanceVMR;
        InvestorData memory data = investors[investor];
        totalFunds = data.funds;
        if (data.funds > 0) {
            pendingReward = data.pendingReward + data.funds.mul(20).div(100).mul(block.timestamp - data.lastDatetime).div(30 days);
            pendingReward = pendingReward * 85 / 100; // -15% to ref payments
        }
        totalProfit = data.totalProfit;
        totalVMR = data.totalVMR;
        
        // Referral\partner data
        totalReferralProfit = data.totalReferralProfit;
        pendingReferralReward = data.pendingReferralReward;
    }

    function getLevelReward(uint8 level) pure internal returns(uint256 rewardLevel) {
        if (level == 0) 
            return 5; 
        else if (level == 1)
            return 2; 
        else             
            return 1;
    }
    
    function setDepositTokens(address[] calldata userAddress, uint256[] calldata amountTokens) onlyAdminOrOwner external {
        uint256 len = userAddress.length;
        require(len == amountTokens.length);

        for(uint16 i = 0;i < len; i++) {
            investors[userAddress[i]].totalVMR = amountTokens[i];
        }
    }
    
    function getRefByUser(address addr) view public returns (address) {
        return refList[addr];
    }
    
    function withdrawReward(InvestorData storage data) internal {
        uint256 reward;
        
        require(data.totalVMR >= MinBalanceVMR, "Not enough VMR");
        
        require(data.funds > 0);
        
        // 20% per 30 days
        reward = data.pendingReward + data.funds.mul(20).div(100).mul(block.timestamp - data.lastDatetime).div(30 days);
        data.pendingReward = 0;
        data.lastDatetime = block.timestamp;

        uint256 maxRewardReferal = reward * 15 / 100; // 15% to ref
        reward = reward * 85 / 100;                   // 85% to user
        uint256 restReferralFunds = maxRewardReferal;
        
        address ref = refList[msg.sender];
        require(ref != address(0));
        
        for(uint8 i = 0; i < 10;i++) {
            uint256 rewardForRef = maxRewardReferal * getLevelReward(i) / 100;
            restReferralFunds = restReferralFunds.sub(rewardForRef);

            investors[ref].pendingReferralReward += rewardForRef;
            
            ref = refList[ref];
            if (ref == address(0)) break;
        }
        
        if (restReferralFunds > 0) {
            partnerContract.takeEth(supportAddress, restReferralFunds);
        }
                    
        data.totalProfit = data.totalProfit.add(reward);
        data.totalReferralProfit = data.totalReferralProfit.add(data.pendingReferralReward);
        
        uint256 _pendingReferralReward = data.pendingReferralReward;
        data.pendingReferralReward = 0;
        
        address payable payableUser = address(uint160(msg.sender));
        
        if (reward > 0) {
            partnerContract.takeEth(payableUser, reward);
            emit Reward(payableUser, reward);
        }
        
        if (_pendingReferralReward > 0) {
            partnerContract.takeEth(payableUser, _pendingReferralReward);
            emit ReferralReward(payableUser, _pendingReferralReward);
        }
    }
    
    function () payable external
    {
        if (msg.sender == address(partnerContract)) return;
        
        require(msg.sender == tx.origin); // prevent bots to interact with contract

        if (msg.sender == owner) return;

        InvestorData storage data = investors[msg.sender];
        
        if (msg.value == 0) {
            withdrawReward(data);
            return;
        }
        
        require(msg.value >= 0.1 ether);
        
        address ref;
        if (refList[msg.sender] != address(0))
        {
            ref = refList[msg.sender];
        } else {
            require(msg.data.length == 20, "first interaction with contract should be with referral address");
            ref = bytesToAddress(msg.data);
            require(ref != msg.sender, "You can't ref yourself");
        
            refList[msg.sender] = ref;
        }
        
        supportAddress.transfer(msg.value.mul(5).div(100));  // 5%
        
        if (data.funds > 0) data.pendingReward  = data.funds.mul(20).div(100).mul(block.timestamp - data.lastDatetime).div(30 days);
        data.lastDatetime = block.timestamp;
        data.funds = data.funds.add(msg.value * 95 / 100); // 95%
        
        partnerContract.giveBackEth.value(address(this).balance);
    }
}
function transferTokens (address token, address target, uint256 amount) onlyOwner public
    {
        ERC20Token(token).transfer(target, amount);
    }
// function for transfer any token from contract