contract OnChainEightBall is ERC721A, Ownable {
  using Strings for uint256;
  string public trait_name = "Sentiment";
  string[] public positive_sentminet = [
          "It is certain.",
          "It is decidedly so.",
          "Without a doubt.",
          "Yes definitely.",
          "You may rely on it.",

          "As I see it, yes.",
          "Most likely.",
          "Outlook good.",
          "Yes.", "GMI", "WAGMI",
          "Signs point to yes."];

  string[] public negative_sentiment = ["Don't count on it.",
          "My reply is no.",
          "My sources say no.",
          "Outlook not so good.", 
          "Very doubtful.", "NGMI"];
  string[] public neutral_sentiment = ["Reply hazy, try again.",
          "Ask again later.",
          "Better not tell you now.",
          "Cannot predict now.",
          "Concentrate and ask again"];

  uint256 sentiments = (positive_sentminet.length + negative_sentiment.length + neutral_sentiment.length);

  mapping(uint256 => uint256) pings;
    
  constructor() ERC721A("On Chain 8-Ball", "BALLS", 500, 10000) {}

  // public
  function mint(uint256 _count) public payable {
    uint256 supply = totalSupply();
    require(supply + _count <= 10000);

    if (msg.sender != owner()) {
      require(msg.value >= (0.005 ether) * _count);
    }
    
    _safeMint(msg.sender, _count);
  }
  
  function buildImage(uint256 _tokenId) public view returns(string memory) {
      TokenOwnership memory tokenMinter = super.minterOf(_tokenId);
      string memory bgHue = ((uint256(keccak256(abi.encodePacked(tokenMinter.addr, tokenMinter.startTimestamp, pings[_tokenId], "bgHue", _tokenId)))) % 361).toString();
      string memory fgHue = ((uint256(keccak256(abi.encodePacked(tokenMinter.addr, tokenMinter.startTimestamp, pings[_tokenId], "fgHue", _tokenId)))) % 361).toString();
      uint256 sentimentIdx = ((uint256(keccak256(abi.encodePacked(tokenMinter.addr, tokenMinter.startTimestamp, pings[_tokenId], "sentiment", _tokenId)))) % sentiments);
      string memory text;
      if (sentimentIdx < positive_sentminet.length) {
        text = positive_sentminet[sentimentIdx];
      } else if ((sentimentIdx - positive_sentminet.length) < negative_sentiment.length) {
        text = negative_sentiment[(sentimentIdx - positive_sentminet.length) ];
      } else {
        text = neutral_sentiment[sentimentIdx - positive_sentminet.length - negative_sentiment.length];
      }
      return Base64.encode(bytes(
          abi.encodePacked(
              '<svg width="500" height="500" xmlns="http://www.w3.org/2000/svg">',
              '<rect height="500" width="500" fill="hsl(',bgHue,', 50%, 25%)"/>',
              '<text x="50%" y="50%" dominant-baseline="middle" fill="hsl(',fgHue,', 100%, 80%)" text-anchor="middle" font-size="41">',text,'</text>',
              '</svg>'
          )
      ));
  }
  
  function buildMetadata(uint256 _tokenId) public view returns(string memory) {
      return string(abi.encodePacked(
              'data:application/json;base64,', Base64.encode(bytes(abi.encodePacked(
                          '{"name": "* Your Very Own Magic 8 ball *"', 
                          ', "description":"', 
                          "Some cool balls. See our website to shake.",
                          '", "image": "', 
                          'data:image/svg+xml;base64,', 
                          buildImage(_tokenId),
                          '"}')))));
  }


  event EightBallShaken(address _from, uint256 _tokenId, string question, string response);

  function shake8ball(string memory question, uint256 _tokenId) public payable returns (string memory){
    require(msg.sender == ownerOf(_tokenId), "not owner");

    pings[_tokenId] += 1;
    uint256 sentimentIdx = (uint256(keccak256(abi.encodePacked(super.minterOf(_tokenId).addr, super.minterOf(_tokenId).startTimestamp, "sentiment")))) % sentiments;
    string memory response;
    if (sentimentIdx < positive_sentminet.length) {
      response = positive_sentminet[sentimentIdx];
    } else if ((sentimentIdx - positive_sentminet.length) < negative_sentiment.length) {
      response = negative_sentiment[(sentimentIdx - positive_sentminet.length) ];
    } else {
      response = neutral_sentiment[sentimentIdx - positive_sentminet.length - negative_sentiment.length];
    }
    emit EightBallShaken(msg.sender, _tokenId, question, response);
    return response;
  }

  function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) {
      require(_exists(_tokenId),"ERC721Metadata: URI query for nonexistent token");
      return buildMetadata(_tokenId);
  }

  receive() external payable {

  }
  function withdraw() public payable onlyOwner {
    // =============================================================================
    (bool os, ) = payable(owner()).call{value: address(this).balance}("");
    require(os);
    // =============================================================================
  }
}
function mint(uint256 _count) public payable {
  uint256 supply = totalSupply();
  require(supply + _count <= 10000);

  if (msg.sender != owner()) {
    require(msg.value >= (0.005 ether) * _count);
  }
    
  _safeMint(msg.sender, _count);
}

// public