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

    bool public biddingActive = false;           // Can you bid on a gal? Yes or no. AAAAAAAAAAAAAAAAAAA

    mapping(address => uint256) private galsClaimed;// How many gals an address has claimed.
    
    mapping(uint256 => uint256) public galBids;     // Mapping of Gal ID keys to their respective Bid Values
    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/spookycameo";
    string constant private jsonAppend = ".json";

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

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

        galBidAddresses[0] = msg.sender;
        galBidAddresses[1] = msg.sender;
        galBidAddresses[2] = msg.sender;
        galBidAddresses[3] = msg.sender;
        galBidAddresses[4] = 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);
    }

    // Toggle whether any gals can be purchased and reserved.
    function toggleBidding() public onlyOwner
    {
        biddingActive = !biddingActive;
    }

    // Tells teh world how many gals a given address has bongos binted.
    function galsClaimedBy(address owner) external view returns (uint256)
    {
        require(owner != address(0), 'Zero address cant into mintings');
        return galsClaimed[owner];
    }

    // 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;
    }

    function bid(uint8 _id) public payable
    {
        require(biddingActive, 'Bidding is not open.');
        require(_id > 0, 'Cannot bid 0.');
        require(_id <= maxGals, 'Cannot bid for a gal ID higher than the max.');
        require(msg.value > galBids[_id], "New Bid must be greater than the previous Bid.");        

        // Refunding the previous bidder when a new bid is paid
        payable(galBidAddresses[_id]).transfer(galBids[_id]); // what about the defaults?? TODO: test this

        // based.png
        galBidAddresses[_id] = msg.sender;
        galBids[_id] = msg.value;

        emit Bid(msg.sender, _id);
    }

    // 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 = 1; i <= maxGals; i++)
        {
            super._mint(galBidAddresses[i], currentMintedGal);
            emit Minted(galBidAddresses[i], i);
        }
    }

    // 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 toggleBidding() public onlyOwner
    {
        biddingActive = !biddingActive;
    }
// Toggle whether any gals can be purchased and reserved.