contract Nobody is ERC721Enumerable, TraitsofNobody, Ownable {
  using SafeMath for uint256;
  using SafeMath for uint8;
  using SafeMathUint for uint256;

  uint public maxNobody = 3210;
  uint public maxNobodyPerPurchase = 10;
  uint256 public price = 30000000000000000; // 0.030 Ether

  bool public isSaleActive = false;
  bool public isClaimActive = false;
  string public baseURI;
  
  mapping(uint256 => bool) public claimedTokenIds; //the Dudes tokenId
  mapping (uint => string) public nobody;

  address public thedudesaddress = 0xB0cf7Da8dc482997525BE8488B9caD4F44315422; 

  theDudes thedudescontract = theDudes(thedudesaddress);  
    
  address public creator = 0x7ddD43C63aa73CDE4c5aa6b5De5D9681882D88f8; 

  constructor (uint _maxNobody, uint _maxNobodyPerPurchase ) ERC721("nobody", "nobody") {
    maxNobody = _maxNobody;
    maxNobodyPerPurchase = _maxNobodyPerPurchase;
    _mint(creator, 1, 385906);
  }

  function claimAll(address _owner) public {
    require(isClaimActive, "Claim is not active yet.");
    require(!allClaimed(_owner), "All your tokens are claimed.");
    int256[] memory tokenIds = claimableOf(_owner);
    for (uint256 i = 0; i < tokenIds.length; i++) {
      if (tokenIds[i] != -1) {
        claim(uint256(tokenIds[i]));
      }
    }
  }

  function claim(uint256 _tokenId) public {
    require(isClaimActive, "Claim is not active yet.");
    require(!claimedTokenIds[_tokenId], "This token is already minted.");
    require(thedudescontract.ownerOf(_tokenId) == msg.sender, "Not the owner of this dudes.");
    string memory thedudesdna = thedudescontract.dudes(_tokenId);
    claimedTokenIds[_tokenId] = true;
    _claim(msg.sender, thedudesdna);
  }

  function _claim(address _to, string memory thedudesdna) internal {
      uint256 mintIndex = totalSupply();
          if (totalSupply() < maxNobody) {
                string memory nobodyid = string(abi.encodePacked(thedudesdna , _substring(thedudesdna,0,1)));
                nobody[mintIndex] = nobodyid;
                //foundDNAs[nobodyid] = true;
                _safeMint(_to, mintIndex);
            }
  }

 function claimableOf(address _owner) public view returns (int256[] memory) {
    uint256[] memory tokenIds = thedudescontract.tokensOfOwner(_owner);
    int256[] memory claimableTokenIds = new int256[](tokenIds.length);
    uint256 index = 0;
    for (uint256 i = 0; i < tokenIds.length; i++) {
      uint256 tokenId = tokenIds[i];
      claimableTokenIds[i] = -1;
      if (thedudescontract.ownerOf(tokenId) == _owner) {
        if (!claimedTokenIds[tokenId]) {
          claimableTokenIds[index] = tokenId.toInt256Safe();
          index++;
        }
      }
    }
    return claimableTokenIds;
  }

  function allClaimed(address _owner) public view returns (bool) {
    int256[] memory tokenIds = claimableOf(_owner);
    bool allClaimed = true;
    for (uint256 i = 0; i < tokenIds.length; i++) {
      if (tokenIds[i] != -1) {
        allClaimed = false;
      }
    }
    return allClaimed;
  }

  function mint(uint256 _numNobody, uint _salt) public payable {
    require(isSaleActive, "Sale is not active!" );
    require(_numNobody > 0 && _numNobody <= maxNobodyPerPurchase, 'Max is 10 at a time');
    require(totalSupply().add(_numNobody) <= maxNobody, "Sorry too many nobody!");
    require(msg.value >= price.mul(_numNobody), "Ether value sent is not correct!");

    _mint(msg.sender, _numNobody, _salt);
  }

  function tokensOfOwner(address _owner) public view returns (uint256[] memory) {
    uint256 tokenCount = balanceOf(_owner);
    if (tokenCount == 0) {
      return new uint256[](0);
    } else {
      uint256[] memory result = new uint256[](tokenCount);
      uint256 index;
      for (index = 0; index < tokenCount; index++) {
        result[index] = tokenOfOwnerByIndex(_owner, index);
      }
      return result;
    }
  }

  function tokenURI(uint256 _tokenId) public view virtual override returns (string memory) {
    require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
    return string(abi.encodePacked(_baseURI(), "/", nobody[_tokenId]));
  }
  
  function _mint(address _to, uint256 _numNobody, uint _salt) internal {
    for (uint256 i = 0; i < _numNobody; i++) {
      uint256 mintIndex = totalSupply();

          if (totalSupply() < maxNobody) {
                string memory nobodyid = _getNobody(_salt * i);
                nobody[mintIndex] = nobodyid;
                _safeMint(_to, mintIndex);
            }
    }
  }

  function _baseURI() internal view virtual override returns (string memory) {
    return baseURI;
  }

  //owner only

  function flipSaleState() public onlyOwner {
        isSaleActive = !isSaleActive;
  }

  function flipClaimState() public onlyOwner {
        isClaimActive = !isClaimActive;
  }

  function setPrice(uint256 _newPrice) public onlyOwner {
      price = _newPrice;
  }
  
  function setBaseURI(string memory _newBaseURI) public onlyOwner {
      baseURI = _newBaseURI;
  }

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

  function reserveNobody(uint256 _numNobody, uint256 _salt) public onlyOwner {        
        _mint(msg.sender, _numNobody, _salt);
  }
  
  function reservetheudes() public onlyOwner{
    for (uint256 i = 0; i < 512; i++) {
      string memory nobodyid = thedudescontract.dudes(i);
      if (foundDNAs[nobodyid] == false) {
          foundDNAs[nobodyid] = true;
        }
      }
  }


}
function flipSaleState() public onlyOwner {
      isSaleActive = !isSaleActive;
}

//owner only