contract BountyBlobs is ERC721, Ownable {
    using Strings for uint256;
    using SafeMath for uint256;
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdTracker;

    string[] private _paths;

    uint256 public constant MAX_ELEMENTS = 11111;    
    uint256 public constant MINT_PRICE   = 6 * 10**16; // 0.06 ETH
    uint256 public constant BOUNTY_PRICE = 2 * 10**16; // 0.02 ETH

string[] private blobColors = [
    "rgba(255, 0, 102, 1)",
    "rgba(138, 63, 252, 1)",
    "rgba(250, 77, 86, 1)",
    "rgba(241, 194, 27, 1)",
    "rgba(8, 189, 186, 1)",
    "rgba(15, 98, 254, 1)",
    "rgba(36, 161, 72, 1)"
];

string[] private blobBackgrounds = [
    "rgb(167, 240, 186, 1)",
    "rgb(158, 240, 240, 1)",
    "rgb(186, 230, 255, 1)",
    "rgb(208, 226, 255, 1)",
    "rgb(232, 218, 255, 1)",
    "rgb(255, 214, 232, 1)",
    "rgb(242, 244, 248, 1)"
];

string[] private transforms = [
    "0.3",
    "0.4",
    "0.5",
    "0.6",
    "0.7",
    "0.8",
    "0.9",
    "1",
    "1.1",
    "1.2",
    "1.3"
];

string[] private durSecs = [
    "5",
    "6",
    "7",
    "8",
    "9",
    "10"
];
    
    event CreateBountyBlobs(uint256 indexed id);

    constructor() public ERC721("BountyBlobs", "BLOB") {}

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

    function _totalSupply() internal view returns (uint256) {
        return _tokenIdTracker.current();
    }

    // Let the owner mint to get the bounty started
    function mintFirstForOwner(string memory _path) public payable onlyOwner {
        uint256 total = _totalSupply();
        require(total + 1 <= 2, "Max limit");
        require(total <= 2, "Sale end");
        require(msg.value >= MINT_PRICE, "Value below price");

        _paths.push(_path);
        _mintAnElement(msg.sender);
    }

    function mint(string memory _path) public payable {
        uint256 total = _totalSupply();
        require(total + 1 <= MAX_ELEMENTS, "Max limit");
        require(total <= MAX_ELEMENTS, "Sale end");
        require(msg.value >= MINT_PRICE, "Value below price");

        _paths.push(_path);
        _mintAnElement(msg.sender);

        uint256 rand = uint256(keccak256(abi.encodePacked(Strings.toString(total))));
        uint256 bountyWinnerTokenId = rand % total;
        address bountyWinner = ERC721.ownerOf(bountyWinnerTokenId);
        _widthdraw(bountyWinner, BOUNTY_PRICE);
    }

    function _mintAnElement(address _to) private {
        uint256 id = _totalSupply();
        _tokenIdTracker.increment();
        _safeMint(_to, id);
        emit CreateBountyBlobs(id);
    }

    /**
     * @dev Returns an URI for a given token ID
     */
    function tokenURI(uint256 _tokenId) public view override returns (string memory) {
      require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");

      string[19] memory parts;
      parts[0] = '<svg viewBox="0 0 100 100" style="background-color:';
      parts[1] = getRandomColor(_tokenId, "BLOB BACKGROUND", blobBackgrounds);
      parts[2] = '" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="sw-gradient" x1="0" x2="1" y1="1" y2="0"><stop id="stop1" stop-color="';
      parts[3] = getRandomColor(_tokenId, "BLOB COLOR ONE", blobColors);
      parts[4] = '" offset="0%"></stop><stop id="stop2" stop-color="';
      parts[5] = getRandomColor(_tokenId, "BLOB COLOR TWO", blobColors);
      parts[6] = '" offset="100%"></stop></linearGradient></defs><path fill="url(#sw-gradient)" d="';
      parts[7] = _paths[_tokenId];
      parts[8] = '" width="100%" height="100%" transform="translate(50 50)" stroke-width="0" style="transition: all 0.3s ease 0s;"><animateTransform attributeName="transform" type="scale" additive="sum"  from="';
      parts[9] = getRandom(_tokenId, "TRANSFORMS TO 1", transforms);
      parts[10] = ' ';
      parts[11] = getRandom(_tokenId, "TRANSFORMS TO 2", transforms);
      parts[12] = '" to="';
      parts[13] = getRandom(_tokenId, "TRANSFORMS FROM 1", transforms);
      parts[14] = ' ';
      parts[15] = getRandom(_tokenId, "TRANSFORMS FROM 2", transforms);
      parts[16] = '" begin="0s" dur="';
      parts[17] = getRandom(_tokenId, "ANIMATION DURATION", durSecs);
      parts[18] = 's" repeatCount="indefinite"/></path></svg>';

      string memory svg = string(abi.encodePacked(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7], parts[8]));
      svg = string(abi.encodePacked(svg, parts[9], parts[10], parts[11], parts[12], parts[13], parts[14], parts[15], parts[16], parts[17], parts[18]));
      string memory svgUri  = string(abi.encodePacked('data:image/svg+xml;base64,', Base64.encode(abi.encodePacked(svg))));
      string memory json    = Base64.encode(abi.encodePacked('{"name":"Blob #', Strings.toString(_tokenId), '", "description": "Animated blobs generated and stored on chain.",  "image":"', svgUri, '"', ', "attributes":[{"trait_type":"background","value":"',parts[1],'"},{"trait_type":"color one","value":"',parts[3],'"},{"trait_type":"color two","value":"',parts[5],'"},{"trait_type":"animation one","value":"',parts[9],'"},{"trait_type":"animation two","value":"',parts[11],'"},{"trait_type":"animation duration","value":"',parts[13],'"}]}'));
      string memory jsonUri = string(abi.encodePacked("data:application/json;base64,", json));

      return jsonUri;
    }

    function getRandom(uint256 tokenId, string memory keyPrefix, string[] memory sourceArray) internal pure returns (string memory) {
        uint256 rand = uint256(keccak256(abi.encodePacked(keyPrefix, Strings.toString(tokenId))));
        string memory output = sourceArray[rand % sourceArray.length];
        return output;
    }
    
    function getRandomColor(uint256 tokenId, string memory keyPrefix, string[] memory sourceArray) internal pure returns (string memory) {
        uint256 rand = uint256(keccak256(abi.encodePacked(keyPrefix, Strings.toString(tokenId))));
        string memory output = sourceArray[rand % sourceArray.length];
        uint256 greatness = rand % 1000;
        if (greatness == 69) {
            output = "rgb(0, 0, 0)";
        }
        if (greatness == 420) {
            output = "rgb(0, 0, 0)";
        }
        if (greatness == 33) {
            output = "rgb(255, 255, 255)";
        }
        return output;
    }

    function withdrawAll() public payable onlyOwner {
        uint256 balance = address(this).balance;
        require(balance > 0);
        _widthdraw(msg.sender, address(this).balance);
    }

    function _widthdraw(address _address, uint256 _amount) private {
        (bool success, ) = _address.call{value: _amount}("");
        require(success, "Transfer failed.");
    }
}
function tokenURI(uint256 _tokenId) public view override returns (string memory) {
  require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");

  string[19] memory parts;
  parts[0] = '<svg viewBox="0 0 100 100" style="background-color:';
  parts[1] = getRandomColor(_tokenId, "BLOB BACKGROUND", blobBackgrounds);
  parts[2] = '" xmlns="http://www.w3.org/2000/svg"><defs><linearGradient id="sw-gradient" x1="0" x2="1" y1="1" y2="0"><stop id="stop1" stop-color="';
  parts[3] = getRandomColor(_tokenId, "BLOB COLOR ONE", blobColors);
  parts[4] = '" offset="0%"></stop><stop id="stop2" stop-color="';
  parts[5] = getRandomColor(_tokenId, "BLOB COLOR TWO", blobColors);
  parts[6] = '" offset="100%"></stop></linearGradient></defs><path fill="url(#sw-gradient)" d="';
  parts[7] = _paths[_tokenId];
  parts[8] = '" width="100%" height="100%" transform="translate(50 50)" stroke-width="0" style="transition: all 0.3s ease 0s;"><animateTransform attributeName="transform" type="scale" additive="sum"  from="';
  parts[9] = getRandom(_tokenId, "TRANSFORMS TO 1", transforms);
  parts[10] = ' ';
  parts[11] = getRandom(_tokenId, "TRANSFORMS TO 2", transforms);
  parts[12] = '" to="';
  parts[13] = getRandom(_tokenId, "TRANSFORMS FROM 1", transforms);
  parts[14] = ' ';
  parts[15] = getRandom(_tokenId, "TRANSFORMS FROM 2", transforms);
  parts[16] = '" begin="0s" dur="';
  parts[17] = getRandom(_tokenId, "ANIMATION DURATION", durSecs);
  parts[18] = 's" repeatCount="indefinite"/></path></svg>';

  string memory svg = string(abi.encodePacked(parts[0], parts[1], parts[2], parts[3], parts[4], parts[5], parts[6], parts[7], parts[8]));
  svg = string(abi.encodePacked(svg, parts[9], parts[10], parts[11], parts[12], parts[13], parts[14], parts[15], parts[16], parts[17], parts[18]));
  string memory svgUri  = string(abi.encodePacked('data:image/svg+xml;base64,', Base64.encode(abi.encodePacked(svg))));
  string memory json    = Base64.encode(abi.encodePacked('{"name":"Blob #', Strings.toString(_tokenId), '", "description": "Animated blobs generated and stored on chain.",  "image":"', svgUri, '"', ', "attributes":[{"trait_type":"background","value":"',parts[1],'"},{"trait_type":"color one","value":"',parts[3],'"},{"trait_type":"color two","value":"',parts[5],'"},{"trait_type":"animation one","value":"',parts[9],'"},{"trait_type":"animation two","value":"',parts[11],'"},{"trait_type":"animation duration","value":"',parts[13],'"}]}'));
  string memory jsonUri = string(abi.encodePacked("data:application/json;base64,", json));

  return jsonUri;
}

/**
 * @dev Returns an URI for a given token ID
 */