CardData*
MtgJsonAllSetsData::createCardData( int multiverseId ) const
{
    // Unfortunately this is a slow linear search.
    for( const std::string& setCode : mAllSetCodes )
    {
        // In parse() this was vetted to be safe and yield an Array-type value.
        const Value& cardsValue = mDoc[setCode]["cards"];

        // This is a linear search looking for the multiverse id.
        for( Value::ConstValueIterator iter = cardsValue.Begin();
                iter != cardsValue.End(); ++iter )
        {
            if( iter->HasMember("multiverseid") )
            {
                const Value& multiverseIdValue = (*iter)["multiverseid"];
                if( multiverseIdValue.IsInt() )
                {
                    int muid = multiverseIdValue.GetInt();
                    if( muid == multiverseId )
                    {
                        mLogger->debug( "found muid ", muid );
                        return new MtgJsonCardData( setCode, *iter );
                    }
                }
            }
        }
    }

    mLogger->warn( "unable to find card multiverseId {}", multiverseId );
    return nullptr;
}
Value::ConstValueIterator
MtgJsonAllSetsData::findCardValueByName( Value::ConstValueIterator first,
                                         Value::ConstValueIterator last,
                                         const std::string&        name ) const
{
    Value::ConstValueIterator iter = first;

    for( Value::ConstValueIterator iter = first; iter != last; ++iter )
    {
        std::string nameStr( (*iter)["name"].GetString() );
        if( StringUtil::icompare( nameStr, name ) )
        {
            mLogger->debug( "found name {}", name );
            return iter;
        }
        else if( iter->HasMember("names") )
        {
            // Here we check if the card has multiple names, i.e. split
            // cards.  If so, create a split card name and normalize the
            // name parameter and see if they match.

            std::string splitCardName = MtgJson::createSplitCardName( (*iter)["names"] );
            std::string nameNormalized = MtgJson::normalizeSplitCardName( name );

            if( StringUtil::icompare( nameNormalized, splitCardName ) )
            {
                mLogger->debug( "found split name {}", name );
                return iter;
            }
        }
    }

    return last;
}
std::multimap<RarityType,std::string>
MtgJsonAllSetsData::getCardPool( const std::string& code ) const
{
    std::multimap<RarityType,std::string> rarityMap;

    if( mAllSetCodes.count(code) == 0 )
    {
        mLogger->warn( "Unable to find set {}, returning empty card pool", code );
        return rarityMap;
    }

    // In parse() this was vetted to be safe and yield an Array-type value.
    const Value& cardsValue = mDoc[code]["cards"];

    mLogger->debug( "{:-^40}", "assembling card pool" );
    for( Value::ConstValueIterator iter = cardsValue.Begin(); iter != cardsValue.End(); ++iter )
    {
        Value::ConstMemberIterator nameIter = iter->FindMember( "name" );
        Value::ConstMemberIterator rarityIter = iter->FindMember( "rarity" );

        if( nameIter != iter->MemberEnd() && nameIter->value.IsString() &&
            rarityIter != iter->MemberEnd() && rarityIter->value.IsString() )
        {
            std::string nameStr( nameIter->value.GetString() );
            std::string rarityStr( rarityIter->value.GetString() );
            mLogger->debug( "json: {} : {}", nameStr, rarityStr );

            // Some cards have multiple entries (i.e. split/flip/double-sided),
            // so make sure they are only represented once.  Done by skipping
            // over cards whose name doesn't match the first entry of the
            // 'names' array (if it exists).
            if( iter->HasMember("names") )
            {
                const Value& names = (*iter)["names"];
                if( names.IsArray() && !names.Empty() && (nameStr != names[0]) )
                {
                    continue;
                }

                // Modify the name for split cards.
                if( MtgJson::isSplitCard( *iter ) )
                {
                    nameStr = MtgJson::createSplitCardName( names );
                }

            }

            // Some cards have variations with multiple entries that should
            // only be counted once.  Done by checking if there are
            // variations and checking a card's number for a non-digit,
            // non-'a' ending.
            if( iter->HasMember("variations") && iter->HasMember("number") )
            {
                const Value& numberValue = (*iter)["number"];
                if( numberValue.IsString() )
                {
                    const std::string numberStr( numberValue.GetString() );
                    const char c = numberStr.back();
                    if( !std::isdigit( c ) && (c != 'a') )
                    {
                        continue;
                    }
                }
            }

            RarityType rarity = MtgJson::getRarityFromString( rarityStr );
            if( rarity != RARITY_UNKNOWN )
                rarityMap.insert( std::make_pair( rarity, nameStr ) );
            else
                mLogger->warn( "Unknown rarity type {}, ignoring!", rarityStr );
        }
        else
        {
            mLogger->notice( "card entry without name or rarity in set {}", code );
        }
    }

    return rarityMap;
}