package org.magic.api.providers.impl;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.magic.api.beans.MagicCard;
import org.magic.api.beans.MagicCardNames;
import org.magic.api.beans.MagicEdition;
import org.magic.api.beans.MagicFormat;
import org.magic.api.beans.MagicFormat.AUTHORIZATION;
import org.magic.api.beans.MagicRuling;
import org.magic.api.beans.enums.MTGColor;
import org.magic.api.beans.enums.MTGFrameEffects;
import org.magic.api.beans.enums.MTGLayout;
import org.magic.api.beans.enums.MTGRarity;
import org.magic.api.interfaces.abstracts.AbstractCardsProvider;
import org.magic.services.MTGConstants;
import org.magic.tools.Chrono;
import org.magic.tools.FileTools;
import org.magic.tools.URLTools;

import com.google.common.collect.Lists;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.jayway.jsonpath.Configuration;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.Option;
import com.jayway.jsonpath.PathNotFoundException;
import com.jayway.jsonpath.ReadContext;
import com.jayway.jsonpath.spi.cache.CacheProvider;
import com.jayway.jsonpath.spi.cache.LRUCache;
import com.jayway.jsonpath.spi.json.GsonJsonProvider;
import com.jayway.jsonpath.spi.json.JsonProvider;
import com.jayway.jsonpath.spi.mapper.GsonMappingProvider;
import com.jayway.jsonpath.spi.mapper.MappingProvider;


public class Mtgjson4Provider extends AbstractCardsProvider {

	
	private static final String SCRYFALL_ID = "scryfallId";
	private static final String FORCE_RELOAD = "FORCE_RELOAD";
	private static final String PRINTINGS = "printings";
	private static final String ARTIST = "artist";
	private static final String TYPE = "type";
	private static final String FOREIGN_DATA = "foreignData";
	private static final String RULINGS = "rulings";
	private static final String LEGALITIES = "legalities";
	private static final String LOYALTY = "loyalty";
	private static final String COLOR_IDENTITY = "colorIdentity";
	private static final String COLORS = "colors";
	private static final String TOUGHNESS = "toughness";
	private static final String POWER = "power";
	private static final String SUBTYPES = "subtypes";
	private static final String TYPES = "types";
	private static final String SUPERTYPES = "supertypes";
	private static final String ORIGINAL_TYPE = "originalType";
	private static final String ORIGINAL_TEXT = "originalText";
	private static final String FLAVOR_TEXT = "flavorText";
	private static final String LAYOUT = "layout";
	private static final String IS_RESERVED = "isReserved";
	private static final String FRAME_VERSION = "frameVersion";
	private static final String CONVERTED_MANA_COST = "convertedManaCost";
	private static final String TEXT = "text";
	private static final String NUMBER = "number";
	private static final String RARITY = "rarity";
	private static final String MULTIVERSE_ID = "multiverseId";
	private static final String MANA_COST = "manaCost";
	private static final String NAME = "name";
	private static final String CARDS_ROOT_SEARCH = ".cards[?(@.";
	private static final String NAMES = "names";

	public static final String URL_JSON_VERSION = "https://mtgjson.com/json/version.json";
	public static final String URL_JSON_ALL_SETS = "https://mtgjson.com/json/AllSets.json";
	public static final String URL_JSON_SETS_LIST="https://mtgjson.com/json/SetList.json";
	public static final String URL_JSON_KEYWORDS="https://mtgjson.com/json/Keywords.json";
	public static final String URL_JSON_ALL_SETS_ZIP ="https://mtgjson.com/json/AllSets.json.zip";
	public static final String URL_JSON_DECKS_LIST = "https://mtgjson.com/json/DeckLists.json";
	public static final String URL_DECKS_URI = "https://mtgjson.com/json/decks/";
	
	private File fileSetJsonTemp = new File(MTGConstants.DATA_DIR,"AllSets-x4.json.zip");
	private File fileSetJson = new File(MTGConstants.DATA_DIR, "AllSets-x4.json");
	public static final File fversion = new File(MTGConstants.DATA_DIR, "version4");
	
	private String version;
	private Chrono chrono;
	private ReadContext ctx;
	
	
	public Mtgjson4Provider() {
		super();
		if(CacheProvider.getCache()==null)
			CacheProvider.setCache(new LRUCache(getInt("LRU_CACHE")));
		
	}

