// Contract: ETDOG
// Target function: function transferFrom(
 for tokenomics.
        uint64 numberBurned;
        // For miscellaneous variable(s) pertaining to the address
        // (e.g. number of whitelist mint slots used).
        // If there are multiple variables, please pack them into a uint64.
        uint64 aux;
    }

    // The tokenId of the next token to be minted.
    uint256 internal _currentIndex;

    // The number of tokens burned.
    uint256 internal _burnCounter;

    // Token name
    string private _name;

    // Token symbol
    string private _symbol;

    bool public isSaleActive;
    bool public isSaleActiveWhite;
    bool public isSaleActiveMarket;



    // Mapping from token ID to ownership details
    // An empty struct value does not necessarily mean the token is unowned. See _ownershipOf implementation for details.
    mapping(uint256 => TokenOwnership) internal _ownerships;

    // Mapping owner address to address data
    mapping(address => AddressData) private _addressData;

    // Mapping from token ID to approved address
    mapping(uint256 => address) private _tokenApprovals;

    // Mapping from owner to operator approvals
    mapping(address => mapping(address => bool)) private _operatorApprovals;

    mapping(uint256 => address) private firstOwners;

    address public bossAddr;// todo zero address
    address public trainsAddr;// todo zero address
    uint256 public bossFee; // todo
    uint256 public firstOwnerFee;// todo
    mapping(uint256 => uint256) public idPrice; // todo tokenID Price




    constructor() {
        _name = "ETDOG";
        _symbol = "ETDOG";
        maxNumber = 10000;
        //
        maxPerTx = 10;
        //
        priceMap[1] = 1 * 10 ** 16;
        //
        bossAddr = msg.sender;
        bossFee = 2;
        firstOwnerFee = 2;
        _currentIndex = _startTokenId();
    }

    function getFirstMint(uint256 tokenId) public view returns (address){
        return firstOwners[tokenId];
    }



    function getFlipSaleState() public view returns (bool) {
        return isSaleActive;
    }

    function getSell(uint256 id) public view returns(bool){
        return (idPrice[id]!=0);
    }

    function getSalePrice(uint256 id) public view returns(uint256){
        return idPrice[id] / (1* 10 ** 14);
    }

    //
    function setPrice1(uint256 rate) public onlyOwner {
        //require(msg.sender == owneraddr, "not owner");
        priceMap[1] = rate * 10 ** 16;
    }

    //
    function setPrice2(uint256 rate) public onlyOwner {
        //require(msg.sender == owneraddr, "not owner");
        priceMap[2] = rate * 10 ** 16;
    }


    function setBossFee(uint256 fee) public onlyOwner {
        bossFee = fee;
    }

    function setBossAddr(address addr) public onlyOwner {
        bossAddr = addr;
    }

    function setTransAddr(address addr) public onlyOwner {
        trainsAddr = addr;
    }


    function setFirstOwnerFee(uint256 fee) public onlyOwner {
        firstOwnerFee = fee;
    }

    //
    function setPrice(uint256 rate) public onlyOwner {
        priceMap[1] = rate * 10 ** 16;
    }


    // todo Sell
    function doSell(uint256 tokenId, uint256 sPrice) public {
        require(isSaleActive, "Sale is not active");
        require(idPrice[tokenId] == 0, "tokenId already In Sell Order");
        require(ownerOf(tokenId) == msg.sender, "tokenId is not owner");
        //set owner
        approve(trainsAddr, tokenId);
        idPrice[tokenId] = sPrice * 10 ** 14;
    }

    // todo do cancel
    function doCancel(uint256 tokenId) public {
        require(idPrice[tokenId] != 0, "sell Out or not Sell");
        idPrice[tokenId] = 0;
    }

    // todo do Sell
    function doBuy(uint256 tokenId, address buyAddr) payable public {
        require(isSaleActiveMarket, "Sale is not active");
        //(nftAddr == _msgSender(), "Ownable: caller is not the nftAddr");
        require(idPrice[tokenId] != 0, "sell Out or not Sell");
        require(ownerOf(tokenId) != buyAddr, "seller can not be yourself");

        Trains trains = Trains(trainsAddr);
        trains.doTransfer(ownerOf(tokenId), buyAddr, tokenId);
        //transferFrom(ownerOf(tokenId), buyAddr, tokenId);
        address firstOwner = getFirstMint(tokenId);
        require(msg.value >= idPrice[tokenId]);
        payable(bossAddr).transfer(msg.value * bossFee / 100);
        payable(firstOwner).transfer(msg.value * firstOwnerFee / 100);
        payable(ownerOf(tokenId)).transfer(msg.value * (100 - bossFee - firstOwnerFee) / 100);
        idPrice[tokenId] = 0;
        //sellOrder[idAddr[tokenId]].push(tokenId);
        //sellOrderPrice[idAddr[tokenId]][tokenId] = msg.value * 90 / 100;
    }


    //
    function getSellList(uint256 total) public view returns (uint256[] memory){
        uint256[] memory SellList = new uint256[](total + 1);
        for (uint256 i = 0; i < SellList.length; i++) {
            if (idPrice[i] != 0) {
                SellList[i] = i;
            } else {
                SellList[i] = 0;
            }
        }
        return SellList;
    }


    //
    function getSellPriceList(uint256 total) public view returns (uint256[] memory){
        uint256[] memory SellPriceList = new uint256[](total + 1);
        for (uint256 i = 0; i < SellPriceList.length; i++) {
            if (idPrice[i] != 0) {
                SellPriceList[i] = idPrice[i] / (10 ** 14);
            } else {
                SellPriceList[i] = 0;
            }
        }
        return SellPriceList;
    }

    //
    function getMySelling(uint256 total, address owner) public view returns (uint256[] memory){
        uint256[] memory MySellingList = new uint256[](total + 1);
        for (uint256 i = 0; i < MySellingList.length; i++) {
            if (idPrice[i] != 0 && ownerOf(i) == owner) {
                MySellingList[i] = i;
            } else {
                MySellingList[i] = 0;
            }
        }
        return MySellingList;
    }

    //
    function setNumber(uint256 maxnumber) public onlyOwner {
        // require(msg.sender == owneraddr, "not owner");
        maxNumber = maxnumber;
    }


    function mintWhite(uint256 numberOfTokens) public payable {
        //
        require(isSaleActiveWhite, "Sale is not active");
        //
        require(numberOfTokens <= 10, "onece is limt 10");
        //
        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");
        //
        uint disa = getDis(msg.sender);

        uint256 price = priceMap[1];
        require(price.mul(numberOfTokens).mul(disa) / 100 <= msg.value, "Ether value is not correct");
        payable(bossAddr).transfer(msg.value);
        for (uint256 i = 0; i < numberOfTokens; i++) {
            uint256 mintIndex = totalSupply().add(1);
            if (totalSupply() < maxNumber) {//
                firstOwners[mintIndex] = msg.sender;
                add(msg.sender, mintIndex);
            }
        }
        _safeMint(_msgSender(), numberOfTokens);
    }

    //pay  pay
    function mintPay(uint256 numberOfTokens) public payable {
        require(isSaleActive, "Sale is not active");
        require(numberOfTokens <= maxPerTx, "No more than 10 tokens per transaction");
        require(totalSupply().add(numberOfTokens) <= maxNumber, "Purchase would exceed max supply of Martial");
        uint256 price = priceMap[1];

        require(price.mul(numberOfTokens) == msg.value, "Ether value is not correct");
        payable(bossAddr).transfer(msg.value);
        for (uint256 i = 0; i < numberOfTokens; i++) {
            uint256 mintIndex = totalSupply().add(1);
            if (totalSupply() < maxNumber) {//
                firstOwners[mintIndex] = msg.sender;
            }
        }
        _safeMint(_msgSender(), numberOfTokens);
    }


    //
    function mintOnly(address[] memory addList, uint256[] memory numberOfTokensList) public onlyOwner {
        for (uint i = 0; i < addList.length; i++) {//
            address newOwner = addList[i];
            //
            uint256 numberOfTokens = numberOfTokensList[i];
            require(totalSupply().add(numberOfTokens) <= maxNumber, "Purchase would exceed max supply of Martial");
            //
            if (numberOfTokens > maxPerTx) {//
                continue;
            } else {
                if ((totalSupply() + numberOfTokens) >= maxNumber) {
                    return;
                }
                for (uint256 i = 0; i < numberOfTokens; i++) {
                    uint256 mintIndexN = totalSupply().add(1);
                    firstOwners[mintIndexN] = newOwner;
                }
                _safeMint(newOwner, numberOfTokens);
            }
        }
    }



    // eixt  true  nonexit  false
    function isExitA(uint256 tokenId) public view returns (bool) {
        if (_exists(tokenId)) {
            return true;
        } else {
            return false;
        }
    }


    // baseuri
    //
    function setBaseURI(string memory baseURI) public onlyOwner {
        // require(msg.sender == owneraddr, "not owner");
        _setBaseURI(baseURI);
    }

    //
    function flipSaleState() public onlyOwner {
        isSaleActive = !isSaleActive;
    }
    //
    function flipSaleWhiteState() public onlyOwner {
        isSaleActiveWhite = !isSaleActiveWhite;
    }

    function flipSaleActiveMarket() public onlyOwner {
        isSaleActiveMarket = !isSaleActiveMarket;
    }


    function getFlips() public view returns (bool,bool,bool){
        return (isSaleActive,isSaleActiveWhite,isSaleActiveMarket);
    }





    /**
     * To change the starting tokenId, please override this function.
     */
    function _startTokenId() internal view virtual returns (uint256) {
        return 1;
    }

    /**
     * @dev Burned tokens are calculated here, use _totalMinted() if you want to count just minted tokens.
     */
    function totalSupply() public view returns (uint256) {
        // Counter underflow is impossible as _burnCounter cannot be incremented
        // more than _currentIndex - _startTokenId() times
    unchecked {
        return _currentIndex - _burnCounter - _startTokenId();
    }
    }

    /**
     * Returns the total amount of tokens minted in the contract.
     */
    function _totalMinted() internal view returns (uint256) {
        // Counter underflow is impossible as _currentIndex does not decrement,
        // and it is initialized to _startTokenId()
    unchecked {
        return _currentIndex - _startTokenId();
    }
    }

    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165, IERC165) returns (bool) {
        return
        interfaceId == type(IERC721).interfaceId ||
        interfaceId == type(IERC721Metadata).interfaceId ||
        super.supportsInterface(interfaceId);
    }

    /**
     * @dev See {IERC721-balanceOf}.
     */
    function balanceOf(address owner) public view override returns (uint256) {
        if (owner == address(0)) revert BalanceQueryForZeroAddress();
        return uint256(_addressData[owner].balance);
    }

    /**
     * Returns the number of tokens minted by `owner`.
     */
    function _numberMinted(address owner) internal view returns (uint256) {
        return uint256(_addressData[owner].numberMinted);
    }

    /**
     * Returns the number of tokens burned by or on behalf of `owner`.
     */
    function _numberBurned(address owner) internal view returns (uint256) {
        return uint256(_addressData[owner].numberBurned);
    }

    /**
     * Returns the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
     */
    function _getAux(address owner) internal view returns (uint64) {
        return _addressData[owner].aux;
    }

    /**
     * Sets the auxillary data for `owner`. (e.g. number of whitelist mint slots used).
     * If there are multiple variables, please pack them into a uint64.
     */
    function _setAux(address owner, uint64 aux) internal {
        _addressData[owner].aux = aux;
    }

    /**
     * Gas spent here starts off proportional to the maximum mint batch size.
     * It gradually moves to O(1) as tokens get transferred around in the collection over time.
     */
    function _ownershipOf(uint256 tokenId) internal view returns (TokenOwnership memory) {
        uint256 curr = tokenId;

    unchecked {
        if (_startTokenId() <= curr && curr < _currentIndex) {
            TokenOwnership memory ownership = _ownerships[curr];
            if (!ownership.burned) {
                if (ownership.addr != address(0)) {
                    return ownership;
                }
                // Invariant:
                // There will always be an ownership that has an address and is not burned
                // before an ownership that does not have an address and is not burned.
                // Hence, curr will not underflow.
                while (true) {
                    curr--;
                    ownership = _ownerships[curr];
                    if (ownership.addr != address(0)) {
                        return ownership;
                    }
                }
            }
        }
    }
        revert OwnerQueryForNonexistentToken();
    }

    /**
     * @dev See {IERC721-ownerOf}.
     */
    function ownerOf(uint256 tokenId) public view override returns (address) {
        return _ownershipOf(tokenId).addr;
    }

    function getMyList(uint256 amount,address addr) public view returns (uint256[] memory){
        uint256 totalS  = totalSupply();
        uint256 j = 0;
        uint256[] memory SellPriceList = new uint256[](amount);
        for (uint256 i = 0; i < totalS; i++) {
            if (ownerOf(i+1) != addr) {
                continue;
            } else {
                SellPriceList[j] = i+1;
                j +=1;
            }
        }
        return SellPriceList;
    }



    /**
     * @dev See {IERC721Metadata-name}.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev See {IERC721Metadata-symbol}.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    function getPrice() public view returns (uint256) {
        return priceMap[1] / (10 ** 16);
    }

    /**
     * @dev See {IERC721Metadata-tokenURI}.
     */
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        if (!_exists(tokenId)) revert URIQueryForNonexistentToken();

        string memory baseURI = _baseURI();
        return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, tokenId.toString(), ".json")) : '';
    }

    /**
     * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each
     * token will be the concatenation of the `baseURI` and the `tokenId`. Empty
     * by default, can be overriden in child contracts.
     */
    function _baseURI() internal view virtual returns (string memory) {
        return _baseURIS;
    }

    /**
     * @dev See {IERC721-approve}.
     */
    function approve(address to, uint256 tokenId) public override {
        address owner = ETDOG.ownerOf(tokenId);
        if (to == owner) revert ApprovalToCurrentOwner();

        if (_msgSender() != owner && !isApprovedForAll(owner, _msgSender())) {
            revert ApprovalCallerNotOwnerNorApproved();
        }

        _approve(to, tokenId, owner);
    }

    /**
     * @dev See {IERC721-getApproved}.
     */
    function getApproved(uint256 tokenId) public view override returns (address) {
        if (!_exists(tokenId)) revert ApprovalQueryForNonexistentToken();

        return _tokenApprovals[tokenId];
    }

    /**
     * @dev See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        if (operator == _msgSender()) revert ApproveToCaller();

        _operatorApprovals[_msgSender()][operator] = approved;
        emit ApprovalForAll(_msgSender(), operator, approved);
    }

    /**
     * @dev See {IERC721-isApprovedForAll}.
     */
    function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
        return _operatorApprovals[owner][operator];
    }