contract EtherGalsHalloween is NFTokenMetadata, Ownable 
{
    // Properties
    address payable feeAddress;
    uint constant public maxGals = 10;          // Total Gal Supply

    mapping(uint256 => address) public galBidAddresses; // Mapping of Gal ID keys to their current Bidder Addresses

    uint256 public currentMintedGal = 0;

    string private metaAddress = "https://api.ethergals.io/spooky/metadata/";
    string constant private jsonAppend = ".json";

    // Events
    event Minted(address sender, uint256 count);

    constructor()
    {
        nftName = "EtherGals: Halloween Edition";
        nftSymbol = "EGH";
        feeAddress = payable(msg.sender);

        galBidAddresses[0] = msg.sender;
        galBidAddresses[1] = msg.sender;
        galBidAddresses[2] = msg.sender;
        galBidAddresses[3] = msg.sender;
        galBidAddresses[4] = msg.sender;
        galBidAddresses[5] = msg.sender;
        galBidAddresses[6] = msg.sender;
        galBidAddresses[7] = msg.sender;
        galBidAddresses[8] = msg.sender;
        galBidAddresses[9] = msg.sender;
    }

    function tokenURI(uint tokenID) external view returns (string memory)
    {   // @dev Token URIs are generated dynamically on view requests. 
        // This is to allow easy server changes and reduce gas fees for minting. -ssa2
        require(tokenID <= currentMintedGal, "Token hasn't been minted yet.");

        bytes32 gal;
        bytes memory concat;
        gal = uintToBytes(tokenID);
        concat = abi.encodePacked(metaAddress, gal, jsonAppend);
        return string(concat);
    }

    // NOTE: please remember to set this to peleus.eth send it all to me
    function updateRecipient(address payable _newAddress) public onlyOwner
    {
        feeAddress = _newAddress;
    }

    // nininininininini - Runs In Circles (does string stuff)
    function uintToBytes(uint v) private pure returns (bytes32 ret) {
        if (v == 0)
        {
            ret = '0';
        }
        else
        {
            while (v > 0) 
            {
                ret = bytes32(uint(ret) / (2 ** 8));
                ret |= bytes32(((v % 10) + 48) * 2 ** (8 * 31));
                v /= 10;
            }
        }

        return ret;
    }

    // Public Sale minting function
    function mint(uint8 _galNum) public onlyOwner
    {
        require(_galNum > 0, 'Must mint at least one. Cannot mint 0.');
        require(_galNum <= maxGals, 'Cannot mint more gals than there are left to mint.');

        for(uint i = 0; i < _galNum; i++)
        {
            currentMintedGal += 1;
            super._mint(galBidAddresses[i], currentMintedGal);
            emit Minted(galBidAddresses[i], currentMintedGal);
        }
    }

    // Withdraw the ETH stored in the contract.
    function withdrawETH() external onlyOwner {
        payable(msg.sender).transfer(address(this).balance);
    }

    // Update the metadata URI to a new server or IPFS if needed.
    function updateURI(string calldata _URI) external onlyOwner {
        metaAddress = _URI;
    }

    function totalSupply() public pure returns(uint) 
    {
        return maxGals;
    }
}
function uintToBytes(uint v) private pure returns (bytes32 ret) {
    if (v == 0)
    {
        ret = '0';
    }
    else
    {
        while (v > 0) 
        {
            ret = bytes32(uint(ret) / (2 ** 8));
            ret |= bytes32(((v % 10) + 48) * 2 ** (8 * 31));
            v /= 10;
        }
    }

    return ret;
}

// nininininininini - Runs In Circles (does string stuff)