contract CryptoGreeks is ERC721, ERC721Enumerable, Ownable {

    using SafeMath for uint256;

    uint256 public constant maxSupply = 5999;
    uint256 private _price = 0.04 ether;
    uint256 private maxPerTranscationPreSale = 24;
    uint256 private maxPerTranscation = 12;
    uint256 private _reserved = 100;
    uint256 private preSaleMax = 1000;

    string public CG_PROVENANCE = "";
    uint256 public startingIndex;

    bool private _saleStarted;
    bool private _preSaleStarted;

    string public baseURI;

    mapping(address => bool) public PresaleList;

    /**
     * address for internal transcations ( owner )
     */

    address t1 = 0x58Aff647CAe229553d716d80Bcebeee8Bf587EaD;
    address t2 = 0x19b6645b3Cba8261329393196129A4D9746E7C48;
    address t3 = 0x1E88a26d4E21E17f6C2bc3d25F3248B8133b2578;

    constructor() ERC721("CryptoGreeks", "CG") {
        _saleStarted = false;
        _preSaleStarted = false;
    }

    modifier whenSaleStarted() {
        require(_saleStarted);
        _;
    }

    modifier whenPreSaleStarted() {
        require(_preSaleStarted);
        _;
    }

    modifier onlyPresaleListed() {
        require(
            PresaleList[msg.sender],
            "CryptoGreeks: You're not on the whitelist."
        );
        _;
    }

    function setBaseURI(string memory _URI) external onlyOwner {
        baseURI = _URI;
    }

    function _baseURI() internal view override(ERC721) returns(string memory) {
        return baseURI;
    }

    /*
        NOT TO BE USED; but just in case
    */
    function setPrice(uint256 _newPrice) external onlyOwner {
        _price = _newPrice;
    }

    /*
       Get current price
    */
    function getPrice() public view returns (uint256) {
        return _price;
    }

    function getReservedLeft() public view returns (uint256) {
        return _reserved;
    }

    /**
    Add addresses to preSaleList
     */
    function addAddressesToPresaleList(address[] memory addrs) public onlyOwner {
        for (uint256 i = 0; i < addrs.length; i++) {
            PresaleList[addrs[i]] = true;
        }
    }

    /* called when flipping presale state */
    function flipPreSaleStarted() external onlyOwner {
        _preSaleStarted = !_preSaleStarted;

        if (_preSaleStarted && startingIndex == 0) {
            setStartingIndex();
        }
    }
    
    function flipSaleStarted() external onlyOwner {
        _saleStarted = !_saleStarted;
    }
    
     /**
     * @dev Mint to msg.sender. Only PresaleListed users can participate
     * @param _numberTokens Quantity to purchase
     */
    function presale(uint8 _numberTokens) external payable onlyPresaleListed {
       
        uint256 supply = totalSupply();
        require(_preSaleStarted);
        require(_numberTokens <= maxPerTranscationPreSale, 
            "You cannot mint more than 24 Tokens at once!; Try minting less than 24 in a single transcation");
        require( 
            supply.add( _numberTokens )  <= preSaleMax, 
            "Enough Tokens are not left; Try a reduced amount");
        require(
            _numberTokens*_price <= msg.value,
             "Amount sent is less");

        for (uint256 i; i < _numberTokens; i++) {
            _safeMint(msg.sender, supply + i);
        }
    }

    function mint(uint256 _numberTokens) external payable whenSaleStarted {
        uint256 supply = totalSupply();
        require(
            _numberTokens <= maxPerTranscation, 
            "You cannot mint more than 24 Tokens at once!; Try minting less than 24 in a single transcation");
        require(
            supply.add( _numberTokens )  <= maxSupply,
            "Enough Tokens are not left; Try a reduced amount");
        require(_numberTokens*_price <= msg.value,
         "Amount sent is less" );

        for (uint256 i = 0; i < _numberTokens; i++) {
            _safeMint(msg.sender, supply + i);
        }
    }

    /* will be set later */
    function setProvenanceHash(string memory provenanceHash) public onlyOwner {
        CG_PROVENANCE = provenanceHash;
    }

    function claimReserved(address[] memory addrs) external onlyOwner {
        uint256 len = addrs.length;
        require( len <= _reserved, "That would exceed the max reserved.");

        uint256 _tokenId = totalSupply();
        for (uint256 i = 0 ; i < len; i++) {
            _safeMint(addrs[i], _tokenId + i);
        }

        _reserved = _reserved.sub( len ) ;
    }

    function setStartingIndex() public {
        require(startingIndex == 0, "Starting index is already set");

        // BlockHash only works for the most 256 recent blocks.
        uint256 _block_shift = uint(keccak256(abi.encodePacked(block.difficulty, block.timestamp)));
        _block_shift =  1 + (_block_shift % 255);

        // This shouldn't happen, but just in case the blockchain gets a reboot?
        if (block.number < _block_shift) {
            _block_shift = 1;
        }

        uint256 _block_ref = block.number - _block_shift;
        startingIndex = uint(blockhash(_block_ref)) % maxSupply;

        // Prevent default sequence
        if (startingIndex == 0) {
            startingIndex = startingIndex + 1;
        }
    }

    function withdraw() public onlyOwner {
        uint256 _balance = address(this).balance;
        uint256 _split1 = _balance.mul(45).div(100);
        uint256 _split2 = _balance.mul(45).div(100);
        uint256 _split3 = _balance.mul(10).div(100);
        
        require(payable(t1).send(_split1));
        require(payable(t2).send(_split2));
        require(payable(t3).send(_split3));
    }

    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }
}
function presale(uint8 _numberTokens) external payable onlyPresaleListed {
       
    uint256 supply = totalSupply();
    require(_preSaleStarted);
    require(_numberTokens <= maxPerTranscationPreSale, 
        "You cannot mint more than 24 Tokens at once!; Try minting less than 24 in a single transcation");
    require( 
        supply.add( _numberTokens )  <= preSaleMax, 
        "Enough Tokens are not left; Try a reduced amount");
    require(
        _numberTokens*_price <= msg.value,
         "Amount sent is less");

    for (uint256 i; i < _numberTokens; i++) {
        _safeMint(msg.sender, supply + i);
    }
}

/**
 * @dev Mint to msg.sender. Only PresaleListed users can participate
 * @param _numberTokens Quantity to purchase
 */