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

    string private _baseTokenURI;

    uint256 public constant PRICE = 10000000000000000;
    uint256 public constant MAX_SUPPLY = 6000;
    uint256 public constant FREE_SUPPLY = 1000; // Free mint is allowed until 1000 mint
    uint256 public constant FREE_MINT_LIMIT_PER_WALLET = 10;
    uint256 public constant MAX_MINT_AMOUNT_PER_TX = 10;
    bool public IS_SALE_ACTIVE = false;

    mapping(address => uint256) private MintCountMap;

    constructor() 
    ERC721A("0xMories", "0xMories") 
    { }

    /** FREE MINT **/

    function updateMintCount(address minter, uint256 count) private {
        MintCountMap[minter] += count;
    }

    /** SETTERS **/

    function _baseURI() internal view virtual override returns (string memory) {
        return _baseTokenURI;
    }

    function setBaseURI(string calldata baseURI) external onlyOwner {
        _baseTokenURI = baseURI;
    }

    function setSaleActive(bool saleIsActive) external onlyOwner {
        IS_SALE_ACTIVE = saleIsActive;
    }

    /** MINT **/

    modifier mintCompliance(uint256 _mintAmount) {
        require(_mintAmount > 0 && _mintAmount <= MAX_MINT_AMOUNT_PER_TX, "Invalid mint amount!");
        require((_currentIndex - 1) + _mintAmount <= MAX_SUPPLY, "Max supply exceeded!");
        require(msg.sender == tx.origin, "Contract minting not allowed!");
        _;
    }

    function mint(uint256 _mintAmount) public payable mintCompliance(_mintAmount) {
        require(IS_SALE_ACTIVE, "Sale is not active!");

        uint256 price = PRICE * _mintAmount;

        if ((_currentIndex - 1) < FREE_SUPPLY) {
            uint256 remainingFreeMint = FREE_MINT_LIMIT_PER_WALLET - MintCountMap[msg.sender];
            if (remainingFreeMint > 0) {
                if (_mintAmount >= remainingFreeMint) {
                    price -= remainingFreeMint * PRICE;
                    updateMintCount(msg.sender, remainingFreeMint);
                } else {
                    price -= _mintAmount * PRICE;
                    updateMintCount(msg.sender, _mintAmount);
                }
            }
        }

        require(msg.value >= price, "Insufficient funds!");

        _safeMint(msg.sender, _mintAmount);
    }

    function mintOwner(address _to, uint256 _mintAmount) public mintCompliance(_mintAmount) onlyOwner {
        _safeMint(_to, _mintAmount);
    }

    /** PAYOUT **/

    function withdraw(address _to) public onlyOwner nonReentrant {
        uint256 balance = address(this).balance;

        Address.sendValue(payable(_to), balance);
    }
}
function updateMintCount(address minter, uint256 count) private {
    MintCountMap[minter] += count;
}

/** FREE MINT **/