// Contract: MainFD
// Target function: function airdrop() onlyOwner public payable
 *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
        }
    }
}

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }
}

interface IEIP2981 {
    /// ERC165 bytes to add to interface array - set in parent contract
    /// implementing this standard
    ///
    /// bytes4(keccak256("royaltyInfo(uint256,uint256)")) == 0x2a55205a

    /// @notice Called with the sale price to determine how much royalty
    //          is owed and to whom.
    /// @param _tokenId - the NFT asset queried for royalty information
    /// @param _salePrice - the sale price of the NFT asset specified by _tokenId
    /// @return receiver - address of who should be sent the royalty payment
    /// @return royaltyAmount - the royalty payment amount for _salePrice
    function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view returns (address receiver,uint256 royaltyAmount);
}

interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}


contract AnleiUtils
{
    using Strings for uint256;

    event Log(string);

    function random(uint number) public view returns(uint) {
        return uint(keccak256(abi.encodePacked(block.timestamp,block.difficulty, msg.sender))) % number;
    }

    function addressArrayEq(address[] memory arr, address addr) internal pure returns(string memory)
    {
        for(uint256 i=0; i<arr.length; i++)
        {
            if(arr[i] == addr)
            {
                return i.toString();
            }
        }
        return "-1";
    }

    function isEqual(string memory a, string memory b) public pure returns (bool) {
        bytes memory aa = bytes(a);
        bytes memory bb = bytes(b);
        // 如果长度不等，直接返回
        if (aa.length != bb.length) return false;
        // 按位比较
        for(uint i = 0; i < aa.length; i ++) {
            if(aa[i] != bb[i]) return false;
        }
 
        return true;
    }
    

    function parseInt(string memory _a) internal pure returns (uint _parsedInt) {
        return parseInt(_a, 0);
    }

    function parseInt(string memory _a, uint _b) internal pure returns (uint _parsedInt) {
        bytes memory bresult = bytes(_a);
        uint mint = 0;
        bool decimals = false;
        for (uint i = 0; i < bresult.length; i++) {
            if ((uint(uint8(bresult[i])) >= 48) && (uint(uint8(bresult[i])) <= 57)) {
                if (decimals) {
                   if (_b == 0) {
                       break;
                   } else {
                       _b--;
                   }
                }
                mint *= 10;
                mint += uint(uint8(bresult[i])) - 48;
            } else if (uint(uint8(bresult[i])) == 46) {
                decimals = true;
            }
        }
        if (_b > 0) {
            mint *= 10 ** _b;
        }
        return mint;
    }
/*
    function uint2str(uint _i) internal pure returns (string memory _uintAsString) {
        if (_i == 0) {
            return "0";
        }
        uint j = _i;
        uint len;
        while (j != 0) {
            len++;
            j /= 10;
        }
        bytes memory bstr = new bytes(len);
        uint k = len - 1;
        while (_i != 0) {
            bstr[k--] = bytes1(uint8(48 + _i % 10));
            _i /= 10;
        }
        return string(bstr);
    }
    
    function strConcat(string memory _a, string memory _b) internal pure returns (string memory _concatenatedString) {
        return strConcat(_a, _b, "", "", "");
    }

    function strConcat(string memory _a, string memory _b, string memory _c) internal pure returns (string memory _concatenatedString) {
        return strConcat(_a, _b, _c, "", "");
    }

    function strConcat(string memory _a, string memory _b, string memory _c, string memory _d) internal pure returns (string memory _concatenatedString) {
        return strConcat(_a, _b, _c, _d, "");
    }

    function strConcat(string memory _a, string memory _b, string memory _c, string memory _d, string memory _e) internal pure returns (string memory _concatenatedString) {
        bytes memory _ba = bytes(_a);
        bytes memory _bb = bytes(_b);
        bytes memory _bc = bytes(_c);
        bytes memory _bd = bytes(_d);
        bytes memory _be = bytes(_e);
        string memory abcde = new string(_ba.length + _bb.length + _bc.length + _bd.length + _be.length);
        bytes memory babcde = bytes(abcde);
        uint k = 0;
        uint i = 0;
        for (i = 0; i < _ba.length; i++) {
            babcde[k++] = _ba[i];
        }
        for (i = 0; i < _bb.length; i++) {
            babcde[k++] = _bb[i];
        }
        for (i = 0; i < _bc.length; i++) {
            babcde[k++] = _bc[i];
        }
        for (i = 0; i < _bd.length; i++) {
            babcde[k++] = _bd[i];
        }
        for (i = 0; i < _be.length; i++) {
            babcde[k++] = _be[i];
        }
        return string(babcde);
    }
*/

    
}
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}
abstract contract EIP2981AllToken is IEIP2981, ERC165 {

    address internal _royaltyAddr;
    uint256 internal _royaltyPerc; // percentage in basis (out of 10,000)

    /**
    *   @param recipient is the royalty recipient
    *   @param percentage is the royalty percentage
    */
    constructor(address recipient, uint256 percentage) {
        _setRoyaltyInfo(recipient, percentage);
    }
    
    /**
    *   @notice EIP 2981 royalty support
    *   @dev royalty amount not dependent on _tokenId
    */
    function royaltyInfo(uint256 _tokenId, uint256 _salePrice) external view virtual override returns (address receiver, uint256 royaltyAmount) {
        return (_royaltyAddr, _royaltyPerc * _salePrice / 10000);
    }

    /**
    *   @notice override ERC 165 implementation of this function
    *   @dev if using this contract with another contract that suppports ERC 265, will have to override in the inheriting contract
    */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165) returns (bool) {
        return interfaceId == type(IEIP2981).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
    *   @notice function to set royalty information
    *   @dev to be called by inheriting contract
    *   @param addr is the royalty payout address for this token id
    *   @param perc is the royalty percentage (out of 10,000) to set for this token id
    */
    function _setRoyaltyInfo(address addr, uint256 perc) internal virtual {
        require(addr != address(0), "EIP2981AllToken: Cannot set royalty receipient to the zero address");
        require(perc < 10000, "EIP2981AllToken: Cannot set royalty percentage above 10000");
        _royaltyAddr = addr;
        _royaltyPerc = perc;
    }
}

