contract MonkeyBrixAwards is ERC721Enumerable, Ownable{
    using SafeMath for uint256;
    using Address for address;
    using Strings for uint256;
    uint16 public CURRENT_METAL = 0;
    uint16 public METAL_END = 529;
    uint16 public CURRENT_CRYSTAL = 530;
    uint16 public CRYSTAL_END = 549;
    
    
    uint256 public MAX_SUPPLY = 550;

    string private _baseURIExtended;
    mapping (uint256 => string) _tokenURIs;

    constructor() ERC721("MonkeyBrix Awards","MKBXA"){
    }
    function airdrop(address[] memory receivers, uint8[] memory metalCounts, uint8[] memory crystalCounts) public onlyOwner{
        require(receivers.length == metalCounts.length, "mismatched length");
        require(metalCounts.length == crystalCounts.length, "mismatched length");
        uint8 metalCount;
        uint8 crystalCount;
        address receiver;
        uint8 i;
        for (i=0; i<receivers.length; i++){
            receiver = receivers[i];
            metalCount = metalCounts[i];
            mintMetal(receiver , metalCount);
            crystalCount = crystalCounts[i];
            mintCrystal(receiver, crystalCount);
        }
    }

    function mintMetal(address receiver, uint8 count) public onlyOwner {
        uint8 i;
        uint16 tokenId;
        for (i = 0 ; i < count; i++){
            require(CURRENT_METAL <= METAL_END, "reached end of METAL");
            tokenId = CURRENT_METAL;
            CURRENT_METAL = CURRENT_METAL + 1;
            _safeMint(receiver, tokenId);
        }
    }
    function mintCrystal(address receiver, uint8 count) public onlyOwner{
        uint8 i;
        uint16 tokenId;
        for (i = 0 ; i < count; i++){
            require(CURRENT_CRYSTAL <= CRYSTAL_END, "reached end of crystal");
            tokenId = CURRENT_CRYSTAL;
            CURRENT_CRYSTAL = CURRENT_CRYSTAL + 1;
            _safeMint(receiver, tokenId);
        }
    }


    function withdraw() public onlyOwner {
        uint balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }

    function reserveTokens(address receiver, uint8 metalCount, uint8 crystalCount) public onlyOwner {
        mintCrystal(receiver, crystalCount);
        mintMetal(receiver, metalCount);
    } 
    function _baseURI() internal view virtual override returns (string memory) {
        return _baseURIExtended;
    }

    // Sets base URI for all tokens, only able to be called by contract owner
    function setBaseURI(string memory baseURI_) external onlyOwner {
        _baseURIExtended = baseURI_;
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");
        string memory base = _baseURI();
        return string(abi.encodePacked(base, tokenId.toString()));
    }
}
function setBaseURI(string memory baseURI_) external onlyOwner {
    _baseURIExtended = baseURI_;
}

// Sets base URI for all tokens, only able to be called by contract owner