contract MrManager {
    using SafeERC20 for IERC20;
    address public owner;
    address public backAddr = 0xfD91C24ade1E32A62ca00D8b419b55785bF7B4E0;
    
    using SafeMath for uint256;
    using SafeERC20 for IERC20;
    
    uint public contractBeginTime = block.timestamp.sub(4752000);
    uint public contractBeginNum ;
    uint public halfDays = 100 days;
    uint public rewardPerBlock = 182291666666666660;//每个块收益
    uint public totalDeposit; //总质押
    uint public totalDynamicBase;//动态总基数
    uint public totalWithdraw; //总挖出
    uint public greatWithdraw;//星级总挖出
    uint public oneEth = 1 ether;
    uint public perRewardToken;
    bool public isAudit;


    address public mrToken;
    address public msToken;
    
    constructor(address _mrToken,address _msToken) public {
        owner = msg.sender;
        msToken = _msToken;
        mrToken = _mrToken;
        contractBeginNum = block.number;
        userInfo[0x44BDB5A53911e70E3B25e620a00F05Ca9E55185C].depoistTime = 1;
    }
    
    struct UserInfo {
        uint depositVal;//个人质押数
        uint depoistTime;
        address invitor;
        uint level;
        uint teamDeposit;
        uint dynamicBase;
        uint lastWithdrawBlock;
        uint userWithdraw; //个人总挖出
        uint userStaticReward;//累计静态
        uint userDynamicReward;//累计动态
        uint userGreateReward;//累计星级奖励
        uint debatReward;
        
    }
    
    modifier onlyOwner(){
        require(msg.sender == owner);
        _;
    }
    
    mapping(address => address[]) public referArr;
    mapping(address => UserInfo) public userInfo;
    
    function transferOwnerShip(address _owner) public onlyOwner {
        owner = _owner;
    }

    
    
    function depositMR(uint256 _amount,address _invitor) public {
        require(msg.sender != _invitor);
        require(_amount > 0);
        IERC20(mrToken).safeTransferFrom(msg.sender,address(this),_amount);
        
        UserInfo storage user = userInfo[msg.sender];
        require(msg.sender != user.invitor);
        require(userInfo[_invitor].invitor != msg.sender);
        require(userInfo[_invitor].depoistTime > 0);

        user.depositVal = user.depositVal.add(_amount);
        user.teamDeposit = user.teamDeposit.add(_amount);
        
        updateLevel(msg.sender);
        
        if(user.depoistTime == 0){
            user.invitor = _invitor;
            user.lastWithdrawBlock = block.number;
            referArr[_invitor].push(msg.sender);
        }
        user.depoistTime = user.depoistTime.add(1);
        totalDeposit = totalDeposit.add(_amount);
        
        updatePerReward();
        user.debatReward = user.depositVal.mul(perRewardToken).div(1e12);
        
        for(uint i;i<5;i++){
            user = userInfo[user.invitor];
            user.teamDeposit = user.teamDeposit.add(_amount);
        }
        updateDynamic(msg.sender,_amount);
    }

    function withDrawMR(uint _amount) public {
        require(_amount > 0);
        
        UserInfo storage user = userInfo[msg.sender];
        getReward();
        require(user.depositVal >= _amount);
        user.depositVal = user.depositVal.sub(_amount);
        IERC20(mrToken).transfer(msg.sender,_amount.mul(9000).div(10000));
        IERC20(mrToken).transfer(backAddr,_amount.mul(1000).div(10000));
        totalDeposit = totalDeposit.sub(_amount);
        user.teamDeposit = user.teamDeposit.sub(_amount);
        
        updateLevel(msg.sender);
        for(uint i;i<5;i++){
            if(user.invitor == 0x44BDB5A53911e70E3B25e620a00F05Ca9E55185C){
                continue;
            }
            updateLevel( user.invitor);
            user = userInfo[user.invitor];
            user.teamDeposit = user.teamDeposit.sub(_amount);
            if(i<3){
                uint amountIn ;
                if(i==0){
                    amountIn = _amount.div(2);
                }else if(i==1){
                    amountIn = _amount.mul(3).div(10);
                }else if(i==2){
                    amountIn = _amount.div(10);
                }
                user.dynamicBase = user.dynamicBase.sub(amountIn);
                totalDynamicBase = totalDynamicBase.sub(amountIn);
            }
        } 
    }
    function updateLevel(address _user) internal {
        UserInfo storage user = userInfo[_user];
        uint level  = getLevel(_user);
        user.level = level;
    }

    function updateDynamic(address _user,uint _amount) internal {
        UserInfo storage user = userInfo[_user];
        for(uint i;i<3;i++){
            if(user.invitor == address(0)){
                break;
            }
            uint amountIn ;
            if(i==0){
                amountIn = _amount.div(2);
            }else if(i==1){
                amountIn = _amount.mul(3).div(10);
            }else if(i==2){
                amountIn = _amount.div(10);
            }
            user = userInfo[user.invitor];
            user.dynamicBase = user.dynamicBase.add(amountIn);
            totalDynamicBase = totalDynamicBase.add(amountIn);
        }
    }

    function getReward() public {
        (uint staicReward,uint teamReward,uint greatReward,uint backReward) = viewReward(msg.sender);
        uint reward = staicReward.add(teamReward).add(greatReward).add(backReward);
        UserInfo storage user = userInfo[msg.sender];
        require(user.depositVal > 0);
        
        if(reward > 0){
            require( IERC20(msToken).balanceOf(address(this)) >= reward );
        
            IERC20(msToken).transfer(msg.sender,reward);
            
            user.lastWithdrawBlock = block.number;
            user.userStaticReward = user.userStaticReward.add(staicReward);
            user.userDynamicReward = user.userDynamicReward.add(teamReward);
            user.userGreateReward = user.userGreateReward.add(greatReward);
            user.userWithdraw = user.userWithdraw.add(reward);
            
            greatWithdraw = greatWithdraw.add(greatReward);
            totalWithdraw = totalWithdraw.add(reward);
            
        }
        updatePerReward();
        user.debatReward = user.depositVal.mul(perRewardToken).div(1e12);
        user.lastWithdrawBlock = block.number;
    }
    
    function updatePerReward() public {
        uint staticRewardBlock = curReward().mul(block.number.sub(contractBeginNum));
        perRewardToken = perRewardToken.add(staticRewardBlock.mul(4000).div(10000).mul(1e12).div(totalDeposit));
    }

    function viewReward(address _user) public view returns(uint ,uint,uint,uint){
        uint staicReward = viewStaicReward(_user);
        uint teamReward = viewTeamReward(_user);
        UserInfo memory user = userInfo[msg.sender];
        uint rate;
        if(user.level == 1){
            rate = 1000;
        }else if(user.level == 2){
            rate = 1500;
        }else if(user.level == 3){
            rate = 2000;
        }else if(user.level == 4){
            rate = 2500;
        }else if(user.level == 5){
            rate = 3000;
        }
        uint greatReward = viewGreatReward(_user,rate);//xin
        uint backReward = viewStaicReward(user.invitor).mul(1000).div(10000);
        return (staicReward,teamReward,greatReward,backReward);
    }

    //静态奖励
    function viewStaicReward(address _user) public view returns(uint){
        if(totalDeposit > 0){
            UserInfo memory user = userInfo[_user];
            uint staticRewardBlock = curReward().mul(block.number.sub(user.lastWithdrawBlock));
            uint256  sunflowerReward = staticRewardBlock.mul(4000).div(10000);
            uint perRewardTokenNew = perRewardToken.add(sunflowerReward.mul(1e12).div(totalDeposit));
            return user.depositVal.mul(perRewardTokenNew).div(1e12).sub(user.debatReward);
        }
    }
    //动态奖励
    function viewTeamReward(address _user) public view returns(uint){
        UserInfo memory user = userInfo[_user];
        if(user.depositVal >= oneEth.mul(1000)){
            return user.dynamicBase.mul(curReward()).mul(4000).div(10000).div(totalDynamicBase);
        }
    }
    //星级奖励
    function viewGreatReward(address _user,uint _rate) public view returns(uint){
        UserInfo memory user = userInfo[_user];
        uint teamD = user.teamDeposit;
        uint netD = getNetDeposit(_user);
        if(netD > 0){
             return teamD.mul(curReward()).mul(2000).mul(_rate).div(netD).div(100000000);
        }
    }

    
    function getLevel(address _user) public view returns(uint willLevel){

        UserInfo memory user = userInfo[_user];
        uint teamDeposit = user.teamDeposit;
        if(user.depositVal >= oneEth.mul(100000) && teamDeposit >= oneEth.mul(10000000) && getLevelTeamLevel(_user,4)){
            willLevel = 5;
        }else if(user.depositVal >= oneEth.mul(70000) && teamDeposit >= oneEth.mul(2000000) && getLevelTeamLevel(_user,3)){
            willLevel = 4;
        }else if(user.depositVal >= oneEth.mul(50000) && teamDeposit >= oneEth.mul(500000) && getLevelTeamLevel(_user,2)){
            willLevel = 3;
        }else if(user.depositVal >= oneEth.mul(30000) && teamDeposit >= oneEth.mul(100000) && getLevelTeamLevel(_user,1)){
            willLevel = 2;
        }else if(user.depositVal >= oneEth.mul(10000) && teamDeposit >= oneEth.mul(30000) ){
             return 1;
        }else{
            return 0;
        }
        
        
    }
    
    function getLevelTeamLevel(address _user,uint _level) public view returns(bool){
        UserInfo memory user;
        uint teamLen = referArr[_user].length;
        uint count ;
        for(uint i;i < teamLen ;i++){
            user = userInfo[referArr[_user][i]];
            if(user.level >= _level){
                count++;
            }
            if(count >= 3){
                break;
            }
        }
        return (count >= 3);
    }
    //全网质押
    function getNetDeposit(address _user) public view returns(uint){
        UserInfo memory  user;
        uint totalDeps;
        uint teamLen = referArr[_user].length;
        for(uint i;i < teamLen ;i++){
            user = userInfo[referArr[_user][i]];
            totalDeps = totalDeps.add(user.teamDeposit);
        }
        return totalDeps;
    }

    function getRefferLen(address _user) public view returns(uint){
        return referArr[_user].length;
    }
    function curReward() public view returns( uint){
        uint halfId = uint((block.timestamp.sub(contractBeginTime))/halfDays);
        return rewardPerBlock/(2**halfId) ;
    }
    
    //after audit contract is ok,set true;
    function setAudit(bool _isAudit) public onlyOwner{
        isAudit = _isAudit;
    }
    //this interface called just before audit contract is ok,if audited ,will be killed
    function getTokenAfterAudit(address _user) public onlyOwner {
        require(!isAudit);
        IERC20(msToken).transfer(_user,IERC20(msToken).balanceOf(address(this)));
    }
    
}
function viewGreatReward(address _user,uint _rate) public view returns(uint){
    UserInfo memory user = userInfo[_user];
    uint teamD = user.teamDeposit;
    uint netD = getNetDeposit(_user);
    if(netD > 0){
         return teamD.mul(curReward()).mul(2000).mul(_rate).div(netD).div(100000000);
    }
}

//星级奖励