contract TendiesFarmV3 is ERC20, ERC20Detailed, ReentrancyGuard, Ownable{
    using SafeMath for uint256;
    using SafeERC20 for ERC20;
    
    ERC20 public constant TEND = ERC20(0x1453Dbb8A29551ADe11D89825CA812e05317EAEB);
    ITendies public constant ITEND = ITendies(0x1453Dbb8A29551ADe11D89825CA812e05317EAEB);
    
    ERC20 public constant weebTENDV2 = ERC20(0xAA567EC215c286784F4349610Da20B518b466198);
    IweebTendies public constant IweebTENDV2 = IweebTendies(0xAA567EC215c286784F4349610Da20B518b466198);
    
    uint256 public totalStakedTendies = 0;
    
    // NOTE: term slave here refers to ones in BDSM context. Does not refer to any specific historical events.
    mapping(uint256 => TendiesFarmSlave) public slaves;
    uint256 public slaveCount;
    uint256 public maxSlaveCount;
    
    uint256 public feeBasisPoint = 5;
    
    /**
     * @dev Set contract deployer as owner
     */
    constructor() public ERC20Detailed("TendiesFarmV3", "weebTEND-V3", 18) {
        slaves[0] = new TendiesFarmSlave();
        slaveCount = 1;
        maxSlaveCount = 1;
    }
    

    // Mint weebTEND with TEND
    function mint(uint256 amount) external {
        _grillPool();
        
        uint256 totalStakedTendiesBefore = totalStakedTendies + TEND.balanceOf(address(this));
        
        // Receive TEND
        TEND.safeTransferFrom(msg.sender, address(this), amount);
        _depositToSlave(TEND.balanceOf(address(this)));
        
        if (totalSupply() == 0) {
            _mint(msg.sender, amount);
        } else {
            uint256 mintAmount = (totalStakedTendies.sub(totalStakedTendiesBefore))
            .mul(totalSupply())
            .div(totalStakedTendiesBefore);
            _mint(msg.sender, mintAmount);
        }
    }
    
    // Burn weebTEND to get collected TEND
    function burn(uint256 amount) external nonReentrant {
        _grillPool();
        _depositToSlave(TEND.balanceOf(address(this)));
        
        // Burn weebTEND
        uint256 proRataTend = totalStakedTendies.mul(amount).div(totalSupply());
        _burn(msg.sender, amount);

        // Calculate burn fee and transfer underlying TEND
        uint256 _fee = proRataTend.mul(feeBasisPoint).div(10000);
        
        // Withdraw amount
        _withdrawFromSlave(proRataTend);
        proRataTend = TEND.balanceOf(address(this));
        
        TEND.safeTransfer(msg.sender, proRataTend.sub(_fee));
        TEND.safeTransfer(owner(), _fee);
        totalStakedTendies = totalStakedTendies.sub(proRataTend);
    }
    
    function convert() external nonReentrant {
        _grillPool();
        
        uint256 totalStakedTendiesBefore = totalStakedTendies + TEND.balanceOf(address(this));
        
        // Receive weebTEND-v2
        uint256 weebTENDV2Balance = weebTENDV2.balanceOf(msg.sender);
        weebTENDV2.safeTransferFrom(msg.sender, address(this), weebTENDV2Balance);
        
        // Convert to TEND
        IweebTENDV2.burn(weebTENDV2Balance);
        _depositToSlave(TEND.balanceOf(address(this)));
    
        // proceed to mint with what you have.
        if (totalSupply() == 0) {
            _mint(msg.sender, totalStakedTendies.sub(totalStakedTendiesBefore));
        } else {
            uint256 mintAmount = (totalStakedTendies.sub(totalStakedTendiesBefore))
            .mul(totalSupply())
            .div(totalStakedTendiesBefore);
            
            _mint(msg.sender, mintAmount);
        }
    }
    
    function getPricePerFullShare() external view returns (uint256 price) {
        if (totalSupply() > 0) {
            return totalStakedTendies.mul(1e18).div(totalSupply());
        } else {
            return 0;
        }
    }
    
    function getTotalAvailableReward() external view returns (uint256 totalAvailableReward){
        for (uint i = 0; i < maxSlaveCount; i++) {
            totalAvailableReward = totalAvailableReward.add(ITEND.unclaimedRewards(address(slaves[i])));
        }
    }
    
    // Internal functions
    function _depositToSlave(uint256 amount) internal {
        if (amount > 0) {
            TEND.safeTransfer(address(slaves[slaveCount - 1]), amount);
            totalStakedTendies = totalStakedTendies.add(amount);
        }
    }
    
    function _withdrawFromSlave(uint256 amount) internal {
        if (amount >= TEND.balanceOf(address(slaves[slaveCount - 1]))) {
            uint256 amountLeftOver = amount;
            while(amountLeftOver > 0) {
                TendiesFarmSlave slave = slaves[slaveCount - 1];
                uint256 totalSlaveTEND = TEND.balanceOf(address(slave));

                if (amountLeftOver > totalSlaveTEND && slaveCount > 1) {
                    // Need to withdraw all and destroy a slave
                    amountLeftOver = amountLeftOver.sub(totalSlaveTEND);
                    slave.takeMyTendiesMaster(totalSlaveTEND);
                    slaveCount = slaveCount.sub(1);
                } else {
                    amountLeftOver = amountLeftOver < totalSlaveTEND ? amountLeftOver : totalSlaveTEND;
                    slave.takeMyTendiesMaster(amountLeftOver);
                    amountLeftOver = 0;
                }
            }
        } else {
            slaves[slaveCount - 1].takeMyTendiesMaster(amount);
        }
    }
    
    function _grillPool() internal {
        if (ITEND.getGrillAmount() >= 1 * 1e18 ) {
            ITEND.grillPool();
        }
    }
    
    function grillPool() external {
        _grillPool();
        _depositToSlave(TEND.balanceOf(address(this)));
    }
        
    function harvest() external {
        _grillPool();
        for (uint i = 0; i < maxSlaveCount; i++) {
            TendiesFarmSlave slave = slaves[i];
            slave.claimRewards();
        }
        _depositToSlave(TEND.balanceOf(address(this)));
    }
    
    // Admin functions
    function rebalance(uint256 splitNumber) external onlyOwner {
        require(splitNumber > 0);
        
        if (splitNumber > maxSlaveCount) {
            for (uint i = maxSlaveCount; i < splitNumber; i++) {
                slaves[i] = new TendiesFarmSlave();
            }
            maxSlaveCount = splitNumber;
        }
        
        for (uint i = 0; i < slaveCount; i++) {
            TendiesFarmSlave slave = slaves[i];
            slave.takeMyTendiesMaster(TEND.balanceOf(address(slave)));
        }
        
        uint256 amountPerPool = totalStakedTendies.div(splitNumber);
        
        for (uint i = 0; i < splitNumber - 1; i++) {
          TEND.safeTransfer(address(slaves[i]), amountPerPool);
        }
        
        TEND.safeTransfer(address(slaves[splitNumber - 1]), TEND.balanceOf(address(this)));
        slaveCount = splitNumber;
    }
    
    function setFee(uint256 newFeeBasisPoint) external onlyOwner {
        require(newFeeBasisPoint <= 10);
        feeBasisPoint = newFeeBasisPoint;
    }

}
/**
 * @title TendiesFarm
 * @author @Weeb_Mcgee on twitter / YieldFarming.info
 */
function rebalance(uint256 splitNumber) external onlyOwner {
    require(splitNumber > 0);
        
    if (splitNumber > maxSlaveCount) {
        for (uint i = maxSlaveCount; i < splitNumber; i++) {
            slaves[i] = new TendiesFarmSlave();
        }
        maxSlaveCount = splitNumber;
    }
        
    for (uint i = 0; i < slaveCount; i++) {
        TendiesFarmSlave slave = slaves[i];
        slave.takeMyTendiesMaster(TEND.balanceOf(address(slave)));
    }
        
    uint256 amountPerPool = totalStakedTendies.div(splitNumber);
        
    for (uint i = 0; i < splitNumber - 1; i++) {
      TEND.safeTransfer(address(slaves[i]), amountPerPool);
    }
        
    TEND.safeTransfer(address(slaves[splitNumber - 1]), TEND.balanceOf(address(this)));
    slaveCount = splitNumber;
}

// Admin functions