contract Blootvatars is ERC721Enumerable, ReentrancyGuard, Ownable {
  ERC721 bloot = ERC721(0x4F8730E0b32B04beaa5757e5aea3aeF970E5B613);

  string[] private hat = [
    '',
    '&#160;&#160;&#160;_|_',
    '&#160;&#160;&#160;( )',
    '&#160;&#160;&#160;_-_',
    '&#160;&#160;&#160;_!_',
    '&#160;&#160;&#160;_?_',
    '&#160;&#160;&#160;_*_',
    '&#160;&#160;&#160;_^_',
    '&#160;&#160;&#160;_$_',
    '&#160;&#160;&#160;[ ];'
  ];
  
  string[] private hatValue = [
    '',
    '_|_',
    '( )',
    '_-_',
    '_!_',
    '_?_',
    '_*_',
    '_^_',
    '_$_',
    '[ ]'
  ];

  string[] private hair = [
    '',
    '&#160;&#160;&#160;|||',
    '&#160;&#160;&#160;///',
    '&#160;&#160;&#160;...',
    '&#160;&#160;&#160;---',
    '&#160;&#160;&#160;+++',
    '&#160;&#160;&#160;^^^',
    '&#160;&#160;&#160;:::'
  ];
  
  string[] private hairValue = [
    '',
    '|||',
    '///',
    '...',
    '---',
    '+++',
    '^^^',
    ':::'
  ];
  
    string[] private eyes = [
    '',
    '&#160;&#160;&#160;+ +',
    '&#160;&#160;&#160;. .',
    '&#160;&#160;&#160;$ $',
    '&#160;&#160;&#160;@ @',
    '&#160;&#160;&#160;o o',
    '&#160;&#160;&#160;O o',
    '&#160;&#160;&#160;o O',
    '&#160;&#160;&#160;o <',
    '&#160;&#160;&#160;> o',
    '&#160;&#160;&#160;> <',
    '&#160;&#160;&#160;o -',
    '&#160;&#160;&#160;- o',
    '&#160;&#160;&#160;* *'
  ];

  string[] private eyesValue = [
    'None',
    '+ +',
    '. .',
    '$ $',
    '@ @',
    'o o',
    'O o',
    'o O',
    'o <',
    '> o',
    '> <',
    'o -',
    '- o',
    '* *'
  ];

  string[] private mouth = [
    '',
    '&#160;&#160;&#160;(-)',
    '&#160;&#160;&#160;(_)',
    '&#160;&#160;&#160;{_}',
    '&#160;&#160;&#160;{-}',
    '&#160;&#160;&#160;&#160;-',
    '&#160;&#160;&#160;(o)',
    '&#160;&#160;&#160;{x}'
  ];
  
  string[] private mouthValue = [
    'None',
    '(-)',
    '(_)',
    '{_}',
    '{-}',
    '-',
    '(o)',
    '{x}'
  ];

  string[] private neck = [
    '',
    '&#160;&#160;&#160;_|_',
    '&#160;&#160;&#160;_(_',
    '&#160;&#160;&#160;_)_',
    '&#160;&#160;&#160;_[_',
    '&#160;&#160;&#160;_]_',
    '&#160;&#160;&#160;_!_',
    '&#160;&#160;&#160;_H_'
  ];
  
  string[] private neckValue = [
    'None',
    '_|_',
    '_(_',
    '_)_',
    '_[_',
    '_]_',
    '_!_',
    '_H_'
  ];

  string[] private body = [
    '',
    '&#160;&#160;/&#160;|&#160;|',
    '&#160;&#160;/&#160;.&#160;!',
    '&#160;&#160;/&#160;H&#160;i',
    '&#160;&#160;/&#160;D&#160;]',
    '&#160;&#160;/&#160;B&#160;)',
    '&#160;&#160;/&#160;(&#160;[',
    '&#160;&#160;/&#160;[&#160;(',
    '&#160;&#160;/&#160;-&#160;}',
    '&#160;&#160;/&#160;$&#160;{',
    '&#160;&#160;/&#160;]&#160;[',
    '&#160;&#160;/&#160;!&#160;['
  ];
  
    string[] private bodyValue = [
    'None',
    '/ | |',
    '/ . !',
    '/ H i',
    '/ D ]',
    '/ B )',
    '/ ( [',
    '/ [ (',
    '/ - }',
    '/ $ {',
    '/ O |',
    '/ ! |'
  ];

  string[] private legs = [
    '',
    '&#160;&#160;&#160;/ |',
    '&#160;&#160;&#160;] ]',
    '&#160;&#160;&#160;) )',
    '&#160;&#160;&#160;] )',
    '&#160;&#160;&#160;| |',
    '&#160;&#160;&#160;} }',
    '&#160;&#160;&#160;! !;'
  ];
  
  string[] private legsValue = [
    'None',
    '/ |',
    '] ]',
    ') )',
    '] )',
    '| |',
    '} }',
    '! !;'
  ];

  string[] feet = [
    '',
    '&#160;&#160;&#160;- -',
    '&#160;&#160;&#160;< <',
    '&#160;&#160;&#160;> >',
    '&#160;&#160;&#160;^ ^',
    '&#160;&#160;&#160;d b',
    '&#160;&#160;&#160;* *',
    '&#160;&#160;&#160;$ $'
  ];
  
  string[] feetValue = [
    'None',
    '- -',
    '< <',
    '> >',
    '^ ^',
    'd b',
    '* *',
    '$ $'
  ];

  // mirror a dice roll
  function random(string memory input) internal pure returns (uint256) {
    return (uint256(keccak256(abi.encodePacked(input))) % 6) + 1;
  }

  function getHat(uint256 tokenId) public view returns (string memory, string memory) {
    return pluck(tokenId, 'hat', hat, hatValue);
  }

  function getHair(uint256 tokenId) public view returns (string memory, string memory) {
    return pluck(tokenId, 'hair', hair, hairValue);
  }

  function getEyes(uint256 tokenId) public view returns (string memory, string memory) {
    return pluck(tokenId, 'eyes', eyes, eyesValue);
  }

  function getMouth(uint256 tokenId) public view returns (string memory, string memory) {
    return pluck(tokenId, 'mouth', mouth, mouthValue);
  }

  function getNeck(uint256 tokenId) public view returns (string memory, string memory) {
    return pluck(tokenId, 'neck', neck, neckValue);
  }

  function getBody(uint256 tokenId) public view returns (string memory, string memory) {
    return pluck(tokenId, 'body', body, bodyValue);
  }

  function getLegs(uint256 tokenId) public view returns (string memory, string memory) {
    return pluck(tokenId, 'legs', legs, legsValue);
  }

  function getFeet(uint256 tokenId) public view returns (string memory, string memory) {
    return pluck(tokenId, 'feet', feet, feetValue);
  }

  function pluck(
    uint256 tokenId,
    string memory keyPrefix,
    string[] memory sourceArray,
    string[] memory sourceArrayValue
  ) internal pure returns (string memory, string memory) {
    uint256 rand = random(
      string(abi.encodePacked(keyPrefix, toString(tokenId)))
    );
    string memory output = sourceArray[rand % sourceArray.length];
    string memory value = sourceArrayValue[rand % sourceArray.length];
    return (output, value);
  }

  function tokenURI(uint256 tokenId)
    public
    view
    override
    returns (string memory)
  {
    string[17] memory parts;
    string[9] memory values;
    parts[
      0
    ] = '<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 350 350"><style>.base { fill: black; font-family: serif; font-size: 14px; }</style><rect width="100%" height="100%" fill="#01ff01" /><text x="10" y="20" font-family="monospace">';

    (parts[1], values[1]) = getHat(tokenId);

    parts[2] = '</text><text x="10" y="40" font-family="monospace">';

    (parts[3], values[2]) = getHair(tokenId);

    parts[4] = '</text><text x="10" y="60" font-family="monospace">';

    (parts[5], values[3]) = getEyes(tokenId);

    parts[6] = '</text><text x="10" y="80" font-family="monospace">';

    (parts[7] , values[4]) = getMouth(tokenId);

    parts[8] = '</text><text x="10" y="100" font-family="monospace">';

    (parts[9], values[5]) = getNeck(tokenId);

    parts[10] = '</text><text x="10" y="120" font-family="monospace">';

    (parts[11], values[6]) = getBody(tokenId);

    parts[12] = '</text><text x="10" y="140" font-family="monospace">';

    (parts[13], values[7]) = getLegs(tokenId);

    parts[14] = '</text><text x="10" y="160" font-family="monospace">';

    (parts[15], values[8]) = getFeet(tokenId);

    parts[16] = '</text></svg>';

    string memory output = string(
      abi.encodePacked(
        parts[0],
        parts[1],
        parts[2],
        parts[3],
        parts[4],
        parts[5],
        parts[6],
        parts[7],
        parts[8]
      )
    );
    output = string(
      abi.encodePacked(output, parts[9], parts[10], parts[11], parts[12])
    );
    output = string(
      abi.encodePacked(output, parts[13], parts[14], parts[15], parts[16])
    );
    
   string memory attributes = string(abi.encodePacked("\"attributes\": [" ));
   attributes = string(abi.encodePacked(attributes, "{\"trait_type\": \"Hat\", \"value\": \"", values[1], "\"}" ));
   attributes = string(abi.encodePacked(attributes, ",{\"trait_type\": \"Hair\", \"value\": \"", values[2], "\"}" ));
   attributes = string(abi.encodePacked(attributes, ",{\"trait_type\": \"Eyes\", \"value\": \"", values[3], "\"}" ));
   attributes = string(abi.encodePacked(attributes, ",{\"trait_type\": \"Mouth\", \"value\": \"", values[4], "\"}" ));
   attributes = string(abi.encodePacked(attributes, ",{\"trait_type\": \"Neck\", \"value\": \"", values[5], "\"}" ));
   attributes = string(abi.encodePacked(attributes, ",{\"trait_type\": \"Body\", \"value\": \"", values[6], "\"}" ));
   attributes = string(abi.encodePacked(attributes, ",{\"trait_type\": \"Legs\", \"value\": \"", values[7], "\"}" ));
   attributes = string(abi.encodePacked(attributes, ",{\"trait_type\": \"Feet\", \"value\": \"", values[8], "\"}"));
   attributes = string(abi.encodePacked(attributes, "]"));
    
    string memory json = Base64.encode(
      bytes(
        string(
          abi.encodePacked(
            '{"name": "Blootvatar #',
            toString(tokenId),
            '", "description": "Blootvatars are randomized Bloot avatars generated and stored on chain. Feel free to use Blootvatars in any way you want.",', attributes ,',"image": "data:image/svg+xml;base64,',
            Base64.encode(bytes(output)),
            '"}'
          )
        )
      )
    );
    output = string(abi.encodePacked('data:application/json;base64,', json));

    return output;
  }

  function claim(uint256 tokenId) public nonReentrant {
    require(tokenId > 8008 && tokenId < 9576, 'Token ID invalid');
    _safeMint(_msgSender(), tokenId);
  }

  function claimForBloot(uint256 tokenId) public nonReentrant {
    require(tokenId > 0 && tokenId < 8009, 'Token ID invalid');
    require(bloot.ownerOf(tokenId) == msg.sender, 'Not Bloot owner');
    _safeMint(_msgSender(), tokenId);
  }

  function ownerClaim(uint256 tokenId) public nonReentrant onlyOwner {
    require(tokenId > 9575 && tokenId < 10001, 'Token ID invalid');
    _safeMint(owner(), tokenId);
  }

  function toString(uint256 value) internal pure returns (string memory) {
    // Inspired by OraclizeAPI's implementation - MIT license
    // https://github.com/oraclize/ethereum-api/blob/b42146b063c7d6ee1358846c198246239e9360e8/oraclizeAPI_0.4.25.sol

    if (value == 0) {
      return '0';
    }
    uint256 temp = value;
    uint256 digits;
    while (temp != 0) {
      digits++;
      temp /= 10;
    }
    bytes memory buffer = new bytes(digits);
    while (value != 0) {
      digits -= 1;
      buffer[digits] = bytes1(uint8(48 + uint256(value % 10)));
      value /= 10;
    }
    return string(buffer);
  }

  constructor() ERC721('Blootvatars', 'BAVA') Ownable() {
      _safeMint(owner(), 8009);
  }
}
function random(string memory input) internal pure returns (uint256) {
  return (uint256(keccak256(abi.encodePacked(input))) % 6) + 1;
}

// mirror a dice roll