	private boolean hasNewVersion() {
		String temp = "";
			try  
			{
				temp = FileTools.readFile(fversion);
			}
			catch(FileNotFoundException ex)
			{
				logger.error(fversion + " doesn't exist"); 
			} catch (IOException e) {
				logger.error(e);
			}
			
			try {
				logger.debug("check new version of " + toString() + " (" + temp + ")");
	
				JsonElement d = URLTools.extractJson(URL_JSON_VERSION);
				version = d.getAsJsonObject().get("version").getAsString();
				if (!version.equals(temp)) {
					logger.info("new version datafile exist (" + version + "). Downloading it");
					return true;
				}

			logger.debug("check new version of " + this + ": up to date");
			return false;
		} catch (Exception e) {
			version = temp;
			logger.error("Error getting last version ",e);
			return false;
		}

	}

	public void init() {
		logger.info("init " + this);

		chrono=new Chrono();

		Configuration.setDefaults(new Configuration.Defaults() {

			private final JsonProvider jsonProvider = new GsonJsonProvider();
			private final MappingProvider mappingProvider = new GsonMappingProvider();

			@Override
			public JsonProvider jsonProvider() {
				return jsonProvider;
			}

			@Override
			public MappingProvider mappingProvider() {
				return mappingProvider;
			}

			@Override
			public Set<Option> options() {
				return EnumSet.noneOf(Option.class);
			}

		});
		Configuration.defaultConfiguration().addOptions(Option.DEFAULT_PATH_LEAF_TO_NULL);

		try {

			logger.debug("loading file " + fileSetJson);

			if (hasNewVersion()||!fileSetJson.exists() || fileSetJson.length() == 0 || getBoolean(FORCE_RELOAD)) {
				logger.info("Downloading "+version + " datafile");
				URLTools.download(URL_JSON_ALL_SETS_ZIP, fileSetJsonTemp);
				FileTools.unZipIt(fileSetJsonTemp,fileSetJson);
				FileTools.saveFile(fversion,version);
				setProperty(FORCE_RELOAD, "false");
			}
			Chrono chr = new Chrono();
			chr.start();
			logger.debug(this + " : parsing db file");
			ctx = JsonPath.parse(fileSetJson);
			logger.debug(this + " : parsing OK in " + chr.stop()+"s");
			
		} catch (Exception e1) {
			logger.error(e1);
		}
	}

	@Override
	public MagicCard getCardById(String id, MagicEdition ed) throws IOException {
		try {
			return searchCardByCriteria("uuid", id, ed, true).get(0);
		}catch(IndexOutOfBoundsException e)
		{
			return null;
		}
	}
	
	
	@Override
	public List<MagicCard> searchCardByCriteria(String att, String crit, MagicEdition ed, boolean exact) throws IOException {
		
		String filterEdition = ".";

		if (ed != null)
		{
			if(ed.getId().equals("NMS"))
				ed.setId("NEM");
				
			filterEdition = filterEdition + ed.getId().toUpperCase();
		}

		String jsquery = "$" + filterEdition + CARDS_ROOT_SEARCH + att + " =~ /^.*" + crit.replaceAll("\\+", " ")+ ".*$/i)]";

		if (exact)
			jsquery = "$" + filterEdition + CARDS_ROOT_SEARCH + att + " == \"" + crit.replaceAll("\\+", " ") + "\")]";

		if (att.equalsIgnoreCase(SET_FIELD)) 
		{
				jsquery = "$." + crit.toUpperCase() + ".cards";
		}
		else if(att.equals("jsonpath"))
		{
			jsquery = crit;
		}
		else if(StringUtils.isNumeric(crit)) {
			jsquery = "$" + filterEdition + CARDS_ROOT_SEARCH + att + " == " + crit + ")]";
		}
		
		return search(jsquery);
		
	}
	
