abstract contract ERC721 is Context, ERC165, IERC721, IERC721Metadata, IERC721Enumerable 
,ERC721Pausable
,ERC721Fees
{
    using SafeMath for uint256;
    using Address for address;
    using EnumerableSet for EnumerableSet.UintSet;
    using Strings for uint256;
	using EnumerableMap for EnumerableMap.UintToAddressMap;
	
	EnumerableMap.UintToAddressMap internal _tokenOwners;
	
    bytes4 private constant _ERC721_RECEIVED = 0x150b7a02;

    mapping (address => EnumerableSet.UintSet) internal _holderTokens;

    mapping (uint256 => address) private _tokenApprovals;

    mapping (address => mapping (address => bool)) internal _operatorApprovals;

    mapping (uint256 => string) internal _tokenURIs;

    string internal _baseURI;
	
    string private _name;

    string private _symbol;
	
    bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd;

    bytes4 private constant _INTERFACE_ID_ERC721_METADATA = 0x5b5e139f;

    bytes4 private constant _INTERFACE_ID_ERC721_ENUMERABLE = 0x780e9d63;	
    
	constructor (string memory name_, string memory symbol_) 
	 
	ERC721Fees(100,1)
		{
        _name = name_;
        _symbol = symbol_;

        _registerInterface(_INTERFACE_ID_ERC721);
        _registerInterface(_INTERFACE_ID_ERC721_METADATA);
        _registerInterface(_INTERFACE_ID_ERC721_ENUMERABLE);
    }
	
    function _ownerOf(uint256 tokenId) internal view virtual returns (address) {
        return _tokenOwners.get(tokenId);
    }
	
    function _safeTransfer(address from, address to, uint256 tokenId, bytes memory _data) internal virtual {
        _transfer(from, to, tokenId);
        require(_checkOnERC721Received(from, to, tokenId, _data));
    }

    function _exists(uint256 tokenId) internal view virtual returns (bool) {
        return _tokenOwners.contains(tokenId);
    }

    function _isApprovedOrOwner(address spender, uint256 tokenId) internal view virtual returns (bool) {
        require(_exists(tokenId));
        address owner = _ownerOf(tokenId);
        return (spender == owner || getApproved(tokenId) == spender || _operatorApprovals[owner][spender]);
    }

    function _safeMint(address to, uint256 tokenId) internal virtual {
        _safeMint(to, tokenId, "");
    }

    function _safeMint(address to, uint256 tokenId, bytes memory _data) internal virtual {
        _mint(to, tokenId);
        require(_checkOnERC721Received(address(0), to, tokenId, _data));
    }

    function _mint(address to, uint256 tokenId) internal virtual {
        require(to != address(0));
        require(!_exists(tokenId));

        _beforeTokenTransfer(address(0), to, tokenId);

        _holderTokens[to].add(tokenId);

        _tokenOwners.set(tokenId, to);

        emit Transfer(address(0), to, tokenId);
    }

    function _burn(uint256 tokenId) internal virtual {
        address owner = _ownerOf(tokenId); // internal owner

        _beforeTokenTransfer(owner, address(0), tokenId);

        _approve(address(0), tokenId);

        if (bytes(_tokenURIs[tokenId]).length != 0) {
            delete _tokenURIs[tokenId];
        }

        _holderTokens[owner].remove(tokenId);

        _tokenOwners.remove(tokenId);

        emit Transfer(owner, address(0), tokenId);
    }

    function _transfer(address from, address to, uint256 tokenId) internal virtual {
        require(_ownerOf(tokenId) == from);
        require(to != address(0));

        _beforeTokenTransfer(from, to, tokenId);

        // Clear approvals from the previous owner
        _approve(address(0), tokenId);

        _holderTokens[from].remove(tokenId);
        _holderTokens[to].add(tokenId);

        _tokenOwners.set(tokenId, to);

        emit Transfer(from, to, tokenId);
    }

    function _setTokenURI(uint256 tokenId, string memory _tokenURI) internal virtual {
        require(_exists(tokenId));
        _tokenURIs[tokenId] = _tokenURI;
    }

    function _setBaseURI(string memory baseURI_) internal virtual {
        _baseURI = baseURI_;
    }

    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data)
        private returns (bool)
    {
        if (!to.isContract()) {
            return true;
        }
        bytes memory returndata = to.functionCall(abi.encodeWithSelector(
            IERC721Receiver(to).onERC721Received.selector,
            _msgSender(),
            from,
            tokenId,
            _data
        ), "ERC721: transfer to non ERC721Receiver implementer");
        bytes4 retval = abi.decode(returndata, (bytes4));
        return (retval == _ERC721_RECEIVED);
    }

    function _approve(address to, uint256 tokenId) internal {
        _tokenApprovals[tokenId] = to;
        emit Approval(_ownerOf(tokenId), to, tokenId); // internal owner
    }

			function _beforeTokenTransfer(address from, address to, uint256 tokenId) internal virtual {
			require(!paused());
		}
	    
	
    function getApproved(uint256 tokenId) public view virtual override returns (address) {
        require(_exists(tokenId));

        return _tokenApprovals[tokenId];
    }	
	
    function balanceOf(address owner) public view virtual override returns (uint256) {
        require(owner != address(0));
        return _holderTokens[owner].length();
    }
	
    function setBaseURI(string memory baseURI_) onlyOwner public virtual {
        _setBaseURI(baseURI_);
    }	
	
    function baseURI() public view virtual returns (string memory) {
        return _baseURI;
    }
	
    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        require(_exists(tokenId));

        string memory _tokenURI = _tokenURIs[tokenId];
        string memory base = baseURI();

        // If there is no base URI, return the token URI.
        if (bytes(base).length == 0) {
            return _tokenURI;
        }
        // If both are set, concatenate the baseURI and tokenURI (via abi.encodePacked).
        if (bytes(_tokenURI).length > 0) {
            return string(abi.encodePacked(base, _tokenURI));
        }
        // If there is a baseURI but no tokenURI, concatenate the tokenID to the baseURI.
        return string(abi.encodePacked(base, tokenId.toString()));
    }

    function totalSupply() public view virtual override returns (uint256) {
        return _tokenOwners.length();
    }
	

    function tokenByIndex(uint256 index) public view virtual override returns (uint256) {
        (uint256 tokenId, ) = _tokenOwners.at(index);
        return tokenId;
    }
	
    function tokenOfOwnerByIndex(address owner, uint256 index) public view virtual override returns (uint256) {
        return _holderTokens[owner].at(index);
    }
	
    function ownerOf(uint256 tokenId) public view virtual override returns (address) {
        return _ownerOf(tokenId);
    }

    function name() public view virtual override returns (string memory) {
        return _name;
    }

    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }


    function approve(address to, uint256 tokenId) public virtual override {
        address owner = ownerOf(tokenId);
        require(to != owner);

        require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()));

        _approve(to, tokenId);
    }

    /**
     * @dev  See {IERC721-setApprovalForAll}.
     */
    function setApprovalForAll(address operator, bool approved) public virtual override {
        require(operator != _msgSender());

        _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];
    }

    /**
     * @dev  See {IERC721-transferFrom}.
     */
    function transferFrom(address from, address to, uint256 tokenId) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId));
				require(hasAuction(tokenId) == false);
		
        _transfer(from, to, tokenId);
    }

    /**
     * @dev  See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId) public virtual override {
				require(hasAuction(tokenId) == false);
				
        safeTransferFrom(from, to, tokenId, "");
    }

    /**
     * @dev  See {IERC721-safeTransferFrom}.
     */
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public virtual override {
        require(_isApprovedOrOwner(_msgSender(), tokenId));
				require(hasAuction(tokenId) == false);
				
        _safeTransfer(from, to, tokenId, _data);
    }	
	
		struct Offer {
        bool isForSale;
        address seller;
        uint minValue; 
		uint endTime;
    }

    struct Bid {
        bool hasBid;
        address bidder;
        uint value;
    }
	
	// NFT 경매 등록 목록
    mapping (uint256 => Offer) public offers;

    // NFT 입찰 목록
    mapping (uint256 => Bid) public bids;
	
	event CreateAuction(address indexed owner,uint _tokenId, uint _minValue,uint _endTime);
	event CancelAuction(uint _tokenId);
	event EndAuction(uint _tokenId,uint price);
	
	event Bidding(uint _tokenId,uint value);
	event CancelBid(uint _tokenId);
	
	//경매등록
	function _createAuction(uint256 _tokenId, uint _minValue,uint _auctionTime) internal virtual {
		require(_ownerOf(_tokenId) == msg.sender);//토큰 소유자인지 확인
		
		Offer storage offer =  offers[_tokenId];
		require(offer.isForSale != true);//현재 판매중인지 확인
		
        offers[_tokenId] = Offer(true, msg.sender, _minValue,block.timestamp + _auctionTime);
		emit CreateAuction(msg.sender, _tokenId, _minValue,block.timestamp + _auctionTime);
	}
	
	//경매취소
    function _cancelAuction(uint256 _tokenId) internal virtual {
       require(_ownerOf(_tokenId) == msg.sender);//토큰 소유자인지 체크
	   
	   Offer storage offer =  offers[_tokenId];
	   require(offer.isForSale == true);//현재 경매중인지 체크
	   
	   Bid storage bid = bids[_tokenId];
	   require(bid.hasBid != true);//입찰자가 있을경우 경매 취소 불가능

	   offers[_tokenId] = Offer(false, msg.sender, 0,0);
	   
	   emit CancelAuction(_tokenId);
    }
	
	//입찰하기
    function _bid(uint256 _tokenId) internal virtual {
		require(_ownerOf(_tokenId) != msg.sender);//토큰 보유자
		
		Offer storage offer =  offers[_tokenId];
		require(block.timestamp < offer.endTime);//경매가 종료되었을 경우
		require(msg.value >= offer.minValue);//입찰 금액이 최소 입찰액보다 작은지 체크
		
        Bid storage existing = bids[_tokenId];
		require(msg.value > existing.value);//입찰금액이 이전 입찰금액보다 적을경우 트랜잭션 취소
		
        if (existing.value > 0) {
			//이전 입찰자에게 이더리움을 돌려줌
			address payable bidder = payable(existing.bidder);
			bidder.transfer(existing.value);
        }
		
        bids[_tokenId] = Bid(true, msg.sender, msg.value);
		
		emit Bidding(_tokenId,msg.value);
    }	
	
	//입찰취소
    function _cancelBid(uint256 _tokenId) internal virtual {
		Offer storage offer =  offers[_tokenId];
		require(offer.isForSale == true);//경매가 진행중인지 체크
		require(block.timestamp < offer.endTime);//경매가 종료되었을 경우

        Bid storage bid = bids[_tokenId];
		require(bid.hasBid == true);
		require(bid.bidder == msg.sender);//입찰자가 본인인 경우
		
		
		
		address payable bidder = payable(bid.bidder);
		address payable seller = payable(offer.seller);
		
					uint cancelFee = bid.value * cancelFeeRate() / 1000;
			bidder.transfer(bid.value - cancelFee);
			seller.transfer(cancelFee);
				
		bids[_tokenId] = Bid(false, address(0), 0);
		
		emit CancelBid(_tokenId);
    }
	
	//경매종료
    function _endAuction(uint256 _tokenId) internal virtual {
        Offer storage offer =  offers[_tokenId];
		require(block.timestamp >= offer.endTime);//경매 종료 시간이 아닐경우 오류
		require(offer.isForSale == true);//경매가 이미 종료된 경우
		
		address payable seller = payable(_ownerOf(_tokenId));
		
		Bid storage bid = bids[_tokenId];
		_transfer(offer.seller, bid.bidder, _tokenId);

        
					// 수수료
			uint _commissionValue = bid.value * feeRate() / 1000;
			uint _sellerValue = bid.value - _commissionValue;
			
			seller.transfer(_sellerValue);//판매자에게 판매대금 지급
			
			address payable contractOwner = payable(_owner);
			contractOwner.transfer(_commissionValue);//발행자에게 수수료 지급
				
		emit EndAuction(_tokenId,bid.value);
		_resetAuction(_tokenId);
	}	
	
	
	function _resetAuction(uint256 _tokenId) internal virtual {
        offers[_tokenId] = Offer(false, address(0), 0,0);
        bids[_tokenId] = Bid(false, address(0), 0);
	}
	
	
	function hasAuction(uint256 _tokenId) public view virtual returns (bool){
		Offer storage offer =  offers[_tokenId];
		if(offer.isForSale != true){
			return false;
		}
		
		return true;
	}
	
}
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
    return _operatorApprovals[owner][operator];
}

/**
 * @dev  See {IERC721-isApprovedForAll}.
 */