contract Cloudies is ERC721A, IERC2981, Ownable, ReentrancyGuard {
    using Strings for uint256;

    string public BASE_URI = "ipfs://QmQ6pFjMioyNY3hvtBCtuuvs3GtuUJPSTytNC9Ab8tgXHD/";
    string public BASE_EXTENSION = ".json";
    uint256 public MAX_PER_TXN = 5;
    uint256 public constant MAX_SUPPLY = 2000;
    uint256 public SALE_PRICE = 0.025 ether;
    uint256 public MAX_FREE = 500;
    bool public SALE_ACTIVE = false;
    address public COLLAB = 0xB0AaDC7EB7461A0FFDCe35bb62044b376A802Afa;

    constructor() ERC721A("Cloudies", "CLOUDIES", 100) {
        _safeMint(msg.sender, 5);
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        virtual
        override(ERC721A, IERC165)
        returns (bool)
    {
        return
            interfaceId == type(IERC2981).interfaceId ||
            super.supportsInterface(interfaceId);
    }

    /**
     * @dev Max 5 per txn, 0.025 eth ea.
     */
    function mint(uint256 quantity) external payable nonReentrant {
        require(SALE_ACTIVE, "sale not active");
        require(totalSupply() + quantity <= MAX_SUPPLY, "excceed max supply");
        require(quantity <= MAX_PER_TXN, "invalid quantity");
        if (totalSupply() >= MAX_FREE) {
            require((SALE_PRICE * quantity) <= msg.value, "insufficient eth");
        }
        require(tx.origin == msg.sender, "cannot use custom contract");
        _safeMint(msg.sender, quantity);
    }

    /**
     * @dev Max 5 per txn, free until 500
     */
    function freeMint(uint256 quantity) external nonReentrant {
        require(SALE_ACTIVE, "sale not active");
        require(totalSupply() <= MAX_FREE, "free mint is over");
        require(quantity <= MAX_PER_TXN, "invalid quantity");
        require(totalSupply() + quantity <= MAX_SUPPLY, "excceed max supply");
        _safeMint(msg.sender, quantity);
    }

    function setCollab(address collab) external onlyOwner {
        COLLAB = collab;
    }

    function setMaxFree(uint256 maxFree) external onlyOwner {
        MAX_FREE = maxFree;
    }

    function setMaxPerTxn(uint256 maxPerTxn) external onlyOwner {
        MAX_PER_TXN = maxPerTxn;
    }

    function setPrice(uint256 price) external onlyOwner {
        SALE_PRICE = price;
    }

    function setSale(bool saleActive) external onlyOwner {
        SALE_ACTIVE = saleActive;
    }

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

    function setBaseExtension(string memory baseExtension) external onlyOwner {
        BASE_EXTENSION = baseExtension;
    }

    function withdrawAll() public onlyOwner {
        uint256 balance = address(this).balance;
        if (COLLAB == msg.sender) {
            _withdraw(msg.sender, address(this).balance);
        } else {
            _withdraw(COLLAB, (balance * 7000) / 10000); // 70%
            _withdraw(msg.sender, (balance * 3000) / 10000); // 30%
        }
    }

    function _withdraw(address _addr, uint256 _amt) private {
        (bool success, ) = _addr.call{value: _amt}("");
        require(success, "transfer failed");
    }

    function tokenURI(uint256 _tokenId)
        public
        view
        override
        returns (string memory)
    {
        require(_exists(_tokenId), "nonexistent token");
        return
            bytes(BASE_URI).length > 0
                ? string(
                    abi.encodePacked(
                        BASE_URI,
                        Strings.toString(_tokenId),
                        BASE_EXTENSION
                    )
                )
                : "";
    }

    function royaltyInfo(uint256 tokenId, uint256 salePrice)
        external
        view
        override
        returns (address receiver, uint256 royaltyAmount)
    {
        require(_exists(tokenId), "nonexistent token");
        return (address(this), (salePrice * 500) / 10000);
    }
}
function mint(uint256 quantity) external payable nonReentrant {
    require(SALE_ACTIVE, "sale not active");
    require(totalSupply() + quantity <= MAX_SUPPLY, "excceed max supply");
    require(quantity <= MAX_PER_TXN, "invalid quantity");
    if (totalSupply() >= MAX_FREE) {
        require((SALE_PRICE * quantity) <= msg.value, "insufficient eth");
    }
    require(tx.origin == msg.sender, "cannot use custom contract");
    _safeMint(msg.sender, quantity);
}

/**
 * @dev Max 5 per txn, 0.025 eth ea.
 */