contract NFTRANKPRO is ERC721, Ownable {
    using SafeMath for uint256; // 安全计算的函数
    OwnersMapping.itmap data; // 下边的map的增删改查

    //data
    function insert(address key, uint typ) internal returns (bool)
    {
        return OwnersMapping.insert(data, key, typ);
    }

    function isLimit(address key) public view returns (bool)
    {
        return OwnersMapping.isLimit(data, key);
    }

    function isLimitAdd(address key, uint256 amount) public view returns (bool)
    {
        return OwnersMapping.isLimitAdd(data, key, amount);
    }

    function getType(address key) public view returns (uint)
    {
        return OwnersMapping.getType(data, key);
    }

    function add(address key, uint256 tokenId) internal returns (bool)
    {
        return OwnersMapping.add(data, key, tokenId);
    }

    function del(address key, uint256 tokenId) internal returns (bool)
    {
        return OwnersMapping.del(data, key, tokenId);
    }

    function contains(address key) public view returns (bool)
    {
        return OwnersMapping.contains(data, key);
    }

    function getTokenIds(address key) public returns (uint256[] memory)
    {

        uint256[]  memory tokenIds = OwnersMapping.getTokenIds(data, key);
        uint256[] memory allIds = new uint256[](tokenIds.length);
        for (uint256 i; i < tokenIds.length; i++) {
            if (ERC721.ownerOf(tokenIds[i]) == key) {
                allIds[i] = tokenIds[i];
            }
        }
        return allIds;
    }


    mapping(uint256 => uint256) private priceMap; // 价格map
    mapping(uint256 => uint256) private amountMap; // 数量map
    uint256 public immutable maxPerTx; // 每次买多少的限量。immutable 这个参数是不可变的意思，带上相当于常量，不带相当于变量
    uint256 public  maxNumber; //发型最大数量，通过每次更改这个数量，相当于释放多少


    bool public isSaleActive;

    constructor()
    public
    ERC721("nftrank", "nftrank") // erc721协议的构造函数，第一个name名称，第二个简称
    {
        maxNumber = 10000;
        // 最大数量  可以通过下的setNumber 持有人修改
        maxPerTx = 10;
        // 每次最多付费买几个
        priceMap[1] = 1 * 10 ** 17;
        // 付费的单价  也可以通过下边的setPrice设置
    }

    // 只有合约创建人更改价格
    function setPrice(uint256 rate) public onlyOwner {
        priceMap[1] = rate * 10 ** 16;
    }

    // 只有合约创建人才可以设置最大数量
    function setNumber(uint256 maxnumber) public onlyOwner {
        maxNumber = maxnumber;
    }

    //自己挖   批量空头
    function mintOnly(address[] memory addList, uint256[] memory numberOfTokensList) public onlyOwner {
        for (uint i = 0; i < addList.length; i++) {// 循环地址的map
            address newOwner = addList[i];
            // 第一个地址
            uint256 numberOfTokens = numberOfTokensList[i];
            // 第一个数量
            if (numberOfTokens > maxPerTx) {//数量大于10  禁止交易
                continue;
            } else {
                for (uint256 i = 0; i < numberOfTokens; i++) {//循环数量
                    uint256 mintIndexN = totalSupply().add(1);
                    // 设置索引
                    if (totalSupply() < maxNumber) {//总量要小于我们设置的大小  随时监测
                        _tokenPaySet(newOwner, mintIndexN);
                        //
                        _safeMint(newOwner, mintIndexN);
                    }
                }
            }
        }
    }

    // eixt  true  nonexit  false   判断tokenid是否已经被挖
    function isExitA(uint256 tokenId) public view returns (bool) {
        if (_exists(tokenId)) {
            return true;
        } else {
            return false;
        }
    }


    //白名单免费挖
    function mintWhite(uint256 numberOfTokens) public {
        //未开启预售
        require(isSaleActive, "Sale is not active");
        //每次最多买10个
        require(numberOfTokens <= 10, "onece is limt 10");
        // 该白名单用户是否已经受限，比如已经购买了3个了
        require(isLimitAdd(msg.sender, numberOfTokens), "in white list and already by over ,now can not buy");
        //判断是否是白名单
        require(contains(msg.sender), "not in white list and now can not buy");
        // 判断总量是否已经超过设定的总量
        require(totalSupply().add(numberOfTokens) <= maxNumber, "Purchase would exceed max supply of Martial");
        for (uint256 i = 0; i < numberOfTokens; i++) {
            uint256 mintIndex = totalSupply().add(1);
            //顺序递增
            _tokenWhitesSet(msg.sender, mintIndex);
            // 一个map 记录白名单的数量
            if (totalSupply() < maxNumber) {
                add(msg.sender, mintIndex);
                _safeMint(msg.sender, mintIndex);
            }
        }
    }

    //设置白名单列表（地址+最大数量）
    function setWhites(address[] memory addList, uint[] memory typeList) public onlyOwner {
        for (uint i = 0; i < addList.length; i++) {
            if (contains(addList[i])) {// 如果已经包含了 则跳过，也就是重复添加的
                continue;
            }
            insert(addList[i], typeList[i]);
            // 不存在 就增加
        }
    }


    // baseuri  就是 腾讯云cos对象 json文件的地址  第1432行，就是拼接的的tokenid和url的地址作为token指向地址。
    // 这个函数也是只有合约创建人自己可以使用
    function setBaseURI(string memory baseURI) public onlyOwner {
        _setBaseURI(baseURI);
    }

    // 这是个开关，这个开关，是控制售卖的开关，只有合约创建人自己可以调用
    function flipSaleState() public onlyOwner {
        isSaleActive = !isSaleActive;
    }

}
function setNumber(uint256 maxnumber) public onlyOwner {
    maxNumber = maxnumber;
}

// 只有合约创建人才可以设置最大数量