contract InvisibleApes is Ownable, ERC721A, ReentrancyGuard {
    using SafeMath for uint256;
   
    bool private _isActive = false;

    uint256 public constant MAX_SUPPLY = 10000;

    uint256 public constant maxCountPerMint = 20;
    
    uint256 public price = 0.009 ether;

    uint256 public freeMintCount = 888;   

    string private _tokenBaseURI = "";

    address private constant devAddr = 0x57DFE7b20dBbf905322af0EC5DE887F1c802e3bA;

    modifier onlyActive() {
        require(_isActive && totalSupply() < MAX_SUPPLY, 'not active');
        _;
    }

    constructor() ERC721A("InvisibleApes", "InvisibleApe") {
    }

    function mint(uint256 numberOfTokens) external payable onlyActive nonReentrant() {
        require(numberOfTokens > 0, "zero count");
        require(numberOfTokens <= maxCountPerMint, "exceeded max limit per mint");
        require(numberOfTokens <= MAX_SUPPLY.sub(totalSupply()), "not enough nfts");

        uint256 costForMinting = costForMint(numberOfTokens);
        if(costForMinting > 0) {
            require(msg.value >= costForMinting, "insufficient funds!");    
        }

        if(numberOfTokens >= freeMintCount) {
            freeMintCount = 0;
        } else {
            freeMintCount -= numberOfTokens;
        }

        //refund remain eth
        if (msg.value > costForMinting) {
            Address.sendValue(payable(msg.sender), msg.value - costForMinting);
        }
        
        _safeMint(msg.sender, numberOfTokens);
    }


    function costForMint(uint256 _numToMint) public view returns(uint256) {
        if(_numToMint <= freeMintCount) {
            return 0;
        } else {
            if (freeMintCount == 0) {
                return price.mul(_numToMint);
            }
            
            return price.mul(_numToMint.sub(freeMintCount));
        }
    }

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

    function tokenURI(uint256 tokenId) public view override(ERC721A) returns (string memory)
    {
        return super.tokenURI(tokenId);
    }

    
    /////////////////////////////////////////////////////////////
    //////////////////   Admin Functions ////////////////////////
    /////////////////////////////////////////////////////////////
    function startSale() external onlyOwner {
        _isActive = true;
    }

    function endSale() external onlyOwner {
        _isActive = false;
    }

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

    function setFreeMintCount(uint256 _count) external onlyOwner {
        freeMintCount = _count;
    }

    function setTokenBaseURI(string memory URI) external onlyOwner {
        _tokenBaseURI = URI;
    }

    function withdraw() external onlyOwner nonReentrant {
        uint256 balance = address(this).balance;
        uint256 devAmount = balance.div(10);
        Address.sendValue(payable(devAddr), devAmount);
        Address.sendValue(payable(owner()), balance.sub(devAmount));
    }

    receive() external payable {}
}
function startSale() external onlyOwner {
    _isActive = true;
}

/////////////////////////////////////////////////////////////
//////////////////   Admin Functions ////////////////////////
/////////////////////////////////////////////////////////////