public static async Task<PostResult> PostMultipleGameResultsAsync(IEnumerable<GameStats> games, Deck deck)
		{
			var validGames = games.Where(x => IsValidGame(x) && !x.HasHearthStatsId).ToList();
			long versionId;
			if(!long.TryParse(deck.HearthStatsDeckVersionId, out versionId))
			{
				Logger.WriteLine("error: invalid HearthStatsDeckVersionId", "HearthStatsAPI");
				return PostResult.Failed;
			}

			var url = BaseUrl + "/matches/multi_create?auth_token=" + _authToken;
			dynamic gameObjs = new ExpandoObject[validGames.Count];
			for(int i = 0; i < validGames.Count; i++)
			{
				gameObjs[i] = new ExpandoObject();
				gameObjs[i].mode = validGames[i].GameMode.ToString();
				gameObjs[i].@class = string.IsNullOrEmpty(validGames[i].PlayerHero) ? deck.Class : validGames[i].PlayerHero;
				gameObjs[i].result = validGames[i].Result.ToString();
				gameObjs[i].coin = validGames[i].Coin.ToString().ToLower();
				gameObjs[i].numturns = validGames[i].Turns;
				gameObjs[i].duration = (int)(validGames[i].EndTime - validGames[i].StartTime).TotalSeconds;
				gameObjs[i].deck_id = deck.HearthStatsIdForUploading;
				gameObjs[i].deck_version_id = versionId;
				if(!string.IsNullOrEmpty(validGames[i].OpponentHero))
					gameObjs[i].oppclass = validGames[i].OpponentHero;
				if(!string.IsNullOrEmpty(validGames[i].OpponentName))
					gameObjs[i].oppname = validGames[i].OpponentName;
				if(!string.IsNullOrEmpty(validGames[i].Note))
					gameObjs[i].notes = validGames[i].Note;
				if(validGames[i].GameMode == GameMode.Ranked && validGames[i].HasRank)
					gameObjs[i].ranklvl = validGames[i].Rank.ToString();
				var opponentCards = validGames[i].GetOpponentDeck().Cards;
				if(opponentCards.Where(Database.IsActualCard).Any())
					gameObjs[i].oppcards = opponentCards.Where(Database.IsActualCard).Select(c => new {id = c.Id, count = c.Count}).ToArray();
				gameObjs[i].created_at = validGames[i].StartTime.ToUniversalTime().ToString("s");
			}


			var data = JsonConvert.SerializeObject(new {deck_id = deck.HearthStatsIdForUploading, matches = gameObjs});

			try
			{
				var response = await PostAsync(url, data);
				dynamic json = JsonConvert.DeserializeObject(response);
				if(json.status.ToString() == "404")
				{
					//deck does not exist on hearthstats
					deck.ResetHearthstatsIds();
					DeckList.Save();
					deck.DeckStats.Games.ForEach(g => g.ResetHearthstatsIds());
					DeckStatsList.Save();
					return PostResult.Failed;
				}
				if(json.status.ToString() != "200")
					Logger.WriteLine($"Error: {json.message.ToString()} (Status code: {json.status.ToString()})", "HearthStatsAPI");

				for(int i = 0; i < validGames.Count; i++)
				{
					if(json.data[i].status == "200")
					{
						validGames[i].HearthStatsId = json.data[i].data.id;
						Logger.WriteLine("assigned id to match: " + validGames[i].HearthStatsId, "HearthStatsAPI");
					}
					else
						Logger.WriteLine(string.Format("Error uploading match {0}: {1}", validGames[i], json.data[i].status), "HearthStatsAPI");
				}
				return PostResult.WasSuccess;
			}
			catch(Exception e)
			{
				Logger.WriteLine(e.ToString(), "HearthStatsAPI");
				return PostResult.Failed;
			}
		}
		public static async Task<PostResult> PostGameResultAsync(GameStats game, Deck deck)
		{
			if(!IsValidGame(game))
				return PostResult.Failed;
			if(!deck.HasHearthStatsId)
			{
				Logger.WriteLine("can not upload game, deck has no hearthstats id", "HearthStatsAPI");
				return PostResult.Failed;
			}
			long versionId;
			if(!long.TryParse(deck.HearthStatsDeckVersionId, out versionId))
			{
				Logger.WriteLine("error: invalid HearthStatsDeckVersionId", "HearthStatsAPI");
				return PostResult.Failed;
			}
			Logger.WriteLine("uploading match: " + game, "HearthStatsAPI");
			var url = BaseUrl + "/matches?auth_token=" + _authToken;

			dynamic gameObj = new ExpandoObject();
			gameObj.mode = game.GameMode.ToString();
			gameObj.@class = string.IsNullOrEmpty(game.PlayerHero) ? deck.Class : game.PlayerHero;
			gameObj.result = game.Result.ToString();
			gameObj.coin = game.Coin.ToString().ToLower();
			gameObj.numturns = game.Turns;
			gameObj.duration = (int)(game.EndTime - game.StartTime).TotalSeconds;
			gameObj.deck_id = deck.HearthStatsIdForUploading;
			gameObj.deck_version_id = versionId;
			if(!string.IsNullOrEmpty(game.OpponentHero))
				gameObj.oppclass = game.OpponentHero;
			if(!string.IsNullOrEmpty(game.OpponentName))
				gameObj.oppname = game.OpponentName;
			if(!string.IsNullOrEmpty(game.Note))
				gameObj.notes = game.Note;
			if(game.GameMode == GameMode.Ranked && game.HasRank)
				gameObj.ranklvl = game.Rank.ToString();
			var opponentCards = game.GetOpponentDeck().Cards;
			if(opponentCards.Where(Database.IsActualCard).Any())
				gameObj.oppcards = opponentCards.Where(Database.IsActualCard).Select(c => new {id = c.Id, count = c.Count}).ToArray();
			gameObj.created_at = game.StartTime.ToUniversalTime().ToString("s");

			var data = JsonConvert.SerializeObject(gameObj);

			try
			{
				var response = await PostAsync(url, data);
				var json = JsonConvert.DeserializeObject(response);
				if(json.status.ToString() == "200")
				{
					game.HearthStatsId = json.data.id;
					Logger.WriteLine("assigned id to match: " + game.HearthStatsId, "HearthStatsAPI");
					return PostResult.WasSuccess;
				}
				if(json.status.ToString() == "fail" && json.message.ToString().Contains("Deck could not be found"))
				{
					//deck does not exist on hearthstats
					deck.ResetHearthstatsIds();
					DeckList.Save();
					deck.DeckStats.Games.ForEach(g => g.ResetHearthstatsIds());
					DeckStatsList.Save();
				}
				return PostResult.Failed;
			}
			catch(Exception e)
			{
				Logger.WriteLine(e.ToString(), "HearthStatsAPI");
				return PostResult.Failed;
			}
		}