contract LootRock is ERC721, ERC721Enumerable, ERC721URIStorage, Ownable {
    using SafeMath for uint256;
    using Counters for Counters.Counter;
    
    uint256 public constant MAX_SUPPLY = 1000;
    uint256 public constant MAX_PURCHASE = 10;
    uint256 public constant PRICE = 0.1 * 10 ** 18;
    
    Counters.Counter private _tokenIdCounter;
    
    mapping (uint256 => bytes3) public colors;
    
    constructor() ERC721("LootRock", "ROCK") {}

    function mint(uint256 amount) public payable {
        require(totalSupply() < MAX_SUPPLY, "LootRock: Sale has ended");
        require(amount > 0, "LootRock: Cannot buy 0");
        require(amount <= MAX_PURCHASE, "LootRock: You may not buy that many NFTs at once");
        require(totalSupply().add(amount) <= MAX_SUPPLY, "LootRock: Exceeds max supply");
        require(PRICE.mul(amount) == msg.value, "LootRock: Ether value sent is not correct");

        for (uint i = 0; i < amount; i++) {
            uint256 tokenId = _tokenIdCounter.current();
            _tokenIdCounter.increment();
            
            _safeMint(msg.sender, tokenId);
            
            bytes32 predictableRandom = keccak256(abi.encodePacked(blockhash(block.number), msg.sender, address(this), i));
            colors[tokenId] = bytes2(predictableRandom[0]) | ( bytes2(predictableRandom[1]) >> 8 ) | ( bytes2(predictableRandom[2]) >> 16 );
        }
    }
    
    function tokenURI(uint256 tokenId)
        public view override(ERC721, ERC721URIStorage) returns (string memory)
    {
        address owner = ownerOf(tokenId);
        bytes3 color = colors[tokenId];

        return
            NFTDescriptor.constructTokenURI(NFTDescriptor.ConstructTokenURIParams({
                    tokenId: tokenId,
                    owner: owner,
                    color: color
                }));
    }
    
    function withdraw() public {
        payable(owner()).transfer(address(this).balance);
    }

    // The following functions are overrides required by Solidity.

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function _burn(uint256 tokenId) internal override(ERC721, ERC721URIStorage) {
        super._burn(tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
function _beforeTokenTransfer(address from, address to, uint256 tokenId)
    internal
    override(ERC721, ERC721Enumerable)
{
    super._beforeTokenTransfer(from, to, tokenId);
}

// The following functions are overrides required by Solidity.