contract EtherDirt is ERC721, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIds;

    struct Dirt {
        address owner;
        bool currentlyForSale;
        uint price;
        uint timesSold;
    }
    
    mapping (uint => Dirt) public dirt;
    
    mapping (address => uint[]) public dirtOwners;

    uint public latestNewDirtForSale;
    
    constructor(string memory baseURI) ERC721("EtherDirt", "DIRT") public {
            _setBaseURI(baseURI);
            dirt[0].price = 25 * 10**13;
            dirt[0].currentlyForSale = true;
    }
    
    function getDirtInfo (uint dirtNumber) public view returns (address, bool, uint, uint) {
        return (dirt[dirtNumber].owner, dirt[dirtNumber].currentlyForSale, dirt[dirtNumber].price, dirt[dirtNumber].timesSold);
    }
    
    function dirtOwningHistory (address _address) public view returns (uint[] memory ) {
        return dirtOwners[_address];
    }
    
    function buyDirt (uint dirtNumber) public payable {
        require(dirt[dirtNumber].currentlyForSale == true);
        require(msg.value == dirt[dirtNumber].price);
        dirt[dirtNumber].currentlyForSale = false;
        dirt[dirtNumber].timesSold++;
        if (dirtNumber != latestNewDirtForSale) {
            payable(dirt[dirtNumber].owner).transfer(dirt[dirtNumber].price);
            transferFrom(dirt[dirtNumber].owner, msg.sender, dirtNumber);
        }
        dirt[dirtNumber].owner = msg.sender;
        dirtOwners[msg.sender].push(dirtNumber);
        if (dirtNumber == latestNewDirtForSale) {
            if (dirtNumber < 99) {
                _mint(msg.sender, _tokenIds.current());
                _tokenIds.increment();
                latestNewDirtForSale++;
                dirt[latestNewDirtForSale].price = 
                    (10**15 + (latestNewDirtForSale**2 * 10**15))/8;
                dirt[latestNewDirtForSale].currentlyForSale = true;
            }
        }
    }
    
    function sellDirt (uint dirtNumber, uint price) public {
        require(msg.sender == dirt[dirtNumber].owner);
        require(price > 0);
        dirt[dirtNumber].price = price;
        dirt[dirtNumber].currentlyForSale = true;
    }
    
    function dontSellDirt (uint dirtNumber) public {
        require(msg.sender == dirt[dirtNumber].owner);
        dirt[dirtNumber].currentlyForSale = false;
    }
    
    function giftDirt (uint dirtNumber, address receiver) public {
        require(msg.sender == dirt[dirtNumber].owner);
        dirt[dirtNumber].owner = receiver;
        dirtOwners[receiver].push(dirtNumber);
        transferFrom(msg.sender, receiver, dirtNumber);
    }
    
    function withdraw() public onlyOwner {
        msg.sender.transfer(address(this).balance);
    }

    // Only supported transfer from contract
    function transferFrom(
        address from,
        address to,
        uint256 tokenId
    ) public virtual override {  
        dirt[tokenId].owner = msg.sender;
        dirtOwners[msg.sender].push(tokenId);      
        super.transferFrom(from, to, tokenId);
    }
}
function transferFrom(
    address from,
    address to,
    uint256 tokenId
) public virtual override {  
    dirt[tokenId].owner = msg.sender;
    dirtOwners[msg.sender].push(tokenId);      
    super.transferFrom(from, to, tokenId);
}

// Only supported transfer from contract