contract ToyOwnership {
    struct ToyToken {
        // owner ID list
        address owner;
        // unique identifier
        uint uid;
        // timestamp
        uint timestamp;
        // exp
        uint exp;
        // toy data
        bytes toyData;
    }

    struct ExternalNft{
        // Contract address
        address nftContractAddress;
        // Token Identifier
        uint nftId;
    }

    // Array containing all TOY Tokens. The first element in toyArray returns
    //  as invalid
    ToyToken[] toyArray;
    // Mapping containing all UIDs tracked by this contract. Valid UIDs map to
    //  index numbers, invalid UIDs map to 0.
    mapping (uint => uint) uidToToyIndex;
    // Mapping containing linked external NFTs. Linked TOY Tokens always map to
    //  non-zero numbers, invalid TOY Tokens map to 0.
    mapping (uint => ExternalNft) uidToExternalNft;
    // Mapping containing tokens IDs for tokens created by an external contract
    //  and whether or not it is linked to a TOY Token 
    mapping (address => mapping (uint => bool)) linkedExternalNfts;
    
    //-------------------------------------------------------------------------
    /// @dev Throws if TOY Token #`_tokenId` isn't tracked by the toyArray.
    //-------------------------------------------------------------------------
    modifier mustExist(uint _tokenId) {
        require (uidToToyIndex[_tokenId] != 0, "Invalid TOY Token UID");
        _;
    }

    //-------------------------------------------------------------------------
    /// @dev Throws if TOY Token #`_tokenId` isn't owned by sender.
    //-------------------------------------------------------------------------
    modifier mustOwn(uint _tokenId) {
        require 
        (
            ownerOf(_tokenId) == msg.sender, 
            "Must be owner of this TOY Token"
        );
        _;
    }

    //-------------------------------------------------------------------------
    /// @dev Throws if parameter is zero
    //-------------------------------------------------------------------------
    modifier notZero(uint _param) {
        require(_param != 0, "Parameter cannot be zero");
        _;
    }

    //-------------------------------------------------------------------------
    /// @dev Creates an empty TOY Token as a [0] placeholder for invalid TOY 
    ///  Token queries.
    //-------------------------------------------------------------------------
    constructor () public {
        toyArray.push(ToyToken(0,0,0,0,""));
    }

    //-------------------------------------------------------------------------
    /// @notice Find the owner of TOY Token #`_tokenId`
    /// @dev throws if `_owner` is the zero address.
    /// @param _tokenId The identifier for a TOY Token
    /// @return The address of the owner of the TOY Token
    //-------------------------------------------------------------------------
    function ownerOf(uint256 _tokenId) 
        public 
        view 
        mustExist(_tokenId) 
        returns (address) 
    {
        // owner must not be the zero address
        require (
            toyArray[uidToToyIndex[_tokenId]].owner != 0, 
            "TOY Token has no owner"
        );
        return toyArray[uidToToyIndex[_tokenId]].owner;
    }

    //-------------------------------------------------------------------------
    /// @notice Count all TOY Tokens assigned to an owner
    /// @dev throws if `_owner` is the zero address.
    /// @param _owner An address to query
    /// @return The number of TOY Tokens owned by `_owner`, possibly zero
    //-------------------------------------------------------------------------
    function balanceOf(address _owner) 
        public 
        view 
        notZero(uint(_owner)) 
        returns (uint256) 
    {
        uint owned;
        for (uint i = 1; i < toyArray.length; ++i) {
            if(toyArray[i].owner == _owner) {
                ++owned;
            }
        }
        return owned;
    }

    //-------------------------------------------------------------------------
    /// @notice Get a list of TOY Tokens assigned to an owner
    /// @dev Throws if `_index` >= `balanceOf(_owner)` or if
    ///  `_owner` is the zero address, representing invalid TOY Tokens.
    /// @param _owner Address to query for TOY Tokens.
    /// @return The complete list of Unique Indentifiers for TOY Tokens
    ///  assigned to `_owner`
    //-------------------------------------------------------------------------
    function tokensOfOwner(address _owner) external view returns (uint[]) {
        uint toysOwned = balanceOf(_owner);
        require(toysOwned > 0, "No owned TOY Tokens");
        uint counter = 0;
        uint[] memory result = new uint[](toysOwned);
        for (uint i = 0; i < toyArray.length; i++) {
            if(toyArray[i].owner == _owner) {
                result[counter] = toyArray[i].uid;
                counter++;
            }
        }
        return result;
    }

    //-------------------------------------------------------------------------
    /// @notice Get number of TOY Tokens tracked by this contract
    /// @return A count of valid TOY Tokens tracked by this contract, where
    ///  each one has an assigned and queryable owner.
    //-------------------------------------------------------------------------
    function totalSupply() external view returns (uint256) {
        return (toyArray.length - 1);
    }

    //-------------------------------------------------------------------------
    /// @notice Get the UID of TOY Token with index number `index`.
    /// @dev Throws if `_index` >= `totalSupply()`.
    /// @param _index A counter less than `totalSupply()`
    /// @return The UID for the #`_index` TOY Token in the TOY Token array.
    //-------------------------------------------------------------------------
    function tokenByIndex(uint256 _index) external view returns (uint256) {
        // index must correspond to an existing TOY Token
        require (_index > 0 && _index < toyArray.length, "Invalid index");
        return (toyArray[_index].uid);
    }

    //-------------------------------------------------------------------------
    /// @notice Enumerate NFTs assigned to an owner
    /// @dev Throws if `_index` >= `balanceOf(_owner)` or if
    ///  `_owner` is the zero address, representing invalid NFTs.
    /// @param _owner Address to query for TOY Tokens.
    /// @param _index A counter less than `balanceOf(_owner)`
    /// @return The Unique Indentifier for the #`_index` TOY Token assigned to
    ///  `_owner`, (sort order not specified)
    //-------------------------------------------------------------------------
    function tokenOfOwnerByIndex(
        address _owner, 
        uint256 _index
    ) external view notZero(uint(_owner)) returns (uint256) {
        uint toysOwned = balanceOf(_owner);
        require(toysOwned > 0, "No owned TOY Tokens");
        require(_index < toysOwned, "Invalid index");
        uint counter = 0;
        for (uint i = 0; i < toyArray.length; i++) {
            if (toyArray[i].owner == _owner) {
                if (counter == _index) {
                    return(toyArray[i].uid);
                } else {
                    counter++;
                }
            }
        }
    }
}
//-----------------------------------------------------------------------------
function balanceOf(address _owner) 
    public 
    view 
    notZero(uint(_owner)) 
    returns (uint256) 
{
    uint owned;
    for (uint i = 1; i < toyArray.length; ++i) {
        if(toyArray[i].owner == _owner) {
            ++owned;
        }
    }
    return owned;
}

//-------------------------------------------------------------------------