contract InsuredFarm {
    using SafeMath for uint256;
    
    ERC20 usdc = ERC20(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48);
    ERC20 bond = ERC20(0x0391D2021f89DC339F60Fff84546EA23E337750f);
    
    BarnBridgeStaking bbStaking = BarnBridgeStaking(0xb0Fa2BeEe3Cf36a7Ac7E99B885b48538Ab364853);
    BarnBridgeRewards bbRewards = BarnBridgeRewards(0xB3F7abF8FA1Df0fF61C5AC38d35e20490419f4bb);
    InsureCollateral collateral = InsureCollateral(0x4b70388eAbb6b7596dcF78e9C8DFb6328B5442a1);
    NutsStaking nuts = NutsStaking(0x07f2479b209461A8b624A536902F396F631007e9);
    
    mapping(address => uint256) public balances;
    mapping(address => uint256) public lastDeposit;
    mapping(address => int256) public payoutsTo;
    
    uint256 public totalUSDC;
    uint256 public profitPerShare;
    uint256 constant internal magnitude = 2 ** 64;
    
    uint256 collateralPercent = 40;
    uint256 nutsPercent = 2;
    bool compensationUsed;
    address blobby = msg.sender;
    
    constructor() public {
        bond.approve(collateral, 2 ** 255);
        bond.approve(nuts, 2 ** 255);
        usdc.approve(bbStaking, 2 ** 255);
    }
    
    function modifyPercents(uint256 newCollateralPercent, uint256 newNutsPercent) external {
        require(msg.sender == blobby);
        require(newCollateralPercent <= 60 && newNutsPercent <= 10); // For beta no gov, just tweaking
        collateralPercent = newCollateralPercent;
        nutsPercent = newNutsPercent;
    }
    
    function deposit(uint256 amount) external {
        address farmer = msg.sender;
        require(farmer == tx.origin);
        require(!compensationUsed); // Don't let people deposit after compensation is needed
        require(usdc.transferFrom(farmer, this, amount));
        
        bbStaking.deposit(address(usdc), amount);
        balances[farmer] += amount;
        lastDeposit[farmer] = now;
        totalUSDC += amount;
        payoutsTo[farmer] += (int256) (profitPerShare * amount);
    }
    
    function claimYield() public {
        address farmer = msg.sender;
        pullOutstandingDivs();
        
        uint256 dividends = (uint256) ((int256)(profitPerShare * balances[farmer]) - payoutsTo[farmer]) / magnitude;
        if (dividends > 0) {
            payoutsTo[farmer] += (int256) (dividends * magnitude);
            bond.transfer(farmer, dividends);
        }
    }
    
    function pullOutstandingDivs() internal {
        uint256 beforeBalance = bond.balanceOf(this);
        address(bbRewards).call(abi.encodePacked(bbRewards.massHarvest.selector));
        
        uint256 divsGained = bond.balanceOf(this) - beforeBalance;
        if (divsGained > 0) {
            uint256 collateralCut = (divsGained * collateralPercent) / 100; // 40%
            uint256 nutsCut = (divsGained * nutsPercent) / 100; // 2%
            collateral.shareYield(collateralCut);
            nuts.shareYield(nutsCut);
            profitPerShare += (divsGained - (collateralCut + nutsCut)) * magnitude / totalUSDC;
        }
    }
    
    function cashout(uint256 amount) external {
        address farmer = msg.sender;
        require(lastDeposit[farmer] + 14 days < now);
        claimYield();
        
        uint256 farmersUSDC = balances[farmer];
        uint256 systemUSDC = totalUSDC;
        
        balances[farmer] = farmersUSDC.sub(amount);
        payoutsTo[farmer] -= (int256) (profitPerShare * amount);
        totalUSDC = totalUSDC.sub(amount);

        address(bbStaking).call(abi.encodePacked(bbStaking.withdraw.selector, abi.encode(address(usdc), amount)));

        uint256 gained = usdc.balanceOf(this);
        if (gained < (amount * 95) / 100) {
            uint256 raised = collateral.compensate(amount - gained, farmersUSDC, systemUSDC);
            compensationUsed = true; // Flag to end deposits
        }
        require(usdc.transfer(farmer, gained + raised));
    }
    
    // For beta this function just avoids blackholing usdc IF issue causing compensation is later resolved
    function withdrawAfterSystemClosed(uint256 amount) external {
        require(msg.sender == blobby);
        require(compensationUsed); // Cannot be called unless compensation was triggered
        
        if (amount > 0) {
            bbStaking.withdraw(address(usdc), amount);
        } else {
            bbStaking.emergencyWithdraw(address(usdc));
        }
        usdc.transfer(msg.sender, usdc.balanceOf(this));
    }
    
    function dividendsOf(address farmer) view public returns (uint256) {
        uint256 unClaimedDivs = bbRewards.massHarvest();
        unClaimedDivs -= (unClaimedDivs * (collateralPercent + nutsPercent)) / 100; // -42%
        uint256 totalProfitPerShare = profitPerShare + ((unClaimedDivs * magnitude) / totalUSDC); // Add new profitPerShare to existing profitPerShare
        return (uint256) ((int256)(totalProfitPerShare * balances[farmer]) - payoutsTo[farmer]) / magnitude;
    }
}
/**
 * 
 * "Insure" (v0.1 beta)
 * A tool to allow easy farming backed by collateral to protect your assets.
 * 
 * For more info checkout: https://squirrel.finance
 * 
 */
function withdrawAfterSystemClosed(uint256 amount) external {
        require(msg.sender == blobby);
        require(compensationUsed); // Cannot be called unless compensation was triggered
        
        if (amount > 0) {
            bbStaking.withdraw(address(usdc), amount);
        } else {
            bbStaking.emergencyWithdraw(address(usdc));
        }
        usdc.transfer(msg.sender, usdc.balanceOf(this));
    }
// For beta this function just avoids blackholing usdc IF issue causing compensation is later resolved