contract MutationAnimal is ERC1155, Ownable {
    using Strings for uint256;

    //----Event----
    event SetBaseURI(string indexed _baseURI);

    //----Struct----
    struct MutationDetail{
        uint256 animalLevel;
        uint256 animalType; 
    }

    //-----Variable-----
    string private _name;
    string private _symbol;

    address public dinoContract;
    string private baseURI;

    mapping(uint256 => bool) public validAnimalTypes;
    mapping (uint256 => MutationDetail) public mutationDetails;

    constructor(
        string memory _mutantName, 
        string memory _mutantSymbol, 
        string memory _baseURI
    )   ERC1155(_baseURI) {
        _name = _mutantName;
        _symbol = _mutantSymbol;
        baseURI = _baseURI;
        validAnimalTypes[0] = true;
        validAnimalTypes[1] = true;
        validAnimalTypes[2] = true;
        emit SetBaseURI(baseURI);
    }

    //------Admin-----
    ///@notice mint the nft in batch
    ///@param ids: ids tokens id to mint
    ///@param amounts: amounts of tokens to mint
    ///@param levels: level of tokens to mint
    ///@param types: types of tokens to mint 
    function mintBatch(uint256[] memory ids, uint256[] memory amounts, uint256[] memory levels, uint256[] memory types)
    external
    onlyOwner
    {
        _mintBatch(owner(), ids, amounts, "");
        for(uint256 i=0; i<ids.length; i++){
            MutationDetail memory currentMutationDetail = MutationDetail({
                animalType: types[i],
                animalLevel: levels[i]
            });
            mutationDetails[ids[i]] = currentMutationDetail;
        }
    }

    ///@notice add valid mutation animal types
    ///@param ids ids to add 
    function addValidMutationAnimals(uint256[] memory ids) 
    external 
    onlyOwner
    {
        for(uint256 i=0; i<ids.length; i++){
            validAnimalTypes[ids[i]] = true;
        }
    }

    ///@notice remove valid mutation animal types
    ///@param ids ids to remove
    function removeValidMutationAnimals(uint256[] memory ids) 
    external 
    onlyOwner
    {
        for(uint256 i=0; i<ids.length; i++){
            validAnimalTypes[ids[i]] = false;
        }
    }

    ///@notice set the dino contract address 
    ///@param dinoContractAddress contract address to set for the dino
    function setdinoContractAddress(address dinoContractAddress)
    external
    onlyOwner
    {
        dinoContract = dinoContractAddress;
    }
    
    ///@notice update base uri for the nft 
    ///@param _baseURI base uri for the nft
    function updateBaseUri(string memory _baseURI) external onlyOwner {
        baseURI = _baseURI;
        emit SetBaseURI(baseURI);
    }

    //---User-----
    ///@notice called by the dino contract to burn the mutant animal
    ///@param typeId type of animal to burn i.e. animal nft id
    ///@param burnTokenAddress token mutator
    function burnSerumForAddress(uint256 typeId, address burnTokenAddress)
    external
    {   
        require(msg.sender == dinoContract, "AnimalNFT: Cannot mutate other than Dino!");
        require(validAnimalTypes[typeId], "AnimalNFT: Cannot mutate from non-valid serum!");
        _burn(burnTokenAddress, typeId, 1);
    }

    //---View---
    function getChemicalLevel(uint256 _id) external view returns(uint256){
        return mutationDetails[_id].animalLevel;
    }

    function getChemicalType(uint256 _id) external view returns(uint256){
        return mutationDetails[_id].animalType;
    }

    function uri(uint256 typeId)
        public
        view                
        override
        returns (string memory)
    {
        require(
            validAnimalTypes[typeId],
            "URI requested for invalid serum type"
        );
        return
            bytes(baseURI).length > 0
                ? string(abi.encodePacked(baseURI, typeId.toString(), '.json'))
                : baseURI;
    }
}
function burnSerumForAddress(uint256 typeId, address burnTokenAddress)
external
{   
    require(msg.sender == dinoContract, "AnimalNFT: Cannot mutate other than Dino!");
    require(validAnimalTypes[typeId], "AnimalNFT: Cannot mutate from non-valid serum!");
    _burn(burnTokenAddress, typeId, 1);
}

///@notice called by the dino contract to burn the mutant animal
///@param typeId type of animal to burn i.e. animal nft id
///@param burnTokenAddress token mutator