	@SuppressWarnings("unchecked")
	private List<MagicCard> search(String jsquery) {
		
		List<String> currentSet = new ArrayList<>();
		ArrayList<MagicCard> ret = new ArrayList<>();
		
		List<Map<String, Object>> cardsElement = ctx.withListeners(fr -> {
			if (fr.path().startsWith("$")) {
				currentSet.add(fr.path().substring(fr.path().indexOf("$[") + 3, fr.path().indexOf(']') - 1));
			}
			return null;
		}).read(jsquery, List.class);
		
		logger.debug("parsing " + jsquery);
		
		int indexSet = 0;
		for (Map<String, Object> map : cardsElement) 
		{
				MagicCard mc = new MagicCard();
				  mc.setId(String.valueOf(map.get("uuid").toString()));
				  mc.setText(String.valueOf(map.get(TEXT)));
						  
				if (map.get(NAME) != null)
					mc.setName(String.valueOf(map.get(NAME)));
						  
				if (map.get(MANA_COST) != null)
					mc.setCost(String.valueOf(map.get(MANA_COST)));
				else
					mc.setCost("");
				
				if (map.get(RARITY) != null)
					mc.setRarity(MTGRarity.rarityByName(String.valueOf(map.get(RARITY))));
			
				if (map.get(TEXT) != null)
					mc.setText(String.valueOf(map.get(TEXT)));
				
				if (map.get(CONVERTED_MANA_COST) != null)
					mc.setCmc((int)Double.parseDouble(map.get(CONVERTED_MANA_COST).toString()));
				
				if (map.get(FRAME_VERSION) != null)
					mc.setFrameVersion(String.valueOf(map.get(FRAME_VERSION)));
				
				if (map.get("flavorName") != null)
					mc.setFlavorName(String.valueOf(map.get("flavorName")));
				
				
				
				if (map.get(ARTIST) != null)
					mc.setArtist(String.valueOf(map.get(ARTIST)));
			
				if (map.get(IS_RESERVED) != null)
					mc.setReserved(Boolean.valueOf(String.valueOf(map.get(IS_RESERVED))));
				
				if (map.get("isOversized") != null)
					mc.setOversized(Boolean.valueOf(String.valueOf(map.get("isOversized"))));
			
				if (map.get(LAYOUT) != null)
					mc.setLayout(MTGLayout.parseByLabel(String.valueOf(map.get(LAYOUT))));
				
				if (map.get(FLAVOR_TEXT) != null)
					mc.setFlavor(String.valueOf(map.get(FLAVOR_TEXT)));
				
				if (map.get("tcgplayerProductId") != null) {
					mc.setTcgPlayerId((int)Double.parseDouble(map.get("tcgplayerProductId").toString()));
				}
				
				if (map.get("mcmId") != null) {
					mc.setMkmId((int)Double.parseDouble(map.get("mcmId").toString()));
				}
				
				if (map.get("mtgstocksId") != null) {
					mc.setMtgstocksId(Double.valueOf(map.get("mtgstocksId").toString()).intValue());
				}
				
				if (map.get("edhrecRank") != null) {
					mc.setEdhrecRank(Double.valueOf(map.get("edhrecRank").toString()).intValue());
				}
				
				if (map.get(ORIGINAL_TEXT) != null)
					mc.setOriginalText(String.valueOf(map.get(ORIGINAL_TEXT)));
				
				if (map.get(ORIGINAL_TYPE) != null)
					mc.setOriginalType(String.valueOf(map.get(ORIGINAL_TYPE)));
				
				if (map.get(SUPERTYPES) != null)
					mc.getSupertypes().addAll((List<String>) map.get(SUPERTYPES));
			
				if (map.get(TYPES) != null)
					mc.getTypes().addAll((List<String>) map.get(TYPES));
			
				if (map.get(SUBTYPES) != null)
					mc.getSubtypes().addAll((List<String>) map.get(SUBTYPES));
			
				if (map.get(POWER) != null)
					mc.setPower(String.valueOf(map.get(POWER)));
				
				if (map.get(TOUGHNESS) != null)
					mc.setToughness(String.valueOf(map.get(TOUGHNESS)));
				
				if (map.get(COLORS) != null)
					mc.getColors().addAll(MTGColor.parseByCode(((List<String>) map.get(COLORS))));
			
				if (map.get(COLOR_IDENTITY) != null)
					mc.getColorIdentity().addAll(MTGColor.parseByCode(((List<String>) map.get(COLOR_IDENTITY))));
				
				if (map.get("frameEffects") != null)
					mc.getFrameEffects().addAll(MTGFrameEffects.parseByLabel(((List<String>) map.get("frameEffects"))));
				
				if (map.get("isMtgo") != null)
					mc.setMtgoCard(Boolean.valueOf(map.get("isMtgo").toString()));
				
				if (map.get("isArena") != null)
					mc.setArenaCard(Boolean.valueOf(map.get("isArena").toString()));
				
				if (map.get("watermark") != null)
					mc.setWatermarks(String.valueOf(map.get("watermark")));
				
				if (map.get("isPromo") != null)
					mc.setPromoCard(Boolean.valueOf(map.get("isPromo").toString()));
				
				if (map.get("mtgArenaId") != null)
					mc.setMtgArenaId(Double.valueOf(map.get("mtgArenaId").toString()).intValue());
			
				if (map.get("isReprint") != null)
					mc.setReprintedCard(Boolean.valueOf(map.get("isReprint").toString()));
				
				if (map.get("scryfallIllustrationId") != null)
					mc.setScryfallIllustrationId(String.valueOf(map.get("scryfallIllustrationId")));
				
				if (map.get(SCRYFALL_ID) != null)
					mc.setScryfallId(String.valueOf(map.get(SCRYFALL_ID)));
				
				if (map.get(LOYALTY) != null) {
					try {
						mc.setLoyalty((int) Double.parseDouble(map.get(LOYALTY).toString()));
					} catch (Exception e) {
						mc.setLoyalty(0);
					}
				}
				
				if (map.get(LEGALITIES) != null) {
					
					for (Map.Entry<String,String> mapFormats : ((Map<String,String>) map.get(LEGALITIES)).entrySet()) {
						MagicFormat mf = new MagicFormat(String.valueOf(mapFormats.getKey()),AUTHORIZATION.valueOf(String.valueOf(mapFormats.getValue()).toUpperCase()));
						mc.getLegalities().add(mf);
					}
				}
				
				if (map.get(RULINGS) != null) {
					for (Map<String, Object> mapRules : (List<Map<String,Object>>) map.get(RULINGS)) {
						MagicRuling mr = new MagicRuling();
						mr.setDate(String.valueOf(mapRules.get("date")));
						mr.setText(String.valueOf(mapRules.get(TEXT)));
						mc.getRulings().add(mr);
					}
				}
				
				MagicCardNames defnames = new MagicCardNames();
				
						if(map.get(MULTIVERSE_ID)!=null)
							   defnames.setGathererId((int)Double.parseDouble(map.get(MULTIVERSE_ID).toString()));
						
							   defnames.setLanguage("English");
							   defnames.setName(mc.getName());
							   defnames.setText(mc.getText());
							   defnames.setType(mc.getFullType());
			
				mc.getForeignNames().add(defnames);
				
				if (map.get(FOREIGN_DATA) != null) {
					for (Map<String, Object> mapNames : (List<Map<String, Object>>) map.get(FOREIGN_DATA)) {
						MagicCardNames fnames = new MagicCardNames();
									   fnames.setLanguage(String.valueOf(mapNames.get("language")));
									   fnames.setName(String.valueOf(mapNames.get(NAME)));
									   
									   if (mapNames.get(TEXT) != null)
										   fnames.setText(String.valueOf(mapNames.get(TEXT)));
									   
									   if (mapNames.get(TYPE) != null)
										   fnames.setType(String.valueOf(mapNames.get(TYPE)));
									   
									   if (mapNames.get(MULTIVERSE_ID) != null)
										   fnames.setGathererId((int) (double) mapNames.get(MULTIVERSE_ID));
									   
									   if (mapNames.get(FLAVOR_TEXT) != null)
										   fnames.setFlavor(String.valueOf(mapNames.get(FLAVOR_TEXT)));
									   
			
						mc.getForeignNames().add(fnames);
					}
				}
				
				
				String codeEd;
				if (currentSet.size() <= 1)
					codeEd = currentSet.get(0);
				else
					codeEd = currentSet.get(indexSet++);
			
				MagicEdition me = getSetById(codeEd);
							 me.setRarity(mc.getRarity());
							 me.setFlavor(mc.getFlavor());
							 me.setScryfallId(mc.getScryfallId());

							 if (map.get(NUMBER) != null)
							 {
								 me.setNumber(String.valueOf(map.get(NUMBER)));
							 }
							 
							 if(map.get(MULTIVERSE_ID)!=null)
							 {
								 defnames.setGathererId((int)Double.parseDouble(map.get(MULTIVERSE_ID).toString()));
								 me.setMultiverseid(String.valueOf((int)Double.parseDouble(map.get(MULTIVERSE_ID).toString())));
							 }
							
				mc.getEditions().add(me);
				
				if (!mc.isBasicLand() && map.get(PRINTINGS) != null)
				{
					for (String print : (List<String>) map.get(PRINTINGS)) {
						if (!print.equalsIgnoreCase(codeEd)) {
							MagicEdition meO = getSetById(print);
							initOtherEditionCardsVar(mc, meO);
							mc.getEditions().add(meO);
						}
					}
			
				}
				
				if( map.get(NAMES) !=null)
				{
					List<String> names = ((List<String>)map.get(NAMES));
					
					if(names.size()==2)
					{
						names.remove(mc.getName());
						mc.setRotatedCardName(names.get(0));
					}
					else if(names.size()>2)
					{
						mc.setRotatedCardName(names.get(1));
						//[Bruna, the Fading Light, Brisela, Voice of Nightmares, Gisela, the Broken Blade]
					}
					
				}
			
		notify(mc);
		ret.add(mc);
	}
		return ret;
	}
	
