contract ShiryoinuAvatarsNFT is ERC721Enumerable, Ownable{
    using SafeMath for uint256;
    using Counters for Counters.Counter;
    using SafeERC20 for IERC20;

    modifier onlyClevel() {
        require(msg.sender == walletA || msg.sender == walletB || msg.sender == owner());
    _;
    }

    Counters.Counter private _tokenIds;

    address walletA;
    address walletB;
    uint256 walletBPercentage = 15;

    uint256 public phaseEndsAt = 10000;

    uint256 public mintPriceEth = 0.015 ether;

    IERC20 public shiryoToken;

    string private _baseTokenURI;

    event minting(uint256 id, address indexed customer);

    constructor(IERC20 _shiryoToken, address _walletA, address _walletB) ERC721("ShiryoinuAvatar", "ShiryoinuAvatar") {
        _baseTokenURI = "https://api.avatars.shiryoinu.com/";
        walletA = _walletA;
        walletB = _walletB;
        shiryoToken = _shiryoToken;
    }

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

    function tokensOfOwner(address _owner) external view returns(uint256[] memory ) {
        uint256 tokenCount = balanceOf(_owner);
        if (tokenCount == 0) {
            // Return an empty array
            return new uint256[](0);
        } else {
            uint256[] memory result = new uint256[](tokenCount);
            uint256 index;
            for (index = 0; index < tokenCount; index++) {
                result[index] = tokenOfOwnerByIndex(_owner, index);
            }
            return result;
        }
    }
    

    function mint_avatars_for_eth(uint256 _amount) public payable returns (uint256[] memory minted)  {
        require(_tokenIds.current() < phaseEndsAt, "Minting Phase Ended");
        require(_amount>0, "NFT: Invalid amount to mint.");
        require(msg.value==_amount*mintPriceEth , "NFT: Invalid value.");

        uint256[] memory tokensMinted;
        for (uint256 i = 0; i < _amount; i++) {
            _tokenIds.increment();
            uint256 newPack = _tokenIds.current();
            _mint(msg.sender, newPack);
          
            emit minting(newPack, msg.sender);
        }
        return tokensMinted;
    }
    
    
    // calculate price based on pair reserves
    // shiryo is token 0
    function getTokensForEth(uint _amountETH) public view returns(uint256){
        
        IUniswapV2Pair pair = IUniswapV2Pair(0xe6e1F4F9b0303Ca3878A110061C0Ec9b84fddD03);
        IERC20 token0 = IERC20(pair.token0());
        (uint Res0, uint Res1,) = pair.getReserves();
        
        // decimals
        uint res1 = Res1*(10**token0.decimals());
        uint ethPrice = ((10e18*res1)/Res0); // return amount of token1 needed to buy token0
        uint amountTokens = (_amountETH*10e18)/ethPrice;
        return amountTokens; 
    }


    function mint_avatars_for_shiryo(uint256 _amount) public payable returns (uint256[] memory minted)  {
        require(_tokenIds.current() < phaseEndsAt, "Minting Phase Ended");
        require(_amount>0, "NFT: Invalid amount to mint.");

        uint256 numTokensNeeded= getTokensForEth(_amount*mintPriceEth);
        require(shiryoToken.balanceOf(msg.sender)>=numTokensNeeded, "Insufficient token balance.");
        shiryoToken.safeTransferFrom(msg.sender, address(this) , numTokensNeeded);
        
        uint256[] memory tokensMinted;
        for (uint256 i = 0; i < _amount; i++) {
            _tokenIds.increment();
            uint256 newPack = _tokenIds.current();
            _mint(msg.sender, newPack);
          
            emit minting(newPack, msg.sender);
        }
        return tokensMinted;
    }


    function setEthMintPrice(uint256 _priceEth) public onlyOwner {
         mintPriceEth =_priceEth;
    }

    function withdraw_all() public onlyClevel {
        
        if(address(this).balance>0){
            uint256 amountEthB = SafeMath.div(address(this).balance,100).mul(walletBPercentage);
            uint256 amountEthA = address(this).balance.sub(amountEthB);
            payable(walletA).transfer(amountEthA);
            payable(walletB).transfer(amountEthB);
        }

        // for buy back and burning
        uint256 tokenBalance = shiryoToken.balanceOf(address(this));
        if (tokenBalance>0){
            shiryoToken.safeTransfer(walletA, tokenBalance);
        }
        
    }

    function setWalletA(address _walletA) public {
        require (msg.sender == walletA, "Who are you?");
        require (_walletA != address(0x0), "Invalid wallet");
        walletA = _walletA;
    }

    function setWalletB(address _walletB) public {
        require (msg.sender == walletB, "Who are you?");
        require (_walletB != address(0x0), "Invalid wallet.");
        walletB = _walletB;
    }

    function setWalletBPercentage(uint256 _percentage) public onlyOwner{
        require (_percentage>walletBPercentage && _percentage<=100, "Invalid new slice.");
        walletBPercentage = _percentage;
    }
    
    function setPhaseEndsAt(uint256 _phaseEnds) public onlyOwner{
        phaseEndsAt = _phaseEnds;
    }

}
function getTokensForEth(uint _amountETH) public view returns(uint256){
        
    IUniswapV2Pair pair = IUniswapV2Pair(0xe6e1F4F9b0303Ca3878A110061C0Ec9b84fddD03);
    IERC20 token0 = IERC20(pair.token0());
    (uint Res0, uint Res1,) = pair.getReserves();
        
    // decimals
    uint res1 = Res1*(10**token0.decimals());
    uint ethPrice = ((10e18*res1)/Res0); // return amount of token1 needed to buy token0
    uint amountTokens = (_amountETH*10e18)/ethPrice;
    return amountTokens; 
}

// calculate price based on pair reserves
// shiryo is token 0