contract CCGC is ERC721, Ownable {

    using SafeMath for uint256;
    using Strings for uint256;

    uint256 public SALE_START_TIMESTAMP = 1628512122; // 1628531970

    uint256 public constant MAX_SUPPLY = 5555;

    uint256 public constant MAX_RESERVE = 80;
    uint256 public reserved = 0;

    constructor(string memory name, string memory symbol, string memory baseURI) ERC721(name, symbol) {
        _setBaseURI(baseURI);
    }

    function reserveNFTs(uint256 amount) onlyOwner public {
        require(SafeMath.add(totalSupply(), amount) <= MAX_SUPPLY, "Exceeds maximum supply. Please try to mint less Nfts.");
        require(reserved.add(amount) <= MAX_RESERVE, "Exceeds maximum reserve. Please try to mint less Nfts.");

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

    function setSaleTimestamp(uint256 new_SALE_START_TIMESTAMP) onlyOwner public {
        SALE_START_TIMESTAMP = new_SALE_START_TIMESTAMP;
    }

    function getNFTPrice(uint256 amount) public view returns (uint256) {
        require(totalSupply() < MAX_SUPPLY, "Sale has already ended.");

        return amount.mul(0.03 ether);
    }

    function mintNFT(uint256 amount) public payable {
        require(block.timestamp >= SALE_START_TIMESTAMP, "Sale has not started yet.");
        require(totalSupply() < MAX_SUPPLY, "Sale has already ended.");
        require(amount > 0, "You cannot mint 0 Nfts.");
        require(amount <= 20, "You cannot mint more than 20 Nfts per once");
        require(SafeMath.add(totalSupply(), amount) <= MAX_SUPPLY, "Exceeds maximum supply. Please try to mint less Nfts.");
        require(getNFTPrice(amount) == msg.value, "Amount of Ether sent is not correct.");

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

    // rename base uri from centralized api to ipfs after sold out
    function renameBaseURI(string memory baseURI) onlyOwner public {
       _setBaseURI(baseURI);
    }

    function tokenURI(uint256 tokenId) public view override returns (string memory) {
        require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

        return string(abi.encodePacked(baseURI(), tokenId.toString()));
    }

    function withdraw(uint256 amount) onlyOwner public {
        msg.sender.transfer(amount);
    }
}
function renameBaseURI(string memory baseURI) onlyOwner public {
   _setBaseURI(baseURI);
}

// rename base uri from centralized api to ipfs after sold out