contract MaestroPupsNFT is ERC721, Ownable {
    
    using SafeMath for uint256;

    uint256 public constant MAX_PUPS = 2999;  //3000 when including tokenID = 0
    uint256 public constant PupCost = 20000000000000000; // 0.02 ETH
    uint public constant maxPupPurchase = 10;

    string public PROVENANCE = "";     
    string public LICENSE_TEXT = ""; 

    bool LOCK_LICENSE = false;
    bool public SALE_ACTIVE = false;

    
    
    mapping(uint => string) public PupNames;
    

    function mintPup(uint numberOfTokens) public payable {
        require(SALE_ACTIVE, "Sale must be active to mint a Pup");
        require(numberOfTokens > 0 && numberOfTokens <= maxPupPurchase, "Can only mint 10 tokens at a time");
        require(totalSupply().add(numberOfTokens) <= MAX_PUPS, "Purchase would exceed max supply of Pups");
        require(msg.value >= PupCost.mul(numberOfTokens), "Ether value sent is not correct");
        
        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_PUPS) {
                _safeMint(msg.sender, mintIndex);
            }
        }

    }

    // Reserve NFTs for founders, team, and giveaways
    uint public nftReserve = 75;
    
    event PupNameChange(address _by, uint _tokenId, string _name);
    
    event licenseisLocked(string _licenseText);

    constructor() ERC721("MaestroPups", "PUPS") {
        
     }
    
    function withdraw() public onlyOwner {
        uint balance = address(this).balance;
        msg.sender.transfer(balance);
    }

    address ownerAcct = 0xA90E6Ff084580B1194f7333a702De7641E33DFb9; 
    function reservePups() public onlyOwner {       
         require(nftReserve > 60, "Initial Reserve has already happened.");
        uint supply = totalSupply();
        uint i;
        for (i = 0; i < 60; i++) {
            _safeMint(ownerAcct, supply + i);
            nftReserve = nftReserve.sub(50);
        }
    }


     
    function reserveMorePups(address _to, uint256 _reserveAmount) public onlyOwner {  
       
        uint supply = totalSupply();
        require(_reserveAmount > 0 && _reserveAmount <= nftReserve, "Not enough reserve left for team");
        for (uint i = 0; i < _reserveAmount; i++) {
            _safeMint(_to, supply + i);
        }
        nftReserve = nftReserve.sub(_reserveAmount);
        
    }


    function setProvenanceHash(string memory provenanceHash) public onlyOwner {
        PROVENANCE = provenanceHash;
    }

    function setBaseURI(string memory baseURI) public onlyOwner {
        _setBaseURI(baseURI);
    }


    function flipSaleState() public onlyOwner {
        SALE_ACTIVE = !SALE_ACTIVE;
    }
    
    
    function tokensOfOwner(address _owner) external view returns(uint256[] memory ) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            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;
        }
    }
    
    // Returns the license for tokens
    function tokenLicense(uint _id) public view returns(string memory) {
        require(_id < totalSupply(), "CHOOSE A PUP WITHIN RANGE");
        return LICENSE_TEXT;
    }
    
    // Locks the license to prevent further changes 
    function lockLicense() public onlyOwner {
        LOCK_LICENSE =  true;
        emit licenseisLocked(LICENSE_TEXT);
    }
    
    // Change the license
    function changeLicense(string memory _license) public onlyOwner {
        require(LOCK_LICENSE == false, "License already locked");
        LICENSE_TEXT = _license;
    }   
    



    function viewPupName(uint _tokenId) public view returns( string memory ){
        require( _tokenId < totalSupply(), "Choose a Pup within range" );
        return PupNames[_tokenId];
    }
    
}
function lockLicense() public onlyOwner {
    LOCK_LICENSE =  true;
    emit licenseisLocked(LICENSE_TEXT);
}

// Locks the license to prevent further changes 