contract POOHEADZ is Ownable, ERC721Enumerable, ReentrancyGuard {
    using SafeMath for uint256;

    string public POOHEAD_PROVENANCE = ""; // IPFS URL WILL BE ADDED WHEN ALL ARE SOLD OUT

    string public LICENSE_TEXT = ""; // IT IS WHAT IT SAYS

    bool licenseLocked = false; // TEAM CAN'T EDIT THE LICENSE AFTER THIS GETS TRUE

    uint256 public constant POOHEADPrice = 50000000000000000; // 0.05 ETH

    uint256 public constant maxPOOHEADPurchase = 10;

    uint256 public constant MAX_POOHEAD = 4000;

    bool public saleIsActive = false;

    mapping(uint256 => string) public POOHEADNames;

    // the baseURI for token metadata
    string public baseURI = "";

    // Reserve 5 for team - Giveaways/Prizes etc
    uint256 public POOHEADReserve = 100;

    address public constant developerAddress =
        0x000431DdC083C803f025C13184cdCe11fF959e64;

    // royalty got from a sell
    uint256 public constant DEVELOPER_SELL_PERCENT = 10; // 10% of sell price

    event licenseisLocked(string _licenseText);

    constructor() ERC721("PooHeadz", "POO") {}

    /**
     * @dev Withdraw contract balance to owner
     */
    function withdraw() public onlyOwner {
        sendValueTo(msg.sender, address(this).balance);
    }

    /**
     * @dev Send an amount of value to a specific address
     * @param to_ address that will receive the value
     * @param value to be sent to the address
     */
    function sendValueTo(address to_, uint256 value) internal {
        address payable to = payable(to_);
        (bool success, ) = to.call{value: value}("");
        require(success, "Transfer failed.");
    }

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

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

    /**
     * @dev baseURI for computing {tokenURI}. Empty by default, can be overwritten
     * in child contracts.
     */
    function _baseURI() internal view override returns (string memory) {
        return baseURI;
    }

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

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

    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(uint256 _id) public view returns (string memory) {
        require(_id < totalSupply(), "CHOOSE A POOHEAD WITHIN RANGE");
        return LICENSE_TEXT;
    }

    // Locks the license to prevent further changes
    function lockLicense() public onlyOwner {
        licenseLocked = true;
        emit licenseisLocked(LICENSE_TEXT);
    }

    // Change the license
    function changeLicense(string memory _license) public onlyOwner {
        require(licenseLocked == false, "License already locked");
        LICENSE_TEXT = _license;
    }

    function mintPOOHEAD(uint256 numberOfTokens) public payable nonReentrant {
        require(saleIsActive, "Sale must be active to mint Pooheads");
        require(
            numberOfTokens > 0 && numberOfTokens <= maxPOOHEADPurchase,
            "Can only mint 10 tokens at a time"
        );
        require(
            totalSupply().add(numberOfTokens) <= MAX_POOHEAD,
            "Purchase would exceed max supply of Pooheads"
        );
        require(
            msg.value >= POOHEADPrice.mul(numberOfTokens),
            "Ether value sent is not correct"
        );
        require(
            msg.value / numberOfTokens == POOHEADPrice,
            "Mint value is not good"
        );

        for (uint256 i = 0; i < numberOfTokens; i++) {
            uint256 mintIndex = totalSupply();
            if (totalSupply() < MAX_POOHEAD) {
                _safeMint(msg.sender, mintIndex);
            }
        }

        uint256 value = msg.value;
        sendValueTo(developerAddress, (value * DEVELOPER_SELL_PERCENT) / 100);
    }

    // GET ALL OF A WALLET AS AN ARRAY OF STRINGS. WOULD BE BETTER MAYBE IF IT RETURNED A STRUCT WITH ID-NAME MATCH
    function POOHEADNamesOfOwner(address _owner)
        external
        view
        returns (string[] memory)
    {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new string[](0);
        } else {
            string[] memory result = new string[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = POOHEADNames[
                    tokenOfOwnerByIndex(_owner, index)
                ];
            }
            return result;
        }
    }
}
function lockLicense() public onlyOwner {
    licenseLocked = true;
    emit licenseisLocked(LICENSE_TEXT);
}

// Locks the license to prevent further changes