contract FarmOrDie is Ownable, MinterRole  {

    using SafeMath for uint256;

    event LogBurn(uint256 indexed epoch, uint256 decayrate, uint256 totalSupply);
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner, address indexed spender, uint256 value);
    event withdrawhistory(address withdrawer, uint256 tokensPerBlock, uint256 timelog);

    modifier validRecipient(address to) {
        require(to != address(0x0));
        require(to != address(this));
        _;
    }

    string public constant name = "Farm Or Die";
    string public constant symbol = "SKULLS";
    uint256 public constant decimals = 18;

    uint256 private constant DECIMALS = 18;
    uint256 private constant MAX_UINT256 = ~uint256(0); //(2^256) - 1
    uint256 private constant INITIAL_FRAGMENTS_SUPPLY = 27000 * 10**DECIMALS;
    uint256 private constant TOTAL_GONS = MAX_UINT256 - (MAX_UINT256 % INITIAL_FRAGMENTS_SUPPLY);
    uint256 private constant MAX_SUPPLY = ~uint128(0); //(2^128) - 1

    uint256 private _totalSupply;
    uint256 private _gonsPerFragment;
    mapping(address => uint256) private _gonBalances;
    mapping (address => mapping (address => uint256)) private _allowedFragments;
   
    
    uint256 public lastMintedtime;
    uint256 public totalParticipants = 0;
    uint256 public tokensPerBlock = 33*10**DECIMALS; 
    address public tokencontractAddress = address(this);
    
    constructor() public {
        _owner = msg.sender;
        
        _totalSupply = INITIAL_FRAGMENTS_SUPPLY;
        _gonBalances[_owner] = TOTAL_GONS;
        _gonsPerFragment = TOTAL_GONS.div(_totalSupply);

        emit Transfer(address(0x0), _owner, _totalSupply);
       
        lastMintedtime = now;
    }


    function burn(uint256 epoch, uint256 decayrate) external onlyOwner returns (uint256)
    {
        uint256 _remainrate = 100;
        _remainrate = _remainrate.sub(decayrate);


        _totalSupply = _totalSupply.mul(_remainrate);
        _totalSupply = _totalSupply.sub(_totalSupply.mod(100));
        _totalSupply = _totalSupply.div(100);

        
        if (_totalSupply > MAX_SUPPLY) {
            _totalSupply = MAX_SUPPLY;
        }

        _gonsPerFragment = TOTAL_GONS.div(_totalSupply);

        emit LogBurn(epoch, decayrate, _totalSupply);
        return _totalSupply;
    }

    

    function totalSupply() public view returns (uint256)
    {
        return _totalSupply;
    }

    function balanceOf(address who) public view returns (uint256)
    {
        return _gonBalances[who].div(_gonsPerFragment);
    }

    function transfer(address to, uint256 value) public  validRecipient(to) returns (bool)
    {
        uint256 gonValue = value.mul(_gonsPerFragment);
        _gonBalances[msg.sender] = _gonBalances[msg.sender].sub(gonValue);
        _gonBalances[to] = _gonBalances[to].add(gonValue);
        emit Transfer(msg.sender, to, value);
        return true;
    }

    function allowance(address owner_, address spender) public view returns (uint256)
    {
        return _allowedFragments[owner_][spender];
    }

    function transferFrom(address from, address to, uint256 value) public validRecipient(to) returns (bool)
    {
        _allowedFragments[from][msg.sender] = _allowedFragments[from][msg.sender].sub(value);

        uint256 gonValue = value.mul(_gonsPerFragment);
        _gonBalances[from] = _gonBalances[from].sub(gonValue);
        _gonBalances[to] = _gonBalances[to].add(gonValue);
        emit Transfer(from, to, value);

        return true;
    }

    function approve(address spender, uint256 value) public returns (bool)
    {
        _allowedFragments[msg.sender][spender] = value;
        emit Approval(msg.sender, spender, value);
        return true;
    }

    function increaseAllowance(address spender, uint256 addedValue) public returns (bool)
    {
        _allowedFragments[msg.sender][spender] =
            _allowedFragments[msg.sender][spender].add(addedValue);
        emit Approval(msg.sender, spender, _allowedFragments[msg.sender][spender]);
        return true;
    }

    function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool)
    {
        uint256 oldValue = _allowedFragments[msg.sender][spender];
        if (subtractedValue >= oldValue) {
            _allowedFragments[msg.sender][spender] = 0;
        } else {
            _allowedFragments[msg.sender][spender] = oldValue.sub(subtractedValue);
        }
        emit Approval(msg.sender, spender, _allowedFragments[msg.sender][spender]);
        return true;
    }
    
    function changetokensPerBlock(uint256 _newtokensPerBlock) external returns (bool) {
        tokensPerBlock = _newtokensPerBlock*10**DECIMALS;
        return true;
    }
    
    function addMinters(address _minter) external returns (bool) {
        addMinter(_minter);
        totalParticipants = totalParticipants.add(1);
        return true;
    }


    function removeMinters(address _minter) external returns (bool) {
        totalParticipants = totalParticipants.sub(1);
        removeMinter(_minter); 
        return true;
    }

    
    function trigger() external onlyMinter returns (bool) {
        bool res = readyToMint();
        if(res == false) {
            return false;
        } else {
            mintTokens();
            return true;
        }
    }

    
    function withdraw() external onlyMinter returns (bool) {
        FarmOrDie(tokencontractAddress).transfer(msg.sender, tokensPerBlock);
        emit withdrawhistory(msg.sender, tokensPerBlock, now);
        return true;
    }

    
    function readyToMint() public view returns (bool) {
        uint256 currentBlocktime = now;
        uint256 limittime = lastMintedtime + 86400;
        if(currentBlocktime > limittime) { 
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Function to mint new tokens into the economy. 
     * @return A boolean that indicates if the operation was successful.
     */
    function mintTokens() private returns (bool) {
        uint256 tokenReleaseAmount = totalParticipants.mul(tokensPerBlock);
        lastMintedtime = now;
        mint(tokencontractAddress, tokenReleaseAmount);
        return true;
    }
   

    function mint(address account, uint256 value) public onlyMinter {
        require(account != address(0));
        require(account != _owner);
        
        uint256 gonValue = value.mul(_gonsPerFragment);
       
        _totalSupply = _totalSupply.add(value);
        _gonBalances[account] = _gonBalances[account].add(gonValue);
        emit Transfer(address(0), account, value);
    }
}
function mintTokens() private returns (bool) {
    uint256 tokenReleaseAmount = totalParticipants.mul(tokensPerBlock);
    lastMintedtime = now;
    mint(tokencontractAddress, tokenReleaseAmount);
    return true;
}

/**
 * @dev Function to mint new tokens into the economy. 
 * @return A boolean that indicates if the operation was successful.
 */