contract DraGONS is ERC721Enumerable, Ownable, RandomlyAssigned {

  GONS private gons;
  uint constant public MAX_DRAGONS_MINT = 5000;
  uint constant public MAX_DRAGONS_AIRDROP = 904; 
  uint256 public constant dragonPrice = 70000000000000000; //0.07 ETH

  address private gonsContract = 0x984EEA281Bf65638ac6ed30C4FF7977EA7fe0433; // GONS SC
  bool public saleIsActive = false;
  bool public airdropIsActive = false;
  uint256 public maxDragons;
  string private baseURI;

  uint256 public totalDragonsMinted = 0;
  uint256 public totalDragonsAirdropped = 0; 

  mapping(uint256 => uint256) public mintedIDs;

  constructor() ERC721("DraGONS 2171", "DRAGONS") RandomlyAssigned (MAX_DRAGONS_MINT+MAX_DRAGONS_AIRDROP,0) {
    maxDragons = MAX_DRAGONS_MINT;
    gons = GONS(gonsContract);
  }

  function _baseURI() internal view override returns (string memory) {
    return baseURI;
  }
  
  function setBaseURI(string memory uri) public onlyOwner {
    baseURI = uri;
  }

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

  function flipAirdropState() public onlyOwner {
    airdropIsActive = !airdropIsActive;
  }

  // Shows if a GON can mint a DraGON
  function canMint(uint256 tokenId) view public returns (bool) {
        bool res = true;
        if (mintedIDs[tokenId] == 1){
            res = false;
        }
      return res;
  }

  // Returns the amount of DraGONS available to be minted
  function getMintableTokenNumber(address user) view public returns (uint256 mintableTokenNumber) {
      mintableTokenNumber = 0;
      for (uint256 i = 0; i < gons.balanceOf(user); i++) {
          uint256 tokenId = gons.tokenOfOwnerByIndex(user, i);
          if (canMint(tokenId))
              mintableTokenNumber++;
      }
      return mintableTokenNumber;
  }


  function getAvailableIDs() public view returns (uint256[] memory) {
      uint256[] memory availableIDs = new uint256[](MAX_DRAGONS_MINT);
      for (uint256 i = 0; i < MAX_DRAGONS_MINT; i++) {
         if (mintedIDs[i] != 1){
            availableIDs[i] = i;
         } else {
            availableIDs[i] = MAX_DRAGONS_MINT; 
         }
        
      }
      return availableIDs;
  }

  /**
    * Mint a DraGON
    */
  function mintDragon(uint256 gonsTokenId) public payable {
    require(saleIsActive, "The sale must be active to mint a DraGON");
    require(totalDragonsMinted < maxDragons, "The purchase would exceed the max supply of DraGONS");
    require(gonsTokenId < maxDragons, "The requested tokenId exceeds upper bound");
    require(gons.ownerOf(gonsTokenId) == msg.sender, "Must own the specific Gorilla Nemesis with the requested tokenId to mint this DraGON");
    require(dragonPrice <= msg.value, "Ether value sent is not correct");
    require(canMint(gonsTokenId), "DraGON already minted");

    uint256 mintIndex = nextToken();
    _safeMint(msg.sender, mintIndex);
     mintedIDs[gonsTokenId] = 1;
    totalDragonsMinted++;
  }

  /**
    * Mint N DraGONS
  */
  function mintNDragons(uint256 numDragons) public payable {
      require(saleIsActive, "The sale must be active to mint a DraGON");
      require(numDragons > 0, "Must mint at least one DraGON");
      require(totalDragonsMinted + numDragons <= MAX_DRAGONS_MINT, "The purchase would exceed the max supply of DraGONS");
      uint balanceGons = gons.balanceOf(msg.sender);
      require(balanceGons >= numDragons, "Must hold N Gorilla Nemesis to mint N DraGONS");
      uint256 toBeMinted = getMintableTokenNumber(msg.sender);
      require(toBeMinted > 0, "You must have at least one Gorilla Nemesis elegible to mint");
      if (toBeMinted > numDragons)
      {
          toBeMinted = numDragons;
      }
      require(dragonPrice * toBeMinted <= msg.value, "Ether value sent is not correct");

      uint256 minted = 0;
      for (uint256 i = 0; i < balanceGons; i++) {
          uint256 tokenId = gons.tokenOfOwnerByIndex(msg.sender, i);

          if (canMint(tokenId)) {
              uint256 mintIndex = nextToken();
              _safeMint(msg.sender, mintIndex);
              mintedIDs[tokenId] = 1;
              minted++;
              totalDragonsMinted++;
          }
          if (minted == toBeMinted) {
              break;
          }
      }
  }

    /**
     * Airdrop-phase: DraGONS assigned to MK owners [1 : 1] (500) + GONS Character owners [1 : 1] (404)
     * We take care of the transaction fees.
     */
    function airdropDragons(address to, uint count) public onlyOwner{
        require(airdropIsActive, "Airdrop-phase must be active to airdrop a DraGON");
        require(totalDragonsAirdropped + count <= MAX_DRAGONS_AIRDROP, "The number of dragons would exceed the maximum airdrop supply");

        for(uint i = 0; i < count; i++) {
            uint mintIndex = nextToken();
            _safeMint(to, mintIndex);
            totalDragonsAirdropped++;
        } 
    }

  /*
  * Withdraw from the contract
  */
 function withdrawAll() public payable onlyOwner {
        require(payable(msg.sender).send(address(this).balance));
    }

}
function getMintableTokenNumber(address user) view public returns (uint256 mintableTokenNumber) {
      mintableTokenNumber = 0;
      for (uint256 i = 0; i < gons.balanceOf(user); i++) {
          uint256 tokenId = gons.tokenOfOwnerByIndex(user, i);
          if (canMint(tokenId))
              mintableTokenNumber++;
      }
      return mintableTokenNumber;
  }
// Returns the amount of DraGONS available to be minted