contract DeadDino is ERC721, Ownable {
	using Strings for uint256;
	using SafeMath for uint256;

	address public constant burn = address(0);
	string public baseUri;
	
	uint256 public maxSupply = 500;
	uint256 public dinoCount;
	address public yieldBone;

	MutationAnimal public mnft;

	string[] public mutationBaseUri = [
		'ipfs://a/', 
		'ipfs://b/',
		'ipfs://c/',
		'ipfs://d/',
		'ipfs://e/',
		'ipfs://f/',
		'ipfs://g/',
		'ipfs://h/',
		'ipfs://i/',
		'ipfs://j/',
		'ipfs://k/',
		'ipfs://l/'
	];

	//------Events-----
	event DinoAscended(uint256 tokenId);
	event MutationDone(address mutator, uint256 id, uint256 checmicalId, uint256 time);

	//------Struct-----
	struct MutationDetails{
		uint256 mutationTypes; // mutation type 0,1,2,3 splicing
		uint256 mutationLevel; // mutation level 0,1,2,3 splicing
	}

	//------Variables----
	mapping (uint256 => bool) public mutationStatus;
	mapping (uint256 => MutationDetails) public mutationDetails;
	mapping (uint256 => bool) public mutation;
	mapping (address => bool) public genesisTrasnsferer;
 

	constructor(uint256 _currentSupply, string memory _baseUri, address _mutationNFT ) ERC721("DeadDino","DEAD DINO") {
		// _setBaseURI("ipfs://QmaGqAY21r2EGRsMb8VFbPyRbqcveroeL6rKsmtzVsqAtZ/");
		_setBaseURI(_baseUri);
		mnft = MutationAnimal(_mutationNFT);
		batchMintNFT(_currentSupply);
	}

	//-----Admin Function----
	///@notice update the uri for the token
	///@param newURI new uri
	function updateURI(string memory newURI) public onlyOwner {
		_setBaseURI(newURI);
	}

	///@notice set yield token 
	///@param _yield new yield bone token
	function setYieldToken(address _yield) external onlyOwner {
		yieldBone = _yield;
	}

	///@notice set base uri
	///@param _baseURI base uri for the nft
	function _setBaseURI(string memory _baseURI) internal {
			baseUri = _baseURI;
	}

	///@notice set mutation base uri 
	///@param _mutationBaseUris set mutation base uri
	function setmutationBaseURi(string[] memory _mutationBaseUris) external onlyOwner {
		mutationBaseUri = _mutationBaseUris;
	}


	///@notice mint nft
	function mintNFT() public onlyOwner {
		require(dinoCount <= maxSupply, "NFT: Cannot mint more than total Supply!");
		dinoCount++;
		uint256 _tokenId = dinoCount;
		_mint(msg.sender, _tokenId);
	}

	///@notice batch mint nft
	///@param totalNumber nft number to mint 
	function batchMintNFT(uint256 totalNumber) public onlyOwner {
		for(uint256 i=0;i < totalNumber; i++){
			mintNFT();
		}
	}

	///@notice add the genisis trader like marketplace or other first distributor addresses
	///@param genesisTrader genesis trader address
	function addGenesisTrader(address genesisTrader) external onlyOwner{
		genesisTrasnsferer[genesisTrader] = true;
	}

	///@notice remoce the genesis trader
	///@param genesisTrader genisis trader address
	function removeGenesisTrader(address genesisTrader) external onlyOwner{
		genesisTrasnsferer[genesisTrader] = false;
	}

	///@notice animal mutation nft update 
	///@param _mnft mutation nft address
	function updateMNFT(address _mnft) external onlyOwner{
		mnft = MutationAnimal(_mnft);
	}

	//-----View Function----

	function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
		require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
		string memory currentUri = baseUri;
		if(mutationStatus[tokenId]){
			MutationDetails memory details = mutationDetails[tokenId];
			uint256 mutationType = details.mutationTypes;
			uint256 mutationLevel = details.mutationLevel;
			uint256 tokenUriIndex = mutationLevel.mul(4).add(mutationType);
			currentUri = mutationBaseUri[tokenUriIndex];
		}
		return bytes(baseUri).length > 0 ? string(abi.encodePacked(currentUri, tokenId.toString(), ".json")) : "";
	}


	//-----User function----
	///@notice function called to get the reward bones
	function getReward() external{
		IBone20(yieldBone).updateReward(msg.sender, address(0));
		IBone20(yieldBone).getReward(msg.sender);
	}

	///@notice function called to get the 
	///@param nftId nft id to mutate
	///@param animalId id for animal to mutate with
	function mutateNFT(uint256 nftId, uint256 animalId) public {
		if(!mutationStatus[nftId]) 
			mutationStatus[nftId] = true;

		mnft.burnSerumForAddress(animalId, msg.sender);
		uint256 levels = mnft.getChemicalLevel(animalId);
		uint256 types = mnft.getChemicalType(animalId);
		mutationDetails[nftId].mutationLevel = levels;
		mutationDetails[nftId].mutationTypes = types;

		emit MutationDone(msg.sender, nftId, animalId, block.timestamp);
	}

	//---Override----
	function transferFrom(address from, address to, uint256 tokenId) public override {
		//sender was the owner 
		//it replicats that the NFT was sold from the Opensea / Owner to the user now it will be rewarded
		if(from == owner() || genesisTrasnsferer[from]){
			IBone20(yieldBone).updateRewardOnMint(to);	
			emit DinoAscended(tokenId);	
		}else{
			IBone20(yieldBone).updateReward(from, to);
		}
		_transfer(from, to, tokenId);
	}

	function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public override {
    //sender was the owner 
    //it means that the NFT was sold from the Opensea / Owner to the user now it will be rewarded
    if(from == owner() || genesisTrasnsferer[from]){
      IBone20(yieldBone).updateRewardOnMint(to);
      emit DinoAscended(tokenId);	
    }else{
      IBone20(yieldBone).updateReward(from, to);
    }
    _safeTransfer(from, to, tokenId, _data);	
  }

}
function setmutationBaseURi(string[] memory _mutationBaseUris) external onlyOwner {
		mutationBaseUri = _mutationBaseUris;
	}
///@notice set mutation base uri 
///@param _mutationBaseUris set mutation base uri