contract OwnerBase
{
    address public owner;
    
    constructor(){
        owner = msg.sender;
    }

    modifier onlyOwner {
        require(msg.sender == owner);
        _;
    }

}


contract PayAbstruct is OwnerBase
{
    constructor()
    {

    }

    //////
    //////  Pay  //////
    //////
    //pay eth to this
    /*function paytocontract() external payable
    {
    }*/
    //this eth to owner
    function withdraw() onlyOwner external payable
    {
        payable(msg.sender).transfer(address(this).balance);
    }
    //get eth total
    function getBalance() public view returns(uint256 num)
    {
        return address(this).balance;
    }
    /*
    function withdraw2(address to) public onlyOwner payable{
        uint256 balance = address(this).balance;
        payable(to).transfer(balance);
    }*/
    
}

contract NFTClass is ERC721A,EIP2981AllToken,OwnerBase,PayAbstruct,DefaultOperatorFilterer {
    using Strings for uint256;

    //NFT json head url
    string private IPFS_URL = "https://nftstorage.link/ipfs/bafybeif6fnkyl4ku2ymuxezc55km2d7hin6oeyi4catmwfnsrvbhm3v644/";
    //Mint param
    uint256 public MAX_SUPPLY = 8888;//total number
    uint256 public USER_LIMIT = 2;//user mint total
    //uint256 public Mint_Price = 0.008 ether;
    uint256 public Mint_Price_normal = 0.008 ether;
    uint256 public Mint_Price_wl = 0.003 ether;

    //white list root - need pay eth
    bytes32 public merkleRoot_wlValue = 0x16858b4922a790eed48063950aba13c87bdf99655bebe69dfcb34ace38c66d70;
    //white list root - free
    bytes32 public merkleRoot_wlFree = 0x3e3e5716f9628f37b1a844c398168372cd96a1043377fc3b6051a452acf77e4e;
    //true = open wl time
    bool public whitelist_switch = true;

    bool private isAirdrop = false;

    //open blind
    //bool public isOpenBlind = false;//true = open blind
    //string public Blind_JSON_URL = "https://bafybeiheymbs6ze7nra3pgwahe2zifq4374v2j3v2zztqdbcufwwxgzppy.ipfs.nftstorage.link/FashionDucksBlindbox.json";



    constructor(
        address royaltyPayout,
        uint256 royaltyPerc,
        string memory nft_name,
        string memory nft_symbol
        )
        ERC721A(nft_name, nft_symbol)
        EIP2981AllToken(royaltyPayout, royaltyPerc)
    {
        //setNFTClass(this);
    }
    
    //////
    //////  NFT  //////
    //////
    function verify_wlValue(bytes32[] memory proof) public view returns(bool result)
    {
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        return MerkleProof.verify(proof, merkleRoot_wlValue, leaf);
    }
    function verify_wlFree(bytes32[] memory proof) public view returns(bool result)
    {
        bytes32 leaf = keccak256(abi.encodePacked(msg.sender));
        return MerkleProof.verify(proof, merkleRoot_wlFree, leaf);
    }
    //
    function getPokerList(address addr) public view returns(uint256[] memory)
    {
        uint256[] memory arr = new uint256[](balanceOf(addr));
        uint256 len = totalSupply();
        uint256 j=0;
        for (uint256 i = _startTokenId(); i <= len; i++) {
            if (ownerOf(i) == addr) {
                arr[j++] = i;
            }
        }
        
        return arr;
    }
    //mint after
    /*function _afterTokenTransfers(
        address from,
        address to,
        uint256 startTokenId,
        uint256 quantity
    ) internal virtual override
    {
        //owner not play game
        if(owner != to) {
            for(uint256 i=startTokenId; i<startTokenId+quantity; i++)
            {
                createPoker(to, i);
            }
        }
    }*/
    //user mint
    function mint(uint256 quantity, bytes32[] memory proof) external payable
    {
        bool wlv = verify_wlValue(proof);
        bool wlf = verify_wlFree(proof);
        //wl onf
        if(whitelist_switch) {
            require(wlv || wlf, "white list time.");
        }
        //not wl
        if(!wlv && !wlf) {
            require(msg.value >= Mint_Price_normal * quantity, "wrong price");
        }
        //is wl
        if(wlv){
            require(msg.value >= Mint_Price_wl * quantity, "wrong price (wl)");
        }
        //go to pay
        require(_totalMinted() + quantity <= MAX_SUPPLY, "Not more supply left");
        require(_numberMinted(msg.sender) + quantity <= USER_LIMIT, "User limit reached");
        //require(balanceOf(msg.sender) + quantity <= USER_LIMIT, "User limit reached");
        

        _safeMint(msg.sender, quantity);
    }
    //owner mint
    function creatorMint(address minter, uint256 quantity) onlyOwner public  payable
    {
        _safeMint(minter, quantity);
    }