contract MumboJumbo is ERC721Enumerable, Ownable{
    using SafeMath for uint256;
    using Address for address;
    using Strings for uint256;


    uint256 public constant NFT_PRICE = 50000000000000000; // 0.05 ETH
    uint public constant MAX_NFT_PURCHASE = 5;
    uint256 public MAX_SUPPLY = 10000;
    bool public saleIsActive = false;

    uint256 public startingIndex;
    uint256 public startingIndexBlock;
    address public artist = 0xE23fD6B28725FaB4895480Bf32272DBcC06DF60f;
    address public dev = 0x8fb04ECA66171959222bB187D9703F8a6CF493e6;

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

    constructor() ERC721("Mumbo Jumbo","MUMBO"){
    }


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

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

    function reserveTokens(uint256 num) public onlyOwner {
        uint supply = totalSupply();
        uint i;
        for (i = 0; i < num; i++) {
            _safeMint(msg.sender, supply + i);
        }

        if (startingIndexBlock == 0) {
            startingIndexBlock = block.number;
        }
    }

    function setMaxTokenSupply(uint256 maxSupply) public onlyOwner {
        MAX_SUPPLY = maxSupply;
    }
    
    function mint() public payable {
        require(saleIsActive, "Sale is not active at the moment");
        require((totalSupply() + 1) <= MAX_SUPPLY, "Purchase would exceed max supply");
        require(NFT_PRICE == msg.value, "Sent ether value is incorrect, mint cost is 0.05 ether");
        
        uint256 totalShares = 10000;
        uint256 payment = msg.value * 5000 / totalShares;

        payable(artist).transfer(payment);
        payable(dev).transfer(payment);
        
        if ((totalSupply() + 1) <= MAX_SUPPLY) {
            _safeMint(msg.sender, totalSupply());
        }
        
    }

    function mint3() public payable {
        require(saleIsActive, "Sale is not active at the moment");
        require(totalSupply().add(3) <= MAX_SUPPLY, "Purchase would exceed max supply");
        require(NFT_PRICE.mul(3) == msg.value, "Must Send 0.15 ETH to Mint 5 NFTs");
        
        
        uint256 totalShares = 10000;
        uint256 payment = msg.value * 5000 / totalShares;

        payable(artist).transfer(payment);
        payable(dev).transfer(payment);

        for (uint i = 0; i < 3; i++) {
            _safeMint(msg.sender, totalSupply());
        }
    }


    function calcStartingIndex() public onlyOwner {
        require(startingIndex == 0, "Starting index has already been set");
        require(startingIndexBlock != 0, "Starting index has not been set yet");

        startingIndex = uint(blockhash(startingIndexBlock)) % MAX_SUPPLY;
        // Just a sanity case in the worst case if this function is called late (EVM only stores last 256 block hashes)
        if(block.number.sub(startingIndexBlock) > 255) {
            startingIndex = uint(blockhash(block.number - 1)) % MAX_SUPPLY;
        }

        // To prevent original sequence
        if (startingIndex == 0) {
            startingIndex = startingIndex.add(1);
        }
    }

    function emergencySetStartingIndexBlock() public onlyOwner {
        require(startingIndex == 0, "Starting index is already set");
        startingIndexBlock = block.number;
    }

    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 _tokenURI = _tokenURIs[tokenId];
        string memory base = _baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }
        // If there is a baseURI but no tokenURI, concatenate the tokenID to the 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