contract BYOPill is ERC721Enumerable, Ownable {

    // Safemath or else things go wrong ...
    using SafeMath for uint256;

    // Struct definitions
    struct PillInfo {
        uint256 synthesis_date;
    }

    // Keep track of information .......
    mapping(uint256 => PillInfo) private m_PillDetails;         // Mapping of each token to its details
    
    // Private members
    string private m_BaseURI = "";                              // Base URI

    // Public members
    uint256 public MAX_PILLS;                                   // Max pill supply
    uint256 public m_PillPrice = 60000000000000000;             // 0.06 ETH
                            
    uint public m_MaxPillPurchase = 15;                         // Max pill quantity purchase per mint call
    bool public m_IsSaleActive = false;                         // Is sale active ?
    string public m_Provenance = "";                            // Concatenation of hash data for all pills.

    constructor(
        string memory name,
        string memory symbol,
        string memory baseURI,
        uint256 maxSupply
    ) ERC721(name, symbol) {
        MAX_PILLS = maxSupply;
        m_BaseURI = baseURI;
    }

    // withdrawBalance
    //  -   Standard withdraw function for contract balance
    function withdrawBalance() public onlyOwner {
        uint256 balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }

    // Reserve pills
    //  -   Reserves and mints pill to owner
    //  -   Can only happen pre-sale
    function reservePills(uint256 quantity) public onlyOwner {
        uint256 synthesisDate = block.timestamp;
        for(uint i = 0; i < quantity; i++) {
            uint mintIndex = totalSupply();
            if (mintIndex < MAX_PILLS) {
                _safeMint(msg.sender, mintIndex);
                m_PillDetails[mintIndex] = PillInfo (synthesisDate);
            }
        }
    }

    // Mint pill
    //  -   Mints pills by quantities
    //  -   Can only happen during sale
    function mintPill(uint numberOfTokens) public payable  {
        // Perform some stress checks before proceeding
        require(numberOfTokens > 0, "Quantity must be at least 1");
        require(m_IsSaleActive, "Sale must be active to mint pill.");
        require(
            totalSupply().add(numberOfTokens) <= MAX_PILLS,
            'Only 10,000 pills are mintable !'
        );
        require(numberOfTokens <= m_MaxPillPurchase, "Can only mint set pills at a time.");
        require(m_PillPrice.mul(numberOfTokens) <= msg.value, 'Ethereum sent is not sufficient.');

        // Perform the minting process
        uint256 synthesisDate = block.timestamp;
        for(uint i = 0; i < numberOfTokens; i++) {
            uint mintIndex = totalSupply();
            if (totalSupply() < MAX_PILLS) {
                _safeMint(msg.sender, mintIndex);
                 m_PillDetails[mintIndex] = PillInfo (synthesisDate);
            }
        }
    }

    // toggleSale
    //  -   Toggles sale on/off
    function toggleSale() public onlyOwner {
        m_IsSaleActive = !m_IsSaleActive;
    }

    // setMaxQuantityPerMint
    //  -   Sets the max quantity of pills that can be minted at once
    function setMaxQuantityPerMint (uint256 quantity) public onlyOwner {
        m_MaxPillPurchase = quantity;
    }
    
    // setBaseURI
    //  -   Sets the base URL for NFT metadata
    function setBaseURI(string memory baseURI) external onlyOwner() {
        m_BaseURI = baseURI;
    }

    // _baseURI
    //  -  Returns the current base URL
    function _baseURI() internal view override returns (string memory) {
        return m_BaseURI;
    }

    // setProvenance
    //  -   Sets the provenance hash
    function setProvenance(string memory _provenance)
        external
        onlyOwner
    {
        m_Provenance = _provenance;
    }

    // getPillInfo
    //  -   Returns on chain pill information
    function getPillInfo(uint256 tokenId) public view returns(PillInfo memory info) {
        require(_exists(tokenId), "That pill has not been minted yet.");
        return m_PillDetails[tokenId];
    }
}
// ,,,,,,................ THE BYOPILL RESEARCH FACILITY ,,,,,,.....................
// ,,,,,,................                                       ................,.,
// ,,..............                                                 . .............
// .............                                                        ...........
// ...........                                                            .........
// ........ .                                                               .......
// ........         (#////*(#(/#/#                                          .......
// ......         ###(((((.((////////*(/,/&/(((*                             ......
// ....         ,######((((.(*******(&&&&###........,,,..//%                  .....
//              (######(#####((****(((((((((,*,,,*/(//(,..,,*/(                 ...
//              *((((((((,### .##(&&&&&&&&**,***,**,/*/*/%,,***/               ....
//               ,(((((((((((.((((%%%&&&&#((((//(/(#%#*(##,,***/*               ...
//                . (/(((*((/((((/#%####%(//(///(#%######(,******                ..
//               ..,**/////(/(///.(#######//(//(/(((/((((,******,                ..
//                     ....,,**//////%/((((((**//////////////(/....             ...
//                               ....,,,**//////(#(####((((/,,,,,........       ...
//                                         ...,,****////////***,,......          ..
//                                                   ............                 .
//                                                                                .
// ,,,,,,.............................,,,,.....,,,,,,...,,,,,,.....................
// Uh-oh, you found our synthesis composition ...
// Visit our discord: https://discord.gg/byopills
function setProvenance(string memory _provenance)
        external
        onlyOwner
    {
        m_Provenance = _provenance;
    }
// setProvenance
//  -   Sets the provenance hash