	private void initOtherEditionCardsVar(MagicCard mc, MagicEdition me) {
		String edCode = me.getId();

		if (!edCode.startsWith("p"))
			edCode = edCode.toUpperCase();

		String jsquery = "$." + edCode + ".cards[?(@.name==\""+ mc.getName().replaceAll("\\+", " ").replaceAll("\"", "\\\\\"") + "\")]";

		List<Map<String, Object>> cardsElement = null;
		try {
			cardsElement = ctx.read(jsquery, List.class);
		} catch (Exception e) {
			logger.error("error in " + jsquery +" " +  e);
			return ;
		}

		if (cardsElement != null)
			for (Map<String, Object> map : cardsElement) {
				try {
					me.setRarity(String.valueOf(map.get(RARITY)));
				} catch (Exception e) {
					me.setRarity(mc.getRarity());
				}
				
				try {
					me.setFlavor(String.valueOf(map.get(FLAVOR_TEXT)));
				} catch (Exception e) {
					me.setFlavor(mc.getFlavor());
				}

				try {

					me.setNumber(String.valueOf(map.get(NUMBER)));
				} catch (Exception e) {
					logger.trace("initOtherEditionCardsVar number not found");
				}
				
				try {

					me.setArtist(String.valueOf(map.get(ARTIST)));
				} catch (Exception e) {
					me.setArtist(mc.getArtist());
				}

				try {
					me.setMultiverseid(String.valueOf((int)Double.parseDouble(map.get(MULTIVERSE_ID).toString())));
				} catch (Exception e) {
					//do nothing
				}
				
				try {
					me.setScryfallId(map.get(SCRYFALL_ID).toString());
				} catch (Exception e) {
					//do nothing
				}
			}
	}

	@Override
	public List<MagicEdition> loadEditions() throws IOException {
		String jsquery = "$.*";
		chrono.start();
		
		List<MagicEdition> eds = new ArrayList<>();
		try {		
		
		URLTools.extractJson(URL_JSON_SETS_LIST).getAsJsonArray().forEach(e->{
				String codeedition = e.getAsJsonObject().get("code").getAsString().toUpperCase();
				eds.add(generateEdition(codeedition));
		});
		
		}catch(Exception ex)
		{
			
			logger.error("Error loading set List from " + URL_JSON_SETS_LIST +". Loading manually");
			final List<String> codeEd = new ArrayList<>();
			ctx.withListeners(fr -> {
				if (fr.path().startsWith("$"))
					codeEd.add(fr.path().substring(fr.path().indexOf("$[") + 3, fr.path().indexOf(']') - 1));
				return null;

			}).read(jsquery, List.class);
			codeEd.stream().map(this::generateEdition).forEach(eds::add);
		}

		logger.debug("Loading editions OK in " + chrono.stop() + " sec.");
		
		return eds;
	}

	private MagicEdition generateEdition(String id) {
		
		if(id.startsWith("p"))
			id=id.toUpperCase();
		
		MagicEdition ed = new MagicEdition(id);
		String base = "$." + id.toUpperCase();
		try{
		ed.setSet(ctx.read(base + ".name", String.class));
		}
		catch(PathNotFoundException pnfe)
		{	
			ed.setSet(id);
		}
		
		try{
			ed.setOnlineOnly(ctx.read(base + ".isOnlineOnly", Boolean.class));
		}
		catch(PathNotFoundException pnfe)
		{ 
			//do nothing
		}
		
		try{
			ed.setFoilOnly(ctx.read(base + ".isFoilOnly", Boolean.class));
		}
		catch(PathNotFoundException pnfe)
		{ 
			//do nothing
		}
			
		
		
		try{
		ed.setReleaseDate(ctx.read(base + ".releaseDate", String.class));
		}
		catch(PathNotFoundException pnfe)
		{ 
			//do nothing
		}
		
		try{
		ed.setType(ctx.read(base + ".type", String.class));
		}catch(PathNotFoundException pnfe)
		{ 
			//do nothing
		}
		
		try{
			ed.setBlock(ctx.read(base + ".block", String.class));
		}catch(PathNotFoundException pnfe)
		{ 
			//do nothing
		}
		
		try{
			ed.setBorder(ctx.read(base + ".cards[0].borderColor", String.class));
		}catch(PathNotFoundException pnfe)
		{ 
			//do nothing
		}
		
		try{
			ed.setGathererCode(ctx.read(base + ".mtgoCode", String.class));
		}catch(PathNotFoundException pnfe)
		{ 
			//do nothing
		}
		
		try {
			ed.setMkmName(ctx.read(base + ".mcmName", String.class));
		} catch (PathNotFoundException pnfe) {
			// do nothing
		}
		
		try {
			ed.setMkmid(ctx.read(base + ".mcmId", Integer.class));
		} catch (PathNotFoundException pnfe) {
			// do nothing
		}
		
		
		try {
			ed.setCardCountOfficial(ctx.read(base + ".baseSetSize", Integer.class));
		} catch (PathNotFoundException pnfe) {
			// do nothing
		}
		
		try {
			ed.setTcgplayerGroupId(ctx.read(base + ".tcgplayerGroupId", Integer.class));
		} catch (PathNotFoundException pnfe) {
			// do nothing
		}
		
		
		try {
			ed.setKeyRuneCode(ctx.read(base+".keyruneCode",String.class));
		}catch(PathNotFoundException pnfe)
		{
			//do nothing
		}
		
		try {
			JsonObject o = ctx.read(base+".translations",JsonObject.class);
			
			o.keySet().forEach(key->ed.getTranslations().put(key, o.get(key).getAsString()));
		}catch(Exception pnfe)
		{
			//do nothing
		}
		
		
		
		try {
			ed.setCardCount(ctx.read(base + ".totalSetSize", Integer.class));
		} catch (PathNotFoundException pnfe) {
			logger.warn("totalSetSize not found in " + ed.getId() + ", manual calculation");
			if (ed.getCardCount() == 0)
				try {
					ed.setCardCount(ctx.read(base + ".cards.length()"));
				} catch (Exception e) {
					ed.setCardCount(0);
				}
		}
		return ed;
	}

	@Override
	public List<String> loadQueryableAttributs() {
		
		return Lists.newArrayList(NAME,ARTIST,TEXT,CONVERTED_MANA_COST,POWER,TOUGHNESS,FLAVOR_TEXT,FRAME_VERSION,IS_RESERVED,LAYOUT,MANA_COST,MULTIVERSE_ID,NUMBER,RARITY,"hasFoil","hasNonFoil","jsonpath");
	}

	@Override
	public String getName() {
		return "MTGJson4";
	}

	@Override
	public String[] getLanguages() {
		return new String[] { "English", "Spanish", "French", "German", "Italian", "Portuguese", "Japanese", "Korean", "Russian", "Simplified Chinese","Traditional Chinese","Hebrew","Latin","Ancient Greek", "Arabic", "Sanskrit","Phyrexian" };
	}

	@Override
	public MagicCard getCardByNumber(String num, MagicEdition me) throws IOException {
		
		if(me==null)
			throw new IOException("Edition must not be null");
		
		String jsquery = "$." + me.getId().toUpperCase() + ".cards[?(@.number == '" + num + "')]";
		try {
			MagicCard mc = search(jsquery).get(0);
			mc.getEditions().add(me);
			return mc;
		} catch (Exception e) {
			logger.error(e);
			return null;
		}
	}
	
	@Override
	public String getVersion() {
		return version;
	}

	@Override
	public URL getWebSite() throws MalformedURLException {
		return new URL("https://mtgjson.com");
	}


	@Override
	public void initDefault() {
		setProperty("LRU_CACHE", "400");
		setProperty(FORCE_RELOAD,"false");
	}
	

	@Override
	public boolean equals(Object obj) {
		
		if(obj ==null)
			return false;
		
		return hashCode()==obj.hashCode();
	}
	
	@Override
	public int hashCode() {
		return getName().hashCode();
	}
}