contract LeSyndicatNFT is ERC721, ERC721Enumerable, Ownable {
    using Counters for Counters.Counter;

    Counters.Counter private _tokenIdCounter;

    uint256 public maximumSupply;

    bool public saleActive = false;

    mapping(address => uint8) public preSaleWhiteListed;

    /// @notice The base link that leads to the json file
    string public baseTokenURI;
    
    /// @notice Revealed variables
    string public notRevealedUrl;
    uint256 revealedId;

    /// @notice Team addresses for withdrawals
    address public team1;
    address public team2;
    address public team3;
    address public communityWallet;

    uint256 public price = 0.046 ether;

    modifier isMaximumReached (uint amount) {
        require(totalSupply() + amount <= maximumSupply, 'Purchase would exceed max supply');
        _;
    }

    modifier isSaleActive () {
        require(saleActive, 'Sale is not active');
        _;
    }

    constructor(
        uint256 _maximumSupply,
        string memory _newBaseUri,
        string memory _notRevealedUri
    ) ERC721("LeSyndicatNFT", "LSN") {
        maximumSupply = _maximumSupply;
        baseTokenURI = _newBaseUri;
        notRevealedUrl = _notRevealedUri;
    }

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

    /// @notice Reserve tokens for community giveaway and airdrop
    function reserveTokens(address to, uint _amount) public onlyOwner isMaximumReached(_amount) {
        for (uint i = 0; i < _amount; i++) {
            _safeMint(to, _tokenIdCounter.current());
            _tokenIdCounter.increment();
        }
    }

    /// @notice Standard mint function (public)
    function publicMint(uint _amount) external payable isMaximumReached(_amount) isSaleActive {
        require(_amount < 6 && _amount > 0, "Maximum mint by transaction is 5 and minimum is 1");
        require(msg.value == price * _amount, "Wrong amount of ETH sent");

        for (uint i = 0; i < _amount; i++) {
            _safeMint(msg.sender, _tokenIdCounter.current());
            _tokenIdCounter.increment();
        }
    }

    /// @notice Pre sale mint function
    function preSaleMint(uint8 _amount) external payable isMaximumReached(_amount) {
        require(_amount <= preSaleWhiteListed[msg.sender], "No tokens available for presale");
        require(msg.value == price * _amount, "Wrong amount of ETH sent");

        preSaleWhiteListed[msg.sender] -= _amount;

        for (uint256 i = 0; i < _amount; i++) {
            _safeMint(msg.sender, _tokenIdCounter.current());
            _tokenIdCounter.increment();
        }
    }

    // The following functions are overrides required by Solidity.
    function _beforeTokenTransfer(address from, address to, uint256 tokenId)
        internal
        override(ERC721, ERC721Enumerable)
    {
        super._beforeTokenTransfer(from, to, tokenId);
    }

    function tokenURI(uint256 tokenId) public view override(ERC721) returns (string memory) {
        if (revealedId > 0 && tokenId <= revealedId) {
            return string(
                abi.encodePacked(
                    baseTokenURI,
                    Strings.toString(tokenId),
                    ".json"
                )
            );
        } else {
            return notRevealedUrl;
        }
    }

    function supportsInterface(bytes4 interfaceId)
        public
        view
        override(ERC721, ERC721Enumerable)
        returns (bool)
    {
        return super.supportsInterface(interfaceId);
    }

    function whiteListAddresses(address[] calldata addresses, uint8 numAllowedToMint) external onlyOwner {
        for (uint256 i = 0; i < addresses.length; i++) {
            preSaleWhiteListed[addresses[i]] = numAllowedToMint;
        }
    }

    function setBaseURI(string memory baseURI) public onlyOwner {
        baseTokenURI = baseURI;
    }

    function setNotRevealedURL(string memory _notRevealedUrl) public onlyOwner {
        notRevealedUrl = _notRevealedUrl;
    }

    function setRevealId(uint256 id) public onlyOwner {
        revealedId = id;
    }

    /// @notice Start and stop sell
    function setSaleActive(bool val) external onlyOwner {
        saleActive = val;
    }

    /// @notice In case the price of ether changes a lot
    function setPrice(uint256 _price) external onlyOwner {
        price = _price;
    }

    function setTeamAddresses(address[] calldata _adresses) external onlyOwner {
        team1 = _adresses[0];
        team2 = _adresses[1];
        team3 = _adresses[2];
        communityWallet = _adresses[3];
    }

    /// @notice Withdraw funds from contract for the team and community
    function teamWithdraw(uint256 _amount) external onlyOwner {
        require(_amount <= address(this).balance, "Amount is greater than contract balance");
        uint256 percent = _amount / 100;
        // 25% Split evenly among the team and the community
        require(payable(team1).send(percent * 25));
        require(payable(team2).send(percent * 25));
        require(payable(team3).send(percent * 25));
        require(payable(communityWallet).send(percent * 25));
    }

    function withdraw() external onlyOwner {
        uint balance = address(this).balance;
        payable(msg.sender).transfer(balance);
    }
}
/// @custom:security-contact lesyndicatnft@gmail.com
function setPrice(uint256 _price) external onlyOwner {
    price = _price;
}

/// @notice In case the price of ether changes a lot