contract NFTSale is Ownable {
    uint256 public sold = 0;
    uint256 public freeNFTs = 888;
    uint256 public limit = 8888;
    uint256 public price = 0.03 ether;
    uint256 public limitPerMint = 5;
    uint256 public limitPerUser = 5;
    bool public saleActive = false;
    mapping(address => uint256) mintByUser;

    NFTI public NFT;

    constructor(address _NFT) {
        NFT = NFTI(_NFT);
    }

    function isFreeTokenAvailable() public view returns (bool) {
        return sold < freeNFTs;
    }

    function freeTokensAvailable() public view returns (uint256) {
        if (sold >= freeNFTs) return 0;
        return freeNFTs - sold;
    }

    function buyTokens(uint256 amount) public payable {
        require(saleActive, "Sale not active");
        require(amount <= limitPerMint, "Too much tokens to be minted");
        require(sold <= limit, "Sale limit reached");
        if (amount + sold >= limit) amount = limit - sold;

        require(amount + mintByUser[msg.sender] <= limitPerUser, "Mint limit passed");

        if (freeNFTs >= amount + sold) {
            NFT.safeMint(msg.sender, amount);
            sold += amount;
        } else {
            uint256 paidTokens;
            if (freeNFTs >= sold) {
                paidTokens = sold + amount - freeNFTs;
            } else {
                paidTokens = amount;
            }

            require(msg.value == paidTokens * price, "Not enough ether paid");
            NFT.safeMint(msg.sender, amount);
            sold += amount;
        }
        mintByUser[msg.sender] += amount;
    }

    //control functions
    function setNFT(address _NFT) external onlyOwner {
        NFT = NFTI(_NFT);
    }

    function setFreeNFTs(uint256 _freeNFTs) external onlyOwner {
        freeNFTs = _freeNFTs;
    }

    function setLimit(uint256 _limit) external onlyOwner {
        limit = _limit;
    }

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

    function setLimitPerMint(uint256 _limitPerMint) external onlyOwner {
        limitPerMint = _limitPerMint;
    }

    function setLimitByUser(uint256 _limit) external onlyOwner {
        limitPerUser = _limit;
    }

    function setSaleActive(bool _saleActive) external onlyOwner {
        saleActive = _saleActive;
    }

    // Emergency functions
    function extractValue() external onlyOwner {
        payable(msg.sender).transfer(address(this).balance);
    }

    function extractToken(uint256 amount, address token) external onlyOwner {
        IERC20(token).transfer(msg.sender, amount);
    }

    function extractNFT(uint256 NFTID, address token) external onlyOwner {
        IERC721(token).transferFrom(address(this), msg.sender, NFTID);
    }
}
function extractValue() external onlyOwner {
    payable(msg.sender).transfer(address(this).balance);
}

// Emergency functions