contract ERC721Token is SupportsInterfaceWithLookup, ERC721BasicToken, ERC721 {

    IEntityStorage internal cbStorage;

    bytes4 private constant InterfaceId_ERC721Enumerable = 0x780e9d63;
    /**
    * 0x780e9d63 ===
    *   bytes4(keccak256('totalSupply()')) ^
    *   bytes4(keccak256('tokenOfOwnerByIndex(address,uint256)')) ^
    *   bytes4(keccak256('tokenByIndex(uint256)'))
    */

    bytes4 private constant InterfaceId_ERC721Metadata = 0x5b5e139f;
    /**
    * 0x5b5e139f ===
    *   bytes4(keccak256('name()')) ^
    *   bytes4(keccak256('symbol()')) ^
    *   bytes4(keccak256('tokenURI(uint256)'))
    */

    string internal uriPrefix;

    // Token name
    string internal name_;

    // Token symbol
    string internal symbol_;

    // Mapping from owner to list of owned token IDs
    mapping(address => uint256[]) internal ownedTokens;
    
    // Array with transferable Tokens
    uint256[] internal transferableTokens;

    /**
    * @dev Constructor function
    */
    constructor(string _name, string _symbol, string _uriPrefix, address _storage) public {
        require(_storage != address(0), "Storage Address is required");
        name_ = _name;
        symbol_ = _symbol;

        // register the supported interfaces to conform to ERC721 via ERC165
        _registerInterface(InterfaceId_ERC721Enumerable);
        _registerInterface(InterfaceId_ERC721Metadata);
        cbStorage = IEntityStorage(_storage);
        uriPrefix = _uriPrefix;
    }

    /**
    * @dev Gets the token name
    * @return string representing the token name
    */
    function name() external view returns (string) {
        return name_;
    }

    /**
    * @dev Gets the token symbol
    * @return string representing the token symbol
    */
    function symbol() external view returns (string) {
        return symbol_;
    }

    /**
    * @dev Returns an URI for a given token ID
    * Throws if the token ID does not exist. May return an empty string.
    * @param _tokenId uint256 ID of the token to query
    */
    function tokenURI(uint256 _tokenId) public view returns (string) {
        require(exists(_tokenId));
        return strConcat(uriPrefix, uintToString(_tokenId));
    }

    /**
    * @dev Gets the total amount of tokens stored by the contract
    * @return uint256 representing the total amount of tokens
    */
    function totalSupply() public view returns (uint256) {
        return cbStorage.totalSupply();
    }

    /**
    * @dev Internal function to add a token ID to the list owned by a given address
    * @param _to address representing the new owner of the token ID
    * @param _tokenId uint256 ID of the token to be added 
    */
    function addTokenTo(address _to, uint256 _tokenId) internal {
        super.addTokenTo(_to, _tokenId);
        ownedTokens[_to].push(_tokenId);
    }

    /**
    * @dev Internal function to remove a token ID from the list owned by a given address
    * @param _from address representing the previous owner of the token ID
    * @param _tokenId uint256 ID of the token to be removed
    */
    function removeTokenFrom(address _from, uint256 _tokenId) internal {
        super.removeTokenFrom(_from, _tokenId);

        uint256 tokenIndex = 0;
        while (ownedTokens[_from][tokenIndex] != _tokenId && tokenIndex < ownedTokens[_from].length) {
            tokenIndex++;
        }
        // Reorg allTokens array
        uint256 lastTokenIndex = ownedTokens[_from].length.sub(1);
        uint256 lastToken = ownedTokens[_from][lastTokenIndex];

        ownedTokens[_from][tokenIndex] = lastToken;
        ownedTokens[_from][lastTokenIndex] = 0;

        ownedTokens[_from].length--;
    }

    /**
    * @dev Internal function to burn a specific token
    * Reverts if the token does not exist, or is marked transferable
    * @param _owner owner address of the token being burned
    * @param _tokenId uint256 ID of the token being burned 
    */
    function _burn(address _owner, uint256 _tokenId) internal {
        // cannot burn a token that is up for sale
        require(!isTransferable(_tokenId)); //, "_burn"
        super._burn(_owner, _tokenId);
        cbStorage.remove(_tokenId);
    }

    /**
    * @dev Gets the number of tokens owned by the specified address
    * @param _owner address of the token owner
    * @return uint256 the number of tokens owned 
    */
    function balanceOf(address _owner) public view returns (uint256) {
        require(_owner != address(0));
        return ownedTokens[_owner].length;
    }

        /**
    * @dev List all token Ids that can be transfered
    * @return array of token IDs
    */
    function listTransferableTokens() public view returns(uint256[]) {
        return transferableTokens;
    } 

    /**
    * @dev Is Token Transferable
    * @param _tokenId uint256 ID of the token
    * @return bool is tokenId transferable 
    */
    function isTransferable(uint256 _tokenId) public view returns (bool) {
        for (uint256 index = 0; index < transferableTokens.length; index++) {
            if (transferableTokens[index] == _tokenId) {
                return true;
            }
        }
        return false;
    }

    /**
    * @dev Returns whether the given spender can transfer a given token ID
    * @param _spender address of the spender to query
    * @param _tokenId uint256 ID of the token to be transferred
    * @return bool whether the token is transferable and msg.sender is approved for the given token ID,
    *  is an operator of the owner, or is the owner of the token
    */
    function isApprovedOrOwner(address _spender, uint256 _tokenId) internal view returns (bool) {
        address owner = ownerOf(_tokenId);
        if (isTransferable(_tokenId)) {
            return (_spender == owner || getApproved(_tokenId) == _spender || isApprovedForAll(owner, _spender));
        }
        return false;
    }

    /**
    * Converts a uint, such aa a token ID number, to a string
    */
    function uintToString(uint v) internal pure returns (string str) {
        uint maxlength = 100;
        bytes memory reversed = new bytes(maxlength);
        uint i = 0;
        while (v != 0) {
            uint remainder = v % 10;
            v = v / 10;
            reversed[i++] = byte(48 + remainder);
        }
        bytes memory s = new bytes(i);
        for (uint j = 0; j < i; j++) {
            s[j] = reversed[i - 1 - j];
        }
        str = string(s);
    }

    /**
    * Basic smashing together of strings.
    */
    function strConcat(string _a, string _b)internal pure returns (string) {
        bytes memory _ba = bytes(_a);
        bytes memory _bb = bytes(_b);
        string memory ab = new string(_ba.length + _bb.length);
        bytes memory ba = bytes(ab);
        uint k = 0;
        for (uint i = 0; i < _ba.length; i++) ba[k++] = _ba[i];
        for (i = 0; i < _bb.length; i++) ba[k++] = _bb[i];
        return string(ba);
    }
}
/**
 * @title Full ERC721 Token
 * This implementation includes all the required and some optional functionality of the ERC721 standard
 * Moreover, it includes approve all functionality using operator terminology
 * @dev see https://github.com/ethereum/EIPs/blob/master/EIPS/eip-721.md Customized to support non-transferability.
 */
function name() external view returns (string) {
    return name_;
}

/**
 * @dev Gets the token name
 * @return string representing the token name
 */