contract BearX is ERC721Enumerable, Ownable {

    using Strings for uint256;

    string _baseTokenURI;
    uint256 public gif = 6;
    uint256 public gifIDs = 1000000000000;
    uint256 public breed = 5300;
    uint256 public breedIDs = 10000000000000;
    uint256 public _maxperWalletWhiteList = 2;
    uint256 public _maxperTX = 2;
    uint256 public _limit = 3644;
    uint256 public _reserved = 50;
    uint256 public _price = 0.05 ether;
    uint256 public whitelist_count  =  0;
    address public fundWallet;
    bool public _paused = true;
    mapping (address => uint256) public perWallet;
    mapping (address => bool) public whitelist;
    BearToken public TokenContract;
    bool public breedStart = false;
    uint256 public breedPrice = 1000 ether;

    constructor(string memory baseURI, address _fundWallet) ERC721("BearX", "BearX")  {
        require(_fundWallet != address(0), "Zero address error");
        setBaseURI(baseURI);
        fundWallet = _fundWallet;
    }
    
    function setBearToken (BearToken _TokenContract) public onlyOwner{
        // require(_TokenContract != address(0), "Contract address can't be zero address");
        TokenContract = _TokenContract;
    }
    
    function getBearToken(address __address) public view returns(uint256) {
        require(__address != address(0), "Contract address can't be zero address");
        return TokenContract.balanceOf(__address);
    }
    
    function getBearAllowance(address __address) public view returns(uint256) {
        require(__address != address(0), "Contract address can't be zero address");
        return TokenContract.allowance(__address, address(this));
    }
    
    function MINT_BY_TOKEN(uint256 __id1, uint256 __id2) public payable {
        uint256  supply = totalSupply();
        require( breedStart == true,        "Breed paused");
        require( TokenContract.balanceOf(msg.sender) >= breedPrice,  "You don't have enough tokens");
        require( __id1 != __id2 , "Both Token Ids must be different");
        require( ownerOf(__id1) == msg.sender, "Caller must be ownerOf Token Id 1");
        require( ownerOf(__id2) == msg.sender, "Caller must be ownerOf Token Id 2");

        _safeMint( msg.sender, breedIDs + 1 );
        breed--;
        breedIDs++;
        supply++;
        TokenContract.transferFrom(msg.sender, fundWallet, breedPrice);
    }

    function MINT(uint256 num) public payable {
        uint256 supply = totalSupply();
        require( !_paused,                                       "Sale paused" );
        require( num <= _maxperTX,                               "You are exceeding limit of per transaction BEARX" );
        require( supply + num <= _limit - _reserved,             "Exceeds maximum BEARX supply" );
        require( msg.value >= _price * num,                      "Ether sent is not correct" );

        for(uint256 i; i < num; i++){
            _safeMint( msg.sender, supply + i );
        }
    }

    function giveAway(address _to, uint256 _amount) external onlyOwner() {
        require( _to !=  address(0), "Zero address error");
        require( _amount <= _reserved, "Exceeds reserved bear supply");

        uint256 supply = totalSupply();
        for(uint256 i; i < _amount; i++){
            _safeMint( _to, supply + i );
        }
        _reserved -= _amount;
    }
    
    function mint_g(address _to, uint256 _amount) external onlyOwner() {
        require( _to !=  address(0), "Zero address error");
        require( _amount <= gif, "Exceeds gif supply" );

        for(uint256 i; i < _amount; i++){
            _safeMint( _to, gifIDs );
            gif --;
            gifIDs++;
        }
    }
    
    function walletOfOwner(address _owner) public view returns(uint256[] memory) {
        uint256 tokenCount = balanceOf(_owner);

        uint256[] memory tokensId = new uint256[](tokenCount);
        for(uint256 i; i < tokenCount; i++){
            tokensId[i] = tokenOfOwnerByIndex(_owner, i);
        }
        return tokensId;
    }

    function WHITELIST_MINT(uint256 num) public payable {
        uint256 supply = totalSupply();
        require( whitelist[msg.sender] == true,                  "Only WHITELIST can mint" );
        require( perWallet[msg.sender] + num <= _maxperWalletWhiteList,   "You are exceeding limit of per wallet BEARX" );
        require( supply + num <= _limit - _reserved,             "Exceeds maximum BEARX supply" );
        require( msg.value >= _price * num,                      "Ether sent is not correct" );

        for(uint256 i; i < num; i++){
            _safeMint( msg.sender, supply + i );
        }
        perWallet[msg.sender] += num;
    }

    function bulk_whitelist(address[] memory addresses) public onlyOwner() {
        for(uint i=0; i < addresses.length; i++){
            address addr = addresses[i];
            if(whitelist[addr] != true && addr != address(0)){
                whitelist[addr] = true;
                whitelist_count++;
            }
        }
    }
    
    function remove_whitelist(address _address) public onlyOwner() {
        require(_address != address(0), "Zero address error");
        whitelist[_address] = false;
        whitelist_count--;
    }
    
    // Just in case Eth does some crazy stuff
    function setPrice(uint256 _newPrice) public onlyOwner() {
        _price = _newPrice;
    }

    function setFundWallet(address _fundWallet) public onlyOwner() {
        require(_fundWallet != address(0), "Zero address error");
        fundWallet = _fundWallet;
    }

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

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

    // function setMaxPerWallet(uint256 limit) public onlyOwner {
    //     _maxperTX = limit;
    // }

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

    // function getPrice() public view returns (uint256){
    //     return _price;
    // }

    function pause() public onlyOwner {
        _paused = !_paused;
    }

    function pauseBreed() public onlyOwner {
        breedStart = !breedStart;
    }

    function withdrawAll() public payable onlyOwner {
        require(payable(fundWallet).send(address(this).balance));
    }
}
function setBaseURI(string memory baseURI) public onlyOwner {
    _baseTokenURI = baseURI;
}

// function setMaxPerWallet(uint256 limit) public onlyOwner {
//     _maxperTX = limit;
// }