contract ChainDrawingsBase is ChainDrawingsAccess, SupportsInterfaceWithLookup, ERC721BasicToken, ERC721Enumerable, ERC721Metadata {
  using SafeMath for uint256;
  using AddressUtils for address;

  string internal name_ = "LianPaoTu";
  string internal symbol_ = "LPT";
  
  //Event
  event Create(address owner, uint256 drawingsID, bytes32 chainID);

  //跑图数据结构-目前保存跑图ID和跑图原创者名称。跑图属性、跑图gps数据、跑图示例图片不适合放在以太坊链，需要放在Fabric或IPFS系统里
  struct ChainDrawings {
    bytes32 chainID;		//链跑图ID。（对应链跑图ID、创意跑步App内跑图ID）（utf8转码必须是bytes32 或 bytes）
    bytes32 author;	//原创者名称
    uint64 createTime;	//链跑图创建时间
  }

  //链跑图数组
  ChainDrawings[] drawings;

  mapping (bytes32 => uint256) public chainIDToTokenID;   //chainID对应的链跑图ID，用来方便查找和判断跑图是否已上链 (tokenID)
  mapping (uint256 => string) internal tokenIDToUri; //Mapping from NFT ID to metadata uri. (tokenID)


  // ERC721Enumerable
  mapping(address => uint256[]) internal ownedTokens;  //Mapping from owner to list of owned token IDs(不包含正在拍卖的跑图 tokenID)
  mapping(uint256 => uint256) internal ownedTokensIndex; //Mapping from token ID to index of the owner tokens list(不包含正在拍卖的跑图 tokenID, index)
  uint256[] internal allTokens; //Array with all token ids, used for enumeration (tokenID)
  mapping(uint256 => uint256) internal allTokensIndex;  //Mapping from token id to position in the allTokens array(tokenID, index)


  SaleClockAuction public saleAuction;
  
  constructor() public {
    _registerInterface(InterfaceId_ERC721Enumerable);
    _registerInterface(InterfaceId_ERC721Metadata);
  }
  
  // Guarantees that _tokenId is a valid Token.  _tokenId ID of the NFT to validate.
  modifier validNFToken(uint256 _tokenId) {
    require(tokenIDToOwner[_tokenId] != address(0));
    _;
  }
  
  function name() external view returns (string) {
    return name_;
  }
  function symbol() external view returns (string) {
    return symbol_;
  }
  function tokenURI(uint256 _tokenId) public view returns (string) {
    require(_exists(_tokenId));
    return tokenIDToUri[_tokenId];
  }
  function tokenOfOwnerByIndex(address _owner, uint256 _index ) public view returns (uint256) {
    require(_index < balanceOf(_owner));
    return ownedTokens[_owner][_index];
  }
  function totalSupply() public view returns (uint256) {
    return allTokens.length;
  }
  function tokenByIndex(uint256 _index) public view returns (uint256) {
    require(_index < totalSupply());
    return allTokens[_index];
  }
  
  //拍卖时，token地址会转入拍卖合约 ownershipTokenCount
  function _transfer(address _from, address _to, uint256 _tokenId) internal {
    if(_from != address(0)){
      ownershipTokenCount[_from] = ownershipTokenCount[_from].sub(1);  //旧的跑图拥有者，跑图数量减1
      delete tokenIDToApproved[_tokenId];
      removeFromOwnedTokens(_from, _tokenId);
    }

    ownershipTokenCount[_to] = ownershipTokenCount[_to].add(1);  //新的拥有者，跑图数量加1
    tokenIDToOwner[_tokenId] = _to;

    uint256 length = ownedTokens[_to].push(_tokenId);
    ownedTokensIndex[_tokenId] = length.sub(1);

    emit Transfer(_from, _to, _tokenId);
  }
  
  function _owns(address _claimant, uint256 _tokenId) internal view returns (bool){
    return tokenIDToOwner[_tokenId] == _claimant;
  }

  function _approvedFor(address _claimant, uint256 _tokenId) internal view returns (bool) {
    return tokenIDToApproved[_tokenId] == _claimant;
  }

  function _approve(uint256 _tokenId, address _approved) internal {
    tokenIDToApproved[_tokenId] = _approved;
    emit Approval(msg.sender, _approved, _tokenId);
  }

  //Required for ERC-721
  function approve(address _to, uint256 _tokenId) public whenNotPaused {
    require(_owns(msg.sender, _tokenId));
    _approve(_tokenId, _to);
  }

  //Required for ERC-721
  function transfer(address _to, uint256 _tokenId) public whenNotPaused{
    require(_to != address(0));
    require(_to != address(this));
    require(_to != address(saleAuction));
    require(_owns(msg.sender, _tokenId));

    _transfer(msg.sender, _to, _tokenId);
  }

  //Required for ERC-721
  function transferFrom(address _from, address _to, uint256 _tokenId) public whenNotPaused{
    require(_to != address(0));
    require(_to != address(this));
    require(_approvedFor(msg.sender, _tokenId));
    require(_owns(_from, _tokenId));
    _transfer(_from, _to, _tokenId);
  }
  
  //Optional for ERC-721
  function safeTransferFrom(address _from, address _to, uint256 _tokenId) public whenNotPaused {
    _safeTransferFrom(_from, _to, _tokenId, "");
  }
  
  //Optional for ERC-721
  function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) public whenNotPaused {
    _safeTransferFrom(_from, _to, _tokenId, _data);
  }
  
  function _safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes _data) internal validNFToken(_tokenId) {
    transferFrom(_from, _to, _tokenId);
    require(checkAndCallSafeTransfer(_from, _to, _tokenId, _data));
  }
 
  // Optional method for ERC-721(不包含正在出售的)
  function tokensOfOwner(address _owner) public view returns(uint256[] ownerTokens){
    if(balanceOf(_owner) == 0){
      return new uint256[](0);
    }else{
      return ownedTokens[_owner];
    }
  }

  //创建链跑图
  function _createDrawings(bytes32 _chainID, bytes32 _author, address _owner, string metaUrl) internal returns(uint) {
    ChainDrawings memory _drawings = ChainDrawings({
      chainID: _chainID,
      author: _author,
      createTime: uint64(now)
    });

    uint256 _tokenId = drawings.push(_drawings);
    _tokenId = _tokenId.sub(1);
    chainIDToTokenID[_chainID] = _tokenId;
    require(_tokenId == uint256(uint32(_tokenId)));

    allTokensIndex[_tokenId] = allTokens.length;
    allTokens.push(_tokenId);
    tokenIDToUri[_tokenId] = metaUrl;

    //emit
    emit Create(_owner, _tokenId, _chainID);
    _transfer(address(0), _owner, _tokenId);

    return _tokenId;
  }

  //从用户跑图列表中删除（ERC721Enumerable）
  function removeFromOwnedTokens(address _owner, uint256 _tokenId) internal {
    require(tokenIDToOwner[_tokenId] == _owner);
    uint len = ownedTokens[_owner].length;
    assert(len > 0);

    if(len == 1){
      delete ownedTokens[_owner];
      delete ownedTokensIndex[_tokenId];
      return;
    }

    uint256 tokenToRemoveIndex = ownedTokensIndex[_tokenId];

    if(tokenToRemoveIndex == len.sub(1)){
      ownedTokens[_owner].length = ownedTokens[_owner].length.sub(1);
      delete ownedTokensIndex[_tokenId];
      return;
    }

    uint256 lastToken = ownedTokens[_owner][len.sub(1)];

    ownedTokens[_owner][tokenToRemoveIndex] = lastToken;
    ownedTokensIndex[lastToken] = tokenToRemoveIndex;
    ownedTokens[_owner].length = ownedTokens[_owner].length.sub(1);
    delete ownedTokensIndex[_tokenId];
  }
}
function transfer(address _to, uint256 _tokenId) public whenNotPaused{
  require(_to != address(0));
  require(_to != address(this));
  require(_to != address(saleAuction));
  require(_owns(msg.sender, _tokenId));

  _transfer(msg.sender, _to, _tokenId);
}

//Required for ERC-721