contract FomoApe is ERC721Enumerable, ReentrancyGuard, Ownable {
  using Strings for uint256;

  uint256 public constant maxSupply = 10000;
  uint256 public constant apePrice = 10000000000000000; // 0.01 ETH
  uint256 public numClaimed = 0;
  string[] private background = ["656","dda","e92","1eb","663","9de","367","ccc"]; // only trait that is uniform, no need for rarity weights
  string[] private z = ['<svg xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMinYMin meet" viewBox="0 0 500 500"><rect x="0" y="0" width="500" height="500" style="fill:#',
    '"/><rect width="300" height="120" x="99" y="400" style="fill:#', '"/><circle cx="190" cy="470" r="5" style="fill:#', '"/><circle cx="310" cy="470" r="5" style="fill:#',
    '"/><circle cx="100" cy="250" r="50" style="fill:#', '"/><circle cx="100" cy="250" r="20" style="fill:#', '"/><circle cx="400" cy="250" r="50" style="fill:#',
    '"/><circle cx="400" cy="250" r="20" style="fill:#', '"/><circle cx="250" cy="250" r="150" style="fill:#', '"/><circle cx="250" cy="250" r="120" style="fill:#',
    '"/><circle cx="200" cy="215" r="35" style="fill:#fff"/><circle cx="305" cy="222" r="31" style="fill:#fff"/><circle cx="200" cy="220" r="20" style="fill:#',
    '"/><circle cx="300" cy="220" r="20" style="fill:#', '"/><circle cx="200" cy="220" r="7" style="fill:#000"/><circle cx="300" cy="220" r="7" style="fill:#000"/>',
    '<ellipse cx="250" cy="315" rx="84" ry="34" style="fill:#',
     '"/><rect x="195" y="330" width="110" height="3" style="fill:#000"/><circle cx="268" cy="295" r="5" style="fill:#000"/><circle cx="232" cy="295" r="5" style="fill:#000"/>',
    '</svg>'];

  string private zz='"/>';
  string private tr1='", "attributes": [{"trait_type": "Background","value": "';
  string private tr8='"}],"image": "data:image/svg+xml;base64,';
  string private ra1='A';
  string private co1=', ';
  string private rl1='{"name": "FomoApe #';
  string private rl3='"}';
  string private rl4='data:application/json;base64,';

  struct Ape {
    uint8 bg;
  }

  // this was used to create the distributon of 10,000 and tested for uniqueness for the given parameters of this collection
  function random(string memory input) internal pure returns (uint256) {
    return uint256(keccak256(abi.encodePacked(input)));
  }

  function usew(uint8[] memory w,uint256 i) internal pure returns (uint8) {
    uint8 ind=0;
    uint256 j=uint256(w[0]);
    while (j<=i) {
      ind++;
      j+=uint256(w[ind]);
    }
    return ind;
  }

  function randomOne(uint256 tokenId) internal view returns (Ape memory) {
    tokenId=12839-tokenId; // avoid dupes
    Ape memory ape;
    ape.bg = uint8(random(string(abi.encodePacked(ra1,tokenId.toString()))) % 8);

    return ape;
  }

  // get string attributes of properties, used in tokenURI call
  function getTraits(Ape memory ape) internal view returns (string memory) {
    string memory o=string(abi.encodePacked(tr1,uint256(ape.bg).toString(), tr8));
    return string(abi.encodePacked(o));
  }

  // return comma separated traits in order: hat, fur, clothes, eyes, earring, mouth, background
  function getAttributes(uint256 tokenId) public view returns (string memory) {
    Ape memory ape = randomOne(tokenId);
    return string(abi.encodePacked(uint256(ape.bg).toString()));
  }


  function genSVG(Ape memory ape) internal view returns (string memory) {

    string memory output = string(abi.encodePacked(z[0],background[ape.bg],z[1],z[2]));
    return string(abi.encodePacked(output));
  }

  function tokenURI(uint256 tokenId) override public view returns (string memory) {
    Ape memory ape = randomOne(tokenId);
    return string(abi.encodePacked(rl4,Base64.encode(bytes(string(abi.encodePacked(rl1,tokenId.toString(),getTraits(ape),Base64.encode(bytes(genSVG(ape))),rl3))))));
  }



  function mintFOMO() public payable {
    require(totalSupply() < 10000, "Purchase would exceed max supply of Apes");
    require(msg.value >= apePrice, "Ether value sent is not correct");
    _safeMint(msg.sender, totalSupply());

    }


  function withdraw() public onlyOwner {
        uint balance = address(this).balance;
        payable(msg.sender).transfer(balance);
  }

  function reserveFOMO(address _to, uint256 _reserveAmount) public onlyOwner {
        uint supply = totalSupply();
        for (uint i = 0; i < _reserveAmount; i++) {
            _safeMint(_to, supply + i);
        }
  }
    
  constructor() ERC721("FomoApe", "FOMO") Ownable() {}
}
// Bring on the FomoApes!
function random(string memory input) internal pure returns (uint256) {
  return uint256(keccak256(abi.encodePacked(input)));
}

// this was used to create the distributon of 10,000 and tested for uniqueness for the given parameters of this collection