contract Contract is ERC721("Metarobotica", "Metarobotica"), ERC721Enumerable, Ownable {
    using SafeMath for uint256;
    using Strings for uint256;
    /*
     * Currently Assuming there will be one baseURI.
     * If it fails to upload all NFTs data under one baseURI,
     * we will divide baseURI and tokenURI function will be changed accordingly.
    */
    string private baseURI;
    string private blindURI;
    uint256 private constant PHASE_1_LIMIT = 2222;
    uint256 private constant PHASE_2_3_LIMIT = 3333;
    uint256 private constant MAX_FREE_LIMIT = 100;
    uint256 public NFT1Price = 120000000000000000;  // 0.12 ETH
    uint256 public NFT2Price = 160000000000000000;  // 0.16 ETH
    uint256 public NFT3Price = 200000000000000000;  // 0.2 ETH
    bool public revealPhase1;
    bool public revealPhase2;
    bool public revealPhase3;
    bool public isActive;
    bool public isPublicSaleActive;
    bytes32 public ogRoot;
    bytes32 public freeRoot;
    bytes32 public wlRoot;
    uint256 public constant OG_MAX_MINT = 4;
    uint256 public constant FREE_MAX_MINT = 1;
    uint256 public constant WHITELIST_MAX_MINT = 2;
    uint256 public constant PUBLIC_MAX_MINT = 4;
    uint256 private freeClaimed;
    mapping(uint256 => mapping(address => uint256)) private claimed;
    mapping(uint256 => uint256) public phaseClaimed;
    uint256 private phase = 1;

    /*
     * Function to reveal NFTs
    */
    function setRevealPhase1(
        bool _reveal
    ) 
        external 
        onlyOwner 
    {
        revealPhase1 = _reveal;
    }

    /*
     * Function to reveal NFTs
    */
    function setRevealPhase2(
        bool _reveal
    ) 
        external 
        onlyOwner 
    {
        revealPhase2 = _reveal;
    }

    /*
     * Function to reveal NFTs
    */
    function setRevealPhase3(
        bool _reveal
    ) 
        external 
        onlyOwner 
    {
        revealPhase3 = _reveal;
    }
    
    /*
     * Function setIsActive to activate/desactivate the smart contract
    */
    function setIsActive(
        bool _isActive
    ) 
        external 
        onlyOwner 
    {
        isActive = _isActive;
    }

    /*
     * Function setIsPublicSaleActive to activate/desactivate public sale
    */
    function setIsPublicSaleActive(
        bool _isActive
    ) 
        external 
        onlyOwner 
    {
        isPublicSaleActive = _isActive;
    }

    function setRoot(uint256 _ogRoot, uint256 _freeRoot, uint256 _wlRoot) external onlyOwner() {
        ogRoot = bytes32(_ogRoot);
        freeRoot = bytes32(_freeRoot);
        wlRoot = bytes32(_wlRoot);
    }

    function setPhase(uint256 _phase) external onlyOwner()  {
        require(_phase >= 1 && _phase <= 3, 'Wrong phase');
        phase = _phase;
    }

    /*
     * Function to set Base and Blind URI 
    */
    function setURIs(
        string memory _blindURI, 
        string memory _URI
    ) 
        external 
        onlyOwner 
    {
        blindURI = _blindURI;
        baseURI = _URI;
    }
    
    /*
     * Function to withdraw collected amount during minting by the owner
    */
    function withdraw(
    ) 
        public 
        onlyOwner 
    {
        address ownerAddress = 0x74B6EE60C68E5AeB3aba65F99EE6112707866e96;
        payable(ownerAddress).transfer(address(this).balance);
    }


     /*
     * Function to mint new NFTs during the public sale
     * It is payable. Amount is calculated as per (NFTPrice.mul(_numOfTokens))
    */
    function mintNFT(
        uint256 _numOfTokens
    ) 
        public 
        payable 
    {
    
        require(isActive, 'Contract is not active');
        require(isPublicSaleActive, 'Public is not active');
        require(phaseClaimed[phase].add(_numOfTokens) <= _getPhaseLimit(), 'Tokens number to mint cannot exceed number of MAX tokens per phase');
        require(_numOfTokens <= PUBLIC_MAX_MINT, 'Cannot purchase this many tokens');
        require(claimed[phase][msg.sender].add(_numOfTokens) <= PUBLIC_MAX_MINT, 'Purchase exceeds max whiteed');
        require(_getPhasePrice().mul(_numOfTokens) == msg.value, "Ether value sent is not correct");
        
        for(uint i = 0; i < _numOfTokens; i++) {
                claimed[phase][msg.sender] += _numOfTokens;
                phaseClaimed[phase]+=1;
            _safeMint(msg.sender, totalSupply());
        }
    }
    
    /*
     * Function to mint new NFTs
     * It is payable. Amount is calculated as per (NFTPrice.mul(_numOfTokens))
    */ 
    function mintNFTOg(
        uint256 _numOfTokens,
        bytes32[] memory _proof
    ) 
        public 
        payable
    {
        require(isActive, 'Sale is not active');
        require(MerkleProof.verify(_proof, ogRoot, keccak256(abi.encodePacked(msg.sender))), "Not whitelisted");
        require(phaseClaimed[phase].add(_numOfTokens) <= _getPhaseLimit(), 'Tokens number to mint cannot exceed number of MAX tokens per phase');
        require(_numOfTokens <= OG_MAX_MINT, 'Cannot purchase this many tokens');
        require(claimed[phase][msg.sender].add(_numOfTokens) <= OG_MAX_MINT, 'Purchase exceeds max whiteed');
        require(_getPhasePrice().mul(_numOfTokens) == msg.value, "Ether value sent is not correct");
        for (uint256 i = 0; i < _numOfTokens; i++) {
                claimed[phase][msg.sender] += _numOfTokens;
                phaseClaimed[phase]+=1;
                _safeMint(msg.sender, totalSupply());
        }
    }

    /*
     * Function to mint new NFTs
     * It is payable. Amount is calculated as per (NFTPrice.mul(_numOfTokens))
    */ 
    function mintNFTWhitelist(
        uint256 _numOfTokens,
        bytes32[] memory _proof
    ) 
        public 
        payable
    {
        require(isActive, 'Sale is not active');
        require(MerkleProof.verify(_proof, wlRoot, keccak256(abi.encodePacked(msg.sender))), "Not whitelisted");
        require(phaseClaimed[phase].add(_numOfTokens) <= _getPhaseLimit(), 'Tokens number to mint cannot exceed number of MAX tokens per phase');
        require(_numOfTokens <= WHITELIST_MAX_MINT, 'Cannot purchase this many tokens');
        require(claimed[phase][msg.sender].add(_numOfTokens) <= WHITELIST_MAX_MINT, 'Purchase exceeds max whiteed');
        require(_getPhasePrice().mul(_numOfTokens) == msg.value, "Ether value sent is not correct");
        for (uint256 i = 0; i < _numOfTokens; i++) {
                claimed[phase][msg.sender] += _numOfTokens;
                phaseClaimed[phase]+=1;
                _safeMint(msg.sender, totalSupply());
        }
    }

    /*
     * Function to mint new NFTs
     * It is payable. Amount is calculated as per (NFTPrice.mul(_numOfTokens))
    */ 
    function mintNFTFree(
        uint256 _numOfTokens,
        bytes32[] memory _proof
    ) 
        public 
        payable
    {
        require(isActive, 'Sale is not active');
        require(MerkleProof.verify(_proof, freeRoot, keccak256(abi.encodePacked(msg.sender))), "Not whitelisted");
        require(freeClaimed < MAX_FREE_LIMIT, 'All free tokens have been minted');
        require(phaseClaimed[phase].add(_numOfTokens) <= _getPhaseLimit(), 'Tokens number to mint cannot exceed number of MAX tokens per phase');
        require(_numOfTokens <= FREE_MAX_MINT, 'Cannot purchase this many tokens');
        require(claimed[phase][msg.sender].add(_numOfTokens) <= FREE_MAX_MINT, 'Purchase exceeds max whiteed');
        for (uint256 i = 0; i < _numOfTokens; i++) {
                claimed[phase][msg.sender] += _numOfTokens;
                phaseClaimed[phase]+=1;
                freeClaimed+=1;
                _safeMint(msg.sender, totalSupply());
        }
    }

    /*
     * Function to mint any NFTs
    */
    function mintMultipleByOwner(
        address _to, 
        uint256 _numOfTokens
    )
        public
        onlyOwner
    {
        require(phaseClaimed[phase].add(_numOfTokens) <= _getPhaseLimit(), "Tokens number to mint cannot exceed number of MAX tokens per phase");
        for(uint256 i = 0; i < _numOfTokens; i++){
            claimed[phase][_to] += 1;
            phaseClaimed[phase]+=1;
            _safeMint(_to, totalSupply());
        }
    }

    /*
     * Function to get token URI of given token ID
    */
    function tokenURI(
        uint256 _tokenId
    )
        public 
        view 
        virtual 
        override 
        returns (string memory) 
    {
        require(_exists(_tokenId), "ERC721Metadata: URI query for nonexistent token");
        if (
            (_tokenId < PHASE_1_LIMIT && revealPhase1) ||
            (_tokenId < (PHASE_1_LIMIT + PHASE_2_3_LIMIT) && revealPhase2) ||
            revealPhase3
            ) {
            return string(abi.encodePacked(baseURI, _tokenId.toString()));
        } else {
            return string(abi.encodePacked(blindURI));
        }
    }
    
    function supportsInterface(
        bytes4 _interfaceId
    ) 
        public
        view 
        override (ERC721, ERC721Enumerable) 
        returns (bool) 
    {
        return super.supportsInterface(_interfaceId);
    }

    // Standard functions to be overridden 
    function _beforeTokenTransfer(
        address _from, 
        address _to, 
        uint256 _tokenId
    ) 
        internal 
        override(ERC721, ERC721Enumerable) 
    {
        super._beforeTokenTransfer(_from, _to, _tokenId);
    }

    function _getPhaseLimit() view private returns (uint256) {
        if (phase == 1) {
            return PHASE_1_LIMIT;
        } else if (phase == 2)  {
            return PHASE_2_3_LIMIT;
        } else if (phase == 3) {
            return PHASE_2_3_LIMIT;
        } else {
            return 0;
        }
    }

    function _getPhasePrice() view private returns (uint256) {
        if (phase == 1) {
            return NFT1Price;
        } else if (phase == 2)  {
            return NFT2Price;
        } else if (phase == 3) {
            return NFT3Price;
        } else {
            return 1;
        }
    }
}
function setIsPublicSaleActive(
    bool _isActive
) 
    external 
    onlyOwner 
{
    isPublicSaleActive = _isActive;
}

/*
 * Function setIsPublicSaleActive to activate/desactivate public sale
 */