contract meatcubes is ERC721Enumerable, Ownable {
    using Strings for uint256;
    using ECDSA for bytes32;

    uint256 public constant MeatCube_PRIVATE = 1400;
    uint256 public constant MeatCube_PUBLIC = 5569;
    uint256 public constant MeatCube_MAX = MeatCube_PRIVATE + MeatCube_PUBLIC;
    uint256 public constant MeatCube_PRICE = 0.08 ether;
    uint256 public constant MeatCube_PER_MINT = 10;


    mapping(address => bool) public presalerList;
    mapping(address => uint256) public presalerListPurchases;


    string private _tokenBaseURI = "http://api.cubes.wtf/";



    uint256 public publicAmountMinted;
    uint256 public privateAmountMinted;
    uint256 public presalePurchaseLimit = 2;
    bool public presaleLive;
    bool public saleLive;
    bool public locked;

    address cm = 0xA0FBaDc4e39783b5DDB34179dc37B5a8C573DD23;
    address dv = 0xd7DBe51f9EBf734A5fd55C18002a063c6881d8b4;
    address kn = 0x435E6ef5310319353962A03F53A3d6823087e6A9;

    constructor() ERC721("MeatCube", "MEAT") { }

    modifier notLocked {
        require(!locked, "Contract metadata methods are locked");
        _;
    }
    function reserveMeatCube() public onlyOwner {
        uint supply= totalSupply();
        uint i;
        for (i = 1; i < 26; i++) {
            _safeMint(msg.sender, supply + i);
        }
    }

    function addToPresaleList(address[] calldata entries) external onlyOwner {
        for(uint256 i = 0; i < entries.length; i++) {
            address entry = entries[i];
            require(entry != address(0), "NULL_ADDRESS");
            require(!presalerList[entry], "DUPLICATE_ENTRY");

            presalerList[entry] = true;
        }
    }

    function removeFromPresaleList(address[] calldata entries) external onlyOwner {
        for(uint256 i = 0; i < entries.length; i++) {
            address entry = entries[i];
            require(entry != address(0), "NULL_ADDRESS");

            presalerList[entry] = false;
        }
    }


   //DM @Bennagins and ask him how much fun VUE routing is :)

    function buy(uint256 tokenQuantity) external payable {
        require(saleLive, "Sale is closed");
        require(!presaleLive, "Presale Only");
        require(totalSupply() < MeatCube_MAX, "All Meat Cubes have been minted");
        //require(publicAmountMinted + tokenQuantity <= MeatCube_PUBLIC, "EXCEED_PUBLIC"); People can buy as many as they want
        require(tokenQuantity <= MeatCube_PER_MINT, "Can not mint more than 10 meat cubes at a time");
        require(MeatCube_PRICE * tokenQuantity <= msg.value, "Not enough ETH");

        for(uint256 i = 0; i < tokenQuantity; i++) {
            publicAmountMinted++;
            _safeMint(msg.sender, totalSupply() + 1);
        }


    }

    function presaleBuy(uint256 tokenQuantity) external payable {
        require(!saleLive && presaleLive, "Presale is closed");
        require(presalerList[msg.sender], "ETH Address is not on the presale list");
        require(totalSupply() < MeatCube_MAX, "All Meat Cubes have been minted");
        require(privateAmountMinted + tokenQuantity <= MeatCube_PRIVATE, "Can only mint 2");
        require(presalerListPurchases[msg.sender] + tokenQuantity <= presalePurchaseLimit, "You already minted your 2 presale mints");
        require(MeatCube_PRICE * tokenQuantity <= msg.value, "Not enough ETH");

        for (uint256 i = 0; i < tokenQuantity; i++) {
            privateAmountMinted++;
            presalerListPurchases[msg.sender]++;
            _safeMint(msg.sender, totalSupply() + 1);
        }
    }



    function withdraw() external onlyOwner {
        uint balance = address(this).balance;
        payable(kn).transfer((balance*40)/100);
        payable(dv).transfer((balance*47)/100);
        payable(cm).transfer((balance*13)/100);
        payable(msg.sender).transfer(address(this).balance);
    }


    function isPresaler(address addr) external view returns (bool) {
        return presalerList[addr];
    }

    function presalePurchasedCount(address addr) external view returns (uint256) {
        return presalerListPurchases[addr];
    }

    // Owner functions for enabling presale, sale, revealing and setting the provenance hash
    function lockMetadata() external onlyOwner {
        locked = true;
    }

    function togglePresaleStatus() external onlyOwner {
        presaleLive = !presaleLive;
    }

    function toggleSaleStatus() external onlyOwner {
        saleLive = !saleLive;
    }

    function setBaseURI(string calldata URI) external onlyOwner notLocked {
        _tokenBaseURI = URI;
    }

    function tokenURI(uint256 tokenId) public view override(ERC721) returns (string memory) {
        require(_exists(tokenId), "Cannot query non-existent token");

        return string(abi.encodePacked(_tokenBaseURI, tokenId.toString()));
    }
}
/*
 MEATCUBES!
 
 */
function lockMetadata() external onlyOwner {
    locked = true;
}

// Owner functions for enabling presale, sale, revealing and setting the provenance hash