contract BCFData is BCFBase, ERC721 {

    // MetaData
    string public constant NAME = "BlockchainFootball";
    string public constant SYMBOL = "BCF";

    struct Player {

        // Attribute ratings
        uint8 overall;
        uint8 pace;
        uint8 shooting;
        uint8 passing;
        uint8 dribbling;
        uint8 defending;
        uint8 physical;
        uint8 form; // No plans to use this atm but offers useful dynamic attribute -- NOT used in v1 of match engine

        // Level could be stored as an enum but plain ol' uint gives us more flexibility to introduce new levels in future
        uint8 level; // 1 = Superstar, 2 = Legend, 3 = Gold, 4 = Silver
        bytes position; // Shortcode - GK, LB, CB, RB, RW, RM, LW, LM, CM, CDM, CAM, ST
        string name; // First and last - arbitrary-length, hence string over bytes32
    }
    
    struct PlayerCard {
        uint playerId; // References the index of a player, i.e players[playerId] -- playerId = 0 invalid (non-existant)
        address owner;
        address approvedForTransfer;
        bool isFirstGeneration;
    }
    
    // Player + PlayerCards Database
    Player[] public players; // Central DB of player attributes
    PlayerCard[] public playerCards;

    // Utility mappings to make trading players and checking ownership gas-efficient
    mapping(address => uint[]) internal ownerToCardsOwned;
    mapping(uint => uint) internal cardIdToOwnerArrayIndex;

    // Extended attributes -- for now these are just an indexed list of values. Metadata will describe what each index represents.
    mapping(uint => uint8[]) public playerIdToExtendedAttributes; // Each index a single unique value > 0 and < 100

    // ERC721
    // Note: The standard is still in draft mode, so these are best efforts implementation based
    // on currently direction of the community and existing contracts which reside in the "wild"
    function implementsERC721() public pure returns (bool) {
        return true;
    }

    function totalSupply() public view returns (uint) {
        return playerCards.length;
    }

    function balanceOf(address _owner) public view returns (uint256 balance) {
        return ownerToCardsOwned[_owner].length;
    }

    function getApproved(uint _tokenId) public view returns (address approved) {
        approved = playerCards[_tokenId].approvedForTransfer;
    }

    function ownerOf(uint256 _tokenId) public view returns (address _owner) {
        _owner = playerCards[_tokenId].owner;
        require(_owner != address(0));
    }

    function approve(address _to, uint256 _tokenId) public whenNotPaused {
        require(ownsPlayerCard(msg.sender, _tokenId));
        approveForTransferTo(_to, _tokenId);
        Approval(msg.sender, _to, _tokenId);
    }

    function transferFrom(address _from, address _to, uint256 _tokenId) public whenNotPaused {
        require(_to != address(0));
        require(_to != address(this));
        require(ownsPlayerCard(_from, _tokenId));
        require(isApprovedForTransferTo(_to, _tokenId));
        
        // As we've validate we can now call the universal transfer method
        transferUnconditionally(_from, _to, _tokenId);
    }

    function transfer(address _to, uint256 _tokenId) public whenNotPaused {
        require(_to != address(0));
        require(_to != address(this));
        require(ownsPlayerCard(msg.sender, _tokenId));
    
        // As we've validate we can now call the universal transfer method
        transferUnconditionally(msg.sender, _to, _tokenId);
    }

    function name() public pure returns (string) {
        return NAME;
    }

    function symbol() public pure returns (string) {
        return SYMBOL;
    }

    function tokensOfOwner(address _owner) external view returns (uint256[] tokenIds) {
        return ownerToCardsOwned[_owner];
    }

    function addCardToOwnersList(address _owner, uint _cardId) internal {
        ownerToCardsOwned[_owner].push(_cardId);
        cardIdToOwnerArrayIndex[_cardId] = ownerToCardsOwned[_owner].length - 1;
    }

    function removeCardFromOwnersList(address _owner, uint _cardId) internal {
        uint length = ownerToCardsOwned[_owner].length;
        uint index = cardIdToOwnerArrayIndex[_cardId];
        uint swapCard = ownerToCardsOwned[_owner][length - 1];

        ownerToCardsOwned[_owner][index] = swapCard;
        cardIdToOwnerArrayIndex[swapCard] = index;

        delete ownerToCardsOwned[_owner][length - 1];
        ownerToCardsOwned[_owner].length--;
    }

    // Internal function to transfer without prior validation, requires callers to perform due-diligence
    function transferUnconditionally(address _from, address _to, uint _cardId) internal {
        
        if (_from != address(0)) {
            // Remove from current owner list first, otherwise we'll end up with invalid indexes
            playerCards[_cardId].approvedForTransfer = address(0);
            removeCardFromOwnersList(_from, _cardId);
        }
        
        playerCards[_cardId].owner = _to;
        addCardToOwnersList(_to, _cardId);

        Transfer(_from, _to, _cardId);
    }

    function isApprovedForTransferTo(address _approved, uint _cardId) internal view returns (bool) {
        return playerCards[_cardId].approvedForTransfer == _approved;
    }

    function approveForTransferTo(address _approved, uint _cardId) internal {
        playerCards[_cardId].approvedForTransfer = _approved;
    }

    function ownsPlayerCard(address _cardOwner, uint _cardId) internal view returns (bool) {
        return playerCards[_cardId].owner == _cardOwner;
    }

    function setPlayerForm(uint _playerId, uint8 _form) external whenNotPaused onlyEditor {
        require(players[_playerId].form > 0); // Check the player and form exist
        require(_form > 0 && _form <= 200); // Max value is players can double their form
        players[_playerId].form = _form;
    }

    function createPlayerCard(uint _playerId, address _newOwner, bool isFirstOfKind) internal returns (uint) {
        require(_playerId > 0); // disallow player cards for the first card - Thiago Messi
        Player storage _player = players[_playerId];
        require(_player.overall > 0); // Make sure the player exists

        PlayerCard memory _cardInstance = PlayerCard({
             playerId: _playerId,
             owner: _newOwner,
             approvedForTransfer: address(0),
             isFirstGeneration: isFirstOfKind
        });

        uint cardId = playerCards.push(_cardInstance) - 1;

        // We send it with 0x0 FROM address so we don't reduce the total number of cards associated with this address 
        transferUnconditionally(0, _newOwner, cardId);

        return cardId;
    }

    // Public Functions - Non ERC721 specific
    function totalPlayerCount() public view returns(uint) {
        return players.length;
    }
    
    function getPlayerForCard(uint _cardId) 
        external
        view
        returns (
        uint8 _overall,
        uint8 _pace,
        uint8 _shooting,
        uint8 _passing,
        uint8 _dribbling,
        uint8 _defending,
        uint8 _physical,
        uint8 _level,
        bytes _position,
        string _fullName,
        uint8 _form
    ) {
        // Fetch the card first
        PlayerCard storage _playerCard = playerCards[_cardId];
        
        // Return the player returned here
        // NOTE: if an invalid card is specified this may return index 0 result (Thiago Messi)
        Player storage player = players[_playerCard.playerId];
        _overall = player.overall;
        _pace = player.pace;
        _shooting = player.shooting;
        _passing = player.passing;
        _dribbling = player.dribbling;
        _defending = player.defending;
        _physical = player.physical;
        _level = player.level;
        _position = player.position;
        _fullName = player.name;
        _form = player.form;
    }

    function isOwnerOfAllPlayerCards(uint256[] _cardIds, address owner) public view returns (bool) {
        require(owner != address(0));

        // This function will return early if it finds any instance of a cardId not owned
        for (uint i = 0; i < _cardIds.length; i++) {
            if (!ownsPlayerCard(owner, _cardIds[i])) {
                return false;
            }
        }

        // Get's here, must own all cardIds
        return true;
    }

    // Extended attributes
    function setExtendedPlayerAttributesForPlayer(uint playerId, uint8[] attributes) external whenNotPaused onlyEditor {
        require(playerId > 0);
        playerIdToExtendedAttributes[playerId] = attributes;
    }

    function getExtendedAttributesForPlayer(uint playerId) public view returns (uint8[]) {
        require(playerId > 0);
        return playerIdToExtendedAttributes[playerId];
    }
}
function implementsERC721() public pure returns (bool) {
    return true;
}

// Each index a single unique value > 0 and < 100
// ERC721
// Note: The standard is still in draft mode, so these are best efforts implementation based
// on currently direction of the community and existing contracts which reside in the "wild"