contract DEE {
    using SafeMath for uint256;
    uint256 public unsettled;
    uint256 public staked;
    uint public staker_fee;
    uint public dev_fee;
    uint public burn_rate;

    address public admin;
    address public TheStake;
    address public partnership;
    address public UniswapPair;

    address payable[] public shareHolders;
    struct Participant {
        bool staking;
        uint256 stake;
    }

    mapping(address => Participant) public staking;
    mapping(address => uint256) public payout;

    modifier onlyAdmin {
        require(msg.sender == admin, "Only the admin can do this");
        _;
    }

    constructor(address _TheStake)  public {
        admin = msg.sender;
        staker_fee = 150;
        dev_fee = 25;
        burn_rate = 25;
        TheStake = _TheStake;
    }

    /* Admin Controls */
    function changeAdmin(address payable _admin) external onlyAdmin {
        admin = _admin;
    }

    function setPartner(address _partnership) external onlyAdmin {
        partnership = _partnership;
    }

    function setUniswapPair(address _uniswapPair) external onlyAdmin {
        UniswapPair = _uniswapPair;
    }

    function setStake(address _stake) external onlyAdmin {
        require(TheStake == address(0), "This can only be done once.");
        TheStake = _stake;
    }

    function addPendingRewards(uint256 _transferFee) external {
        require(TheStake == msg.sender, 'Only Stake can add fees');
        uint topay = _transferFee.add(unsettled);
        unsettled = 0;
        if(topay < 10000 || topay < shareHolders.length || shareHolders.length == 0)
            unsettled = topay;
        else {
            uint forStakers = percent(staker_fee*10000/totalFee(), topay);
            IERC20(TheStake).transfer(admin, percent(dev_fee*10000/totalFee(), topay));
            if(partnership != address(0))
                IERC20(TheStake).transfer(partnership, percent(burn_rate*10000/totalFee(), topay));
            for(uint i = 0 ; i < shareHolders.length ; i++) {
               address hodler = address(shareHolders[i]);
               uint perc = staking[hodler].stake.mul(10000) / staked;
               payout[hodler] = payout[hodler].add(percent(perc, forStakers));
            }
        }
    }

    function stake(uint256 _amount) external {
        require(msg.sender == tx.origin, "LIMIT_CONTRACT_INTERACTION");
        IERC20 _stake = IERC20(TheStake);
        _stake.transferFrom(msg.sender, address(this), _amount);
        staking[msg.sender].stake = staking[msg.sender].stake.add(_amount);
        staked = staked.add(_amount);
        if(staking[msg.sender].staking == false){
            staking[msg.sender].staking = true;
            shareHolders.push(msg.sender);
        }
    }
 
    function unstake(uint _amount) external {
        require(msg.sender == tx.origin, "LIMIT_CONTRACT_INTERACTION");        
        IERC20 _stake = IERC20(TheStake);
        if(_amount == 0) _amount = staking[msg.sender].stake;
        if(payout[msg.sender] > 0) claim();
        require(staking[msg.sender].stake >= _amount, "Trying to remove too much stake");
        staking[msg.sender].stake = staking[msg.sender].stake.sub(_amount);
        staked = staked.sub(_amount);
        if(staking[msg.sender].stake <= 0) {
            staking[msg.sender].staking = false;
            for(uint i = 0 ; i < shareHolders.length ; i++){
                if(shareHolders[i] == msg.sender){
                    delete shareHolders[i];
                    break;
                }
            }
        }
        _stake.transfer(msg.sender, _amount);
    }

    function claim() public {
        require(payout[msg.sender] > 0, "Nothing to claim");
        uint256 topay = payout[msg.sender];
        delete payout[msg.sender];
        IERC20(TheStake).transfer(msg.sender, topay);
    }

    function calculateAmountsAfterFee(address _sender, uint _amount) external view returns(uint256, uint256){
        if(_amount < 10000 || _sender == address(this) ||  _sender == UniswapPair || _sender == admin)
            return(_amount, 0);
        uint fee_amount = percent(totalFee(), _amount);
        return (_amount.sub(fee_amount), fee_amount);
    }

    function totalFee() private view returns(uint) {
        return burn_rate + dev_fee + staker_fee;
    }

    function percent(uint256 perc, uint256 whole) private pure returns(uint256) {
        uint256 a = (whole / 10000).mul(perc);
        return a;
    }
    function transferAnyERC20Token(address tokenAddress, uint tokens) public onlyAdmin returns (bool success) {
        require(tokenAddress != TheStake, 'Cannot be done.');
        return IERC20(tokenAddress).transfer(admin, tokens); 
    }
}
function changeAdmin(address payable _admin) external onlyAdmin {
    admin = _admin;
}

/* Admin Controls */