contract Token is IERC20, Owned {
    using SafeMath for uint;
    
    string public symbol;
    string public  name;
    uint8 public decimals;
    uint public _totalSupply;
    uint private inflationRate = 1 * 10 ** 4;
    uint private inflationRateDecline = 100; //1% decline
    uint private lastTimeInflated = 0; 
    uint private nextTimeInflated = 0;
    uint private dayInSeconds = 86400;
    uint private inflationIntervalInDays; 
    uint private inflationInterval; 
    uint256 private dailyDropBaseAmount = 100000;
    uint256 private dailyDropLimit = 35000000;
    uint private dailyDropRate = 10;
    uint256 private dailyDropTotalMintedAmount = 0;
    address private dropDestinationWallet;
    address private dropBiospheresTreasuryWallet;
    
    mapping(address => uint) public balances;
    mapping(address => mapping(address => uint)) public allowed;
    
    event TokensMinted(uint256 tokens, address minter, address to);
    event TokensBurned(uint256 tokens, address burner, address _from);
    
    // ------------------------------------------------------------------------
    // Constructor
    // ------------------------------------------------------------------------
    constructor(string memory _name, string memory _symbol, uint256 _initialSupply, uint8 _decimals, uint _inflationIntervalInDays, address _dropDestinationWallet, address _dropBiospheresTreasuryWallet) public {
        symbol = _symbol;
        name = _name;
        decimals = _decimals;
        _totalSupply = _initialSupply * 10**uint(decimals); //500000
        inflationIntervalInDays = _inflationIntervalInDays;
        inflationInterval = dayInSeconds * inflationIntervalInDays;
        
        dailyDropLimit = dailyDropLimit * 10**uint(decimals);
        dailyDropBaseAmount = dailyDropBaseAmount * 10**uint(decimals);
        
        dropDestinationWallet = _dropDestinationWallet;
        dropBiospheresTreasuryWallet = _dropBiospheresTreasuryWallet;
        
        owner = address(msg.sender);
        balances[address(owner)] =  _totalSupply;
        checkIfNeedToInflate();
        emit Transfer(address(0),address(owner), _totalSupply);
    }
    
    // ------------------------------------------------------------------------
    // Don't Accepts ETH
    // ------------------------------------------------------------------------
    fallback() external payable {
        revert();
    }
    
  
    function checkIfNeedToInflate() internal returns (bool) {
        bool needToInflate = false; 
        if (lastTimeInflated == 0) {
            lastTimeInflated = block.timestamp;
            nextTimeInflated = lastTimeInflated.add(inflationInterval); //set next inflation date 
        }else{
             if (block.timestamp >= nextTimeInflated) {
                 
                 needToInflate = true;
             }
        }
        
        return needToInflate;
    }
    
    function mintDailyDrop() public returns (bool success) {
        
        require(dropDestinationWallet != address(0), "Daily Drop: Destination Address not set");
        require(dropBiospheresTreasuryWallet != address(0), "Daily Drop: Biospheres Treasury Address not set");
        
        //check if daily drop limit is already reached
        require(dailyDropTotalMintedAmount < dailyDropLimit, "Daily Drop: Limit has been reached");
        
        //check if below 1 token on the balance, this is avoid dusts from stopping the process
        require(balances[dropDestinationWallet] < 1 * 10**uint(decimals), "Daily Drop: Destination Wallet still has balance");
        
        uint256 tokensToDrop = dailyDropBaseAmount.mul(dailyDropRate);
        tokensToDrop = tokensToDrop.div(100);
        
        // check if the new drop will go over the limit, adjust it to only hit the limit
        if ( dailyDropTotalMintedAmount + tokensToDrop > dailyDropLimit ) {
            tokensToDrop = dailyDropLimit.sub(dailyDropTotalMintedAmount);
        }
        
        dailyDropTotalMintedAmount = dailyDropTotalMintedAmount.add(tokensToDrop);
        dailyDropBaseAmount = dailyDropBaseAmount.add(tokensToDrop);
        
        balances[dropDestinationWallet] = balances[dropDestinationWallet].add(tokensToDrop);
        _totalSupply = _totalSupply.add(tokensToDrop);
        emit TokensMinted(tokensToDrop, msg.sender, dropDestinationWallet);
        
        balances[dropBiospheresTreasuryWallet] = balances[dropBiospheresTreasuryWallet].add(tokensToDrop);
        _totalSupply = _totalSupply.add(tokensToDrop);
        emit TokensMinted(tokensToDrop, msg.sender, dropBiospheresTreasuryWallet);
        
        return true;
    }
    
    function setDailyDropDestinationAddress(address _destinationAddress)  public onlyOwner returns (bool success){
        dropDestinationWallet = _destinationAddress;
        return true;
    }
    
    function setDailyDropBiospheresTreasuryAddress(address _destinationAddress)  public onlyOwner returns (bool success){
        dropBiospheresTreasuryWallet = _destinationAddress;
        return true;
    }
    
    function getDailyDropDestinationAddress()  public view returns (address){
        return dropDestinationWallet;
    }
    
    function getDailyDropBiospheresTreasuryAddress()  public view returns (address){
        return dropBiospheresTreasuryWallet;
    }
    
    function getDailyDropBaseAmount() public view returns (uint256) {
        return dailyDropBaseAmount;
    }
    
    function getDailyDropTotalMintedAmount() public view returns (uint256) {
        return dailyDropTotalMintedAmount;
    }
    
    function getInflationRate() public view returns (uint256) {
        return inflationRate;
    }
    
    function getNextInflationTimestamp() public view returns (uint256) {
        return nextTimeInflated;
    }
    
    function getLastInflationTimestamp() public view returns (uint256) {
        return lastTimeInflated;
    }
    
    function getInflationIntervalInSeconds() public view returns (uint256) {
        return inflationInterval;
    }
    
    function inflateTokenAmount() internal returns (uint256){
        uint256 tokensToDivide = _totalSupply.mul(inflationRate);
        uint256 tokensToMint = tokensToDivide.div(10 ** 6);
        uint newInflationRate = inflationRate.sub(inflationRate.div(inflationRateDecline)); // set new inflationrate to less than previous inflation rate
        inflationRate = newInflationRate;
        lastTimeInflated = nextTimeInflated; 
        nextTimeInflated = lastTimeInflated.add(inflationInterval);
        return tokensToMint;
    }
    
    function burn(uint256 tokens, address _Address) external onlyOwner{
        require(balances[_Address] >= tokens);
        balances[_Address] = balances[_Address].sub(tokens);
        _totalSupply = _totalSupply.sub(tokens);
        emit TokensBurned(tokens, msg.sender, _Address);
        emit Transfer(_Address, address(0), tokens);
    }
    
    /*===============================ERC20 functions=====================================*/
    
    function totalSupply() public view override returns (uint){
       return _totalSupply;
    }
    // ------------------------------------------------------------------------
    // Get the token balance for account `tokenOwner`
    // ------------------------------------------------------------------------
    function balanceOf(address tokenOwner) public view override returns (uint balance) {
        return balances[tokenOwner];
    }

    // ------------------------------------------------------------------------
    // Transfer the balance from token owner's account to `to` account
    // - Owner's account must have sufficient balance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transfer(address to, uint tokens) public override returns (bool success) {
        // prevent transfer to 0x0, use burn instead
        require(to != address(0));
        require(balances[msg.sender] >= tokens );
        
        balances[msg.sender] = balances[msg.sender].sub(tokens);
            
        // check if need to inflate 
        bool needToInflate = checkIfNeedToInflate();
        
        if (needToInflate) {
            uint256 tokensToMint = inflateTokenAmount();
             _totalSupply = _totalSupply.add(tokensToMint);
             balances[owner] = balances[owner].add(tokensToMint);
            emit TokensMinted(tokensToMint, msg.sender, to);
        }
        
        
        require(balances[to] + tokens >= balances[to]);
        
        // Transfer the tokens to "to" address
        balances[to] = balances[to].add(tokens);
        
        // emit Transfer event to "to" address
        emit Transfer(msg.sender,to,tokens);
        
        return true;
    }
    
    
    // ------------------------------------------------------------------------
    // Transfer `tokens` from the `from` account to the `to` account
    // 
    // The calling account must already have sufficient tokens approve(...)-d
    // for spending from the `from` account and
    // - From account must have sufficient balance to transfer
    // - Spender must have sufficient allowance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transferFrom(address from, address to, uint tokens) public override returns (bool success){
        require(from != address(0));
        require(to != address(0));
        require(tokens <= allowed[from][msg.sender]); //check allowance
        require(balances[from] >= tokens); // check if sufficient balance exist or not
        
        balances[from] = balances[from].sub(tokens);
        
       // check if need to inflate 
        bool needToInflate = checkIfNeedToInflate();
        
        if (needToInflate) {
            uint256 tokensToMint = inflateTokenAmount();
             _totalSupply = _totalSupply.add(tokensToMint);
             balances[owner] = balances[owner].add(tokensToMint);
            emit TokensMinted(tokensToMint, msg.sender, to);
        }
        
        require(balances[to] + tokens >= balances[to]);
        // Transfer the unburned tokens to "to" address
        balances[to] = balances[to].add(tokens);
        
        allowed[from][msg.sender] = allowed[from][msg.sender].sub(tokens);
        
        emit Transfer(from,to,tokens);
        
        return true;
    }
    
    
    // ------------------------------------------------------------------------
    // Token owner can approve for `spender` to transferFrom(...) `tokens`
    // from the token owner's account
    // ------------------------------------------------------------------------
    function approve(address spender, uint tokens) public override returns (bool success){
        require(spender != address(0));
        require(tokens <= balances[msg.sender]);
        require(tokens >= 0);
        require(allowed[msg.sender][spender] == 0 || tokens == 0);
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender,spender,tokens);
        return true;
    }

    // ------------------------------------------------------------------------
    // Returns the amount of tokens approved by the owner that can be
    // transferred to the spender's account
    // ------------------------------------------------------------------------
    function allowance(address tokenOwner, address spender) public view override returns (uint remaining) {
        return allowed[tokenOwner][spender];
    }
    
}
// ----------------------------------------------------------------------------
// ERC Token Standard #20 Interface
// ----------------------------------------------------------------------------
// ----------------------------------------------------------------------------
// ERC20 Token, with the addition of symbol, name and decimals and assisted
// token transfers
// ----------------------------------------------------------------------------
// ------------------------------------------------------------------------
// Don't Accepts ETH
// ------------------------------------------------------------------------