contract CryptoFamousOwnership is CryptoFamousBase {
  // EVENTS
  /// @dev emitted when a new Card is created. Can happen when a social identity is claimed or stolen for the first time.
  event CardCreated(uint256 indexed cardId, uint8 socialNetworkType, uint64 socialId, address claimer, address indexed owner);

  // STORAGE
  /// @dev contains all the Cards in the system. Card with ID 0 is invalid.
  Card[] public allCards;

  /// @dev SocialNetworkType -> (SocialId -> CardId)
  mapping (uint8 => mapping (uint64 => uint256)) private socialIdentityMappings;

  /// @dev getter for `socialIdentityMappings`
  function socialIdentityToCardId(uint256 _socialNetworkType, uint256 _socialId) public view returns (uint256 cardId) {
    uint8 _socialNetworkType8 = uint8(_socialNetworkType);
    require(_socialNetworkType == uint256(_socialNetworkType8));

    uint64 _socialId64 = uint64(_socialId);
    require(_socialId == uint256(_socialId64));

    cardId = socialIdentityMappings[_socialNetworkType8][_socialId64];
    return cardId;
  }

  mapping (uint8 => mapping (address => uint256)) private claimerAddressToCardIdMappings;

  /// @dev returns the last Card ID claimed by `_claimerAddress` in network with `_socialNetworkType`
  function lookUpClaimerAddress(uint256 _socialNetworkType, address _claimerAddress) public view returns (uint256 cardId) {
    uint8 _socialNetworkType8 = uint8(_socialNetworkType);
    require(_socialNetworkType == uint256(_socialNetworkType8));

    cardId = claimerAddressToCardIdMappings[_socialNetworkType8][_claimerAddress];
    return cardId;
  }

  /// @dev A mapping from Card ID to the timestamp of the first completed Claim of that Card
  mapping (uint256 => uint128) public cardIdToFirstClaimTimestamp;

  /// @dev A mapping from Card ID to the current owner address of that Card
  mapping (uint256 => address) public cardIdToOwner;

  /// @dev A mapping from owner address to the number of Cards currently owned by it
  mapping (address => uint256) internal ownerAddressToCardCount;

  function _changeOwnership(address _from, address _to, uint256 _cardId) internal whenNotPaused {
      ownerAddressToCardCount[_to]++;
      cardIdToOwner[_cardId] = _to;

      if (_from != address(0)) {
          ownerAddressToCardCount[_from]--;
      }
  }

  function _recordFirstClaimTimestamp(uint256 _cardId) internal {
    cardIdToFirstClaimTimestamp[_cardId] = uint128(now); //solhint-disable-line not-rely-on-time
  }

  function _createCard(
      uint256 _socialNetworkType,
      uint256 _socialId,
      address _owner,
      address _claimer
  )
      internal
      whenNotPaused
      returns (uint256)
  {
      uint8 _socialNetworkType8 = uint8(_socialNetworkType);
      require(_socialNetworkType == uint256(_socialNetworkType8));

      uint64 _socialId64 = uint64(_socialId);
      require(_socialId == uint256(_socialId64));

      uint16 claimNonce = 0;
      if (_claimer != address(0)) {
        claimNonce = 1;
      }

      Card memory _card = Card({
          socialNetworkType: _socialNetworkType8,
          socialId: _socialId64,
          claimer: _claimer,
          claimNonce: claimNonce,
          reserved1: 0
      });
      uint256 newCardId = allCards.push(_card) - 1;
      socialIdentityMappings[_socialNetworkType8][_socialId64] = newCardId;

      if (_claimer != address(0)) {
        claimerAddressToCardIdMappings[_socialNetworkType8][_claimer] = newCardId;
        _recordFirstClaimTimestamp(newCardId);
      }

      // event CardCreated(uint256 indexed cardId, uint8 socialNetworkType, uint64 socialId, address claimer, address indexed owner);
      CardCreated(
          newCardId,
          _socialNetworkType8,
          _socialId64,
          _claimer,
          _owner
      );

      _changeOwnership(0, _owner, newCardId);

      return newCardId;
  }

  /// @dev Returns the toal number of Cards in existence
  function totalNumberOfCards() public view returns (uint) {
      return allCards.length - 1;
  }

  /// @notice Returns a list of all Card IDs currently owned by `_owner`
  /// @dev (this thing iterates, don't call from smart contract code)
  function tokensOfOwner(address _owner) external view returns(uint256[] ownerTokens) {
      uint256 tokenCount = ownerAddressToCardCount[_owner];

      if (tokenCount == 0) {
          return new uint256[](0);
      }

      uint256[] memory result = new uint256[](tokenCount);
      uint256 total = totalNumberOfCards();
      uint256 resultIndex = 0;

      uint256 cardId;

      for (cardId = 1; cardId <= total; cardId++) {
          if (cardIdToOwner[cardId] == _owner) {
              result[resultIndex] = cardId;
              resultIndex++;
          }
      }

      return result;
  }
}
function socialIdentityToCardId(uint256 _socialNetworkType, uint256 _socialId) public view returns (uint256 cardId) {
  uint8 _socialNetworkType8 = uint8(_socialNetworkType);
  require(_socialNetworkType == uint256(_socialNetworkType8));

  uint64 _socialId64 = uint64(_socialId);
  require(_socialId == uint256(_socialId64));

  cardId = socialIdentityMappings[_socialNetworkType8][_socialId64];
  return cardId;
}

/// @dev getter for `socialIdentityMappings`