contract SaleClockAuction is Pausable {
  bool public isSaleClockAuction = true;

  struct Auction {
    address seller;
    uint128 startingPrice;  //wei
    uint128 endingPrice;    //wei
    uint64 duration; //秒
    uint64 startedAt; //出售开始时间，如果出售终止则为0
  }

  ERC721Basic public nonFungibleContract;    //NFT 合约地址

  uint256 public commission;    //交易佣金，按交易总额的百分比收取。0 - 10000 表示0% - 100%
  mapping (uint256 => Auction) tokenIdToAuction;   //mapping 所有参与竞拍的跑图ID
  mapping (address => uint256[]) public ownershipAuctionTokenIDs;   //记录用户当前正在拍卖的全部跑图ID

  event AuctionCreated(uint256 tokenId, uint256 startingPrice, uint256 endingPrice, uint256 duration);
  event AuctionSuccessful(uint256 tokenId, uint256 totalPrice, address winner);
  event AuctionCancelled(uint256 tokenId);

  bytes4 constant InterfaceSignature_ERC721 = 0x80ac58cd;

  constructor(address _nftAddress, uint256 _commission) public {
    require(_commission <= 10000);
    commission = _commission;
   
    ERC721Basic candidateContract = ERC721Basic(_nftAddress);
    require(candidateContract.implementsERC721());
    require(candidateContract.supportsInterface(InterfaceSignature_ERC721));
    nonFungibleContract = candidateContract;
  }

  function () external {}	//不直接接收数字货币

  //输入参数合法性校验
  modifier canBeStoredWith64Bits(uint256 _value){
    require(_value <= 18446744073709551615);
    _;
  }

  modifier canBeStoredWith128Bits(uint256 _value){
    require(_value < 340282366920938463463374607431768211455);
    _;
  }

  function _owns(address _claimant, uint256 _tokenId) internal view returns (bool){
    return (nonFungibleContract.ownerOf(_tokenId) == _claimant);
  }

  function _escrow(address _owner, uint256 _tokenId) internal {
    nonFungibleContract.transferFrom(_owner, this, _tokenId);
    //增加-保存用户正在拍卖的跑图ID集合
    ownershipAuctionTokenIDs[_owner].push(_tokenId);
  }

  //添加竞拍（会将跑图从当前主人地址转移到拍卖合约账户下，冻结）
  function _addAuction(uint256 _tokenId, Auction _auction) internal {
    require(_auction.duration >= 1 minutes);

    tokenIdToAuction[_tokenId] = _auction;

    emit AuctionCreated(
      uint256(_tokenId),
      uint256(_auction.startingPrice),
      uint256(_auction.endingPrice),
      uint256(_auction.duration)
    );
  }

  //取消拍卖
  function _cancelAuction(uint256 _tokenId, address _seller) internal {
    _removeAuction(_tokenId);
    nonFungibleContract.transfer(_seller, _tokenId);
    //将取消拍卖的tokentID从用户正在拍卖的跑图ID集合列表里删除
    removeFromOwnershipAuctionTokenIDs(_seller, _tokenId);
    
    emit AuctionCancelled(_tokenId);
  }

  //从用户正在拍卖的跑图ID集合列表里删除
  function removeFromOwnershipAuctionTokenIDs(address seller, uint256 tokenId) internal {
    uint len = ownershipAuctionTokenIDs[seller].length;
    if(len > 0){
      bool hasFound = false;
      for(uint i=0; i<len-1; i++){
        if(!hasFound && ownershipAuctionTokenIDs[seller][i] == tokenId){
          hasFound = true;
          ownershipAuctionTokenIDs[seller][i] = ownershipAuctionTokenIDs[seller][i+1];
        }else if(hasFound){
          ownershipAuctionTokenIDs[seller][i] = ownershipAuctionTokenIDs[seller][i+1];
        }
      }

      if(!hasFound && ownershipAuctionTokenIDs[seller][len - 1] == tokenId){  //如果最后一个元素才是要删除的
        hasFound = true;
      }
      
      if(hasFound){
        delete ownershipAuctionTokenIDs[seller][len-1];
        ownershipAuctionTokenIDs[seller].length--; //需要将数组的长度减一
      }
    }
  }

  function _bid(uint256 _tokenId, uint256 _bidAmount) internal returns(uint256){
    Auction storage auction = tokenIdToAuction[_tokenId];

    require(_isOnAuction(auction));

    uint256 price = _currentPrice(auction);
    require(_bidAmount >= price);

    address seller = auction.seller;
    _removeAuction(_tokenId);

    //将取消拍卖的tokentID从用户正在拍卖的跑图ID集合列表里删除
    removeFromOwnershipAuctionTokenIDs(seller, _tokenId);
    
    //向出售者支付售卖所得。
    if(price > 0) {
      uint256 auctioneerCommission = _computeCommission(price);
      uint256 sellerProceeds = price - auctioneerCommission;

      seller.transfer(sellerProceeds);
    }

    //将余额还给出售者
    uint256 bidExcess = _bidAmount - price;
    msg.sender.transfer(bidExcess);

    emit AuctionSuccessful(_tokenId, price, msg.sender);

    return price;
  } 

  function _removeAuction(uint256 _tokenId) internal {
    delete tokenIdToAuction[_tokenId];
  }

  function _isOnAuction(Auction storage _auction) internal view returns (bool){
    return (_auction.startedAt > 0);
  }

  function _currentPrice(Auction storage _auction) internal view returns (uint256) {
    uint256 secondsPassed = 0;
    
    if(now > _auction.startedAt){
      secondsPassed = now - _auction.startedAt;
    }

    return _computeCurrentPrice(
      _auction.startingPrice,
      _auction.endingPrice,
      _auction.duration,
      secondsPassed
    );
  }

  //根据拍卖时间计算当前跑图价格
  function _computeCurrentPrice(
    uint256 _startingPrice,
    uint256 _endingPrice,
    uint256 _duration,
    uint256 _secondsPassed
  ) internal pure returns (uint256){
    
    if(_secondsPassed >= _duration){	//如果超过竞拍时间，直接取最小价格
      return _endingPrice;  
    } else {
      int256 totalPriceChange = int256(_endingPrice) - int256(_startingPrice);
      int256 currentPriceChange = totalPriceChange * int256(_secondsPassed) / int256(_duration);

      int256 currentPrice = int256(_startingPrice) + currentPriceChange;

      return uint256(currentPrice); 
    }
  }

  //计算跑图出售佣金
  function _computeCommission(uint256 _price) internal view returns (uint256) { 
    return _price * commission / 10000;
  }

  //提取账户资金
  function withdrawBalance() external {
    address nftAddress = address(nonFungibleContract);  
    require(msg.sender == owner || msg.sender == nftAddress);

    nftAddress.transfer(address(this).balance);
  }

  //创建拍卖
  function createAuction(
    uint256 _tokenId,
    uint256 _startingPrice,
    uint256 _endingPrice,
    uint256 _duration,
    address _seller
  ) public
    canBeStoredWith128Bits(_startingPrice)
    canBeStoredWith128Bits(_endingPrice)
    canBeStoredWith64Bits(_duration)
  {
    require(msg.sender == address(nonFungibleContract));
    require(_owns(_seller, _tokenId));
    _escrow(_seller, _tokenId);
    Auction memory auction = Auction(
      _seller,
      uint128(_startingPrice),
      uint128(_endingPrice),
      uint64(_duration),
      uint64(now)
    );
    _addAuction(_tokenId, auction);
  }

  function bid(uint256 _tokenId) public payable whenNotPaused {
    _bid(_tokenId, msg.value);
    nonFungibleContract.transfer(msg.sender, _tokenId);
  }

  function cancelAuction(uint256 _tokenId) public {
    Auction storage auction = tokenIdToAuction[_tokenId];
    require(_isOnAuction(auction));
    address seller = auction.seller;
    require(msg.sender == seller);

    _cancelAuction(_tokenId, seller);
  }

  function cancelAuctionWhenPaused(uint256 _tokenId) public onlyOwner whenPaused {
    Auction storage auction = tokenIdToAuction[_tokenId];
    require(_isOnAuction(auction));
    _cancelAuction(_tokenId, auction.seller);
  }

  function getAuction(uint256 _tokenId) public view returns(
    address seller,
    uint256 startingPrice,
    uint256 endingPrice,
    uint256 duration,
    uint256 startedAt
  ){
    Auction storage auction = tokenIdToAuction[_tokenId];
    require(_isOnAuction(auction));
    return(
      auction.seller,
      auction.startingPrice,
      auction.endingPrice,
      auction.duration,
      auction.startedAt
    );
  }

  function getCurrentPrice(uint256 _tokenId) public view returns (uint256){
    Auction storage auction = tokenIdToAuction[_tokenId];
    require(_isOnAuction(auction));
    return _currentPrice(auction);
  }

  function getFund() public view returns (uint256 balance){
    return address(this).balance;
  }

  //获取用户当前所有正在拍卖的跑图ID
  function getAuctionTokenIDsOfOwner(address owner) public view returns(uint256[]){
    return ownershipAuctionTokenIDs[owner];
  }
}
function getAuctionTokenIDsOfOwner(address owner) public view returns(uint256[]){
  return ownershipAuctionTokenIDs[owner];
}

//获取用户当前所有正在拍卖的跑图ID