public Task<double> GetHitGroupDamageAsync(Demo demo, Hitgroup hitGroup, List<long> steamIdList, List<int> roundNumberList)
		{
			double result = 0;
			switch (hitGroup)
			{
				case Hitgroup.Chest:
					result = 110;
					break;
				case Hitgroup.LeftArm:
					result = 20;
					break;
				case Hitgroup.RightArm:
					result = 30;
					break;
				case Hitgroup.Head:
					result = 40;
					break;
				case Hitgroup.LeftLeg:
					result = 50;
					break;
				case Hitgroup.RightLeg:
					result = 60;
					break;
				case Hitgroup.Stomach:
					result = 70;
					break;
			}

			return Task.FromResult(result);
		}
		internal void WatchDemoAt(Demo demo, int tick, bool delay = false)
		{
			_arguments.Add("+playdemo");
			if (delay && tick > 1000) tick -= 1000;
			_arguments.Add(demo.Path + "@" + tick);
			StartGame();
		}
		/// <summary>
		/// Check if there are banned players and update their banned flags
		/// </summary>
		/// <param name="demo"></param>
		/// <returns></returns>
		public async Task<Demo> AnalyzeBannedPlayersAsync(Demo demo)
		{
			List<string> ids = demo.Players.Select(playerExtended => playerExtended.SteamId.ToString()).ToList();
			IEnumerable<Suspect> suspects = await _steamService.GetBanStatusForUserList(ids);
			var enumerableSuspects = suspects as IList<Suspect> ?? suspects.ToList();
			if (enumerableSuspects.Any())
			{
				List<string> whitelistIds =  await _cacheService.GetPlayersWhitelist();
				// Update player's flag
				foreach (Suspect suspect in enumerableSuspects)
				{
					PlayerExtended cheater = demo.Players.FirstOrDefault(p => p.SteamId.ToString() == suspect.SteamId);
					if (cheater != null && !whitelistIds.Contains(cheater.SteamId.ToString()))
					{
						if (suspect.GameBanCount > 0)
						{
							demo.HasCheater = true;
							cheater.IsOverwatchBanned = true;
						}
						if (suspect.VacBanned)
						{
							demo.HasCheater = true;
							cheater.IsVacBanned = true;
						}
					}
				}
			}
			return demo;
		}
		internal void WatchHighlightDemo(Demo demo, string steamId = null)
		{
			_arguments.Add("+playdemo");
			_arguments.Add(demo.Path);
			_arguments.Add(steamId ?? Properties.Settings.Default.SteamID.ToString());
			StartGame();
		}
		public async Task GenerateXls(Demo demo, string fileName)
		{
			SingleExport export = new SingleExport(demo);
			IWorkbook workbook = await export.Generate();
			FileStream sw = File.Create(fileName);
			workbook.Write(sw);
			sw.Close();
		}
		public EseaAnalyzer(Demo demo)
		{
			Parser = new DemoParser(File.OpenRead(demo.Path));
			// Reset to have update on UI
			demo.ResetStats();
			Demo = demo;
			RegisterEvents();
		}
		/// <summary>
		/// Write the JSON file with demo's data
		/// Overwrite the file if it exists
		/// </summary>
		/// <param name="demo"></param>
		/// <returns></returns>
		public async Task WriteDemoDataCache(Demo demo)
		{
			string pathDemoFileJson = _pathFolderCache + "\\" + demo.Id + ".json";

			string json = await Task.Factory.StartNew(() => JsonConvert.SerializeObject(demo, _settingsJson));

			File.WriteAllText(pathDemoFileJson, json);
		}
		public EbotAnalyzer(Demo demo)
		{
			Parser = new DemoParser(File.OpenRead(demo.Path));
			// Reset to have update on UI
			demo.ResetStats();
			Demo = demo;
			RegisterEvents();
			IsMatchStarted = true;
		}
		public HeatmapService(MapService mapService, Demo demo, ComboboxSelector eventSelector, ComboboxSelector sideSelector, PlayerExtended selectedPlayer, Round selectedRound)
		{
			MapService = mapService;
			_demo = demo;
			_eventSelector = eventSelector;
			_sideSelector = sideSelector;
			_selectedPlayer = selectedPlayer;
			_selectedRound = selectedRound;
		}
		/// <summary>
		/// Return the demo's data from its JSON file
		/// </summary>
		/// <param name="demo"></param>
		/// <returns></returns>
		public async Task<Demo> GetDemoDataFromCache(Demo demo)
		{
			string pathDemoFileJson = _pathFolderCache + "\\" + demo.Id + ".json";

			string json = File.ReadAllText(pathDemoFileJson);

			Demo demoFromJson = await Task.Factory.StartNew(() => JsonConvert.DeserializeObject<Demo>(json));

			return demoFromJson;
		}
		public async Task<double> GetTotalDamageAsync(Demo demo, List<long> steamIdList, List<int> roundNumberList)
		{
			double total = 0;
			await Task.Factory.StartNew(() =>
			{
				// get the total damage made at specific round(s) and player(s)
				total += demo.PlayersHurted.Where(e => e.AttackerSteamId != 0 && steamIdList.Contains(e.AttackerSteamId)
				&& roundNumberList.Contains(e.RoundNumber)).Sum(e => e.HealthDamage);
			});
			return total;
		}
		public EntryKillsTeamSheet(IWorkbook workbook, Demo demo)
		{
			Headers = new Dictionary<string, CellType>(){
				{ "Name", CellType.String },
				{ "Total", CellType.Numeric },
				{ "Win", CellType.Numeric },
				{ "Loss", CellType.Numeric },
				{ "Ratio", CellType.String }
			};
			Demo = demo;
			Sheet = workbook.CreateSheet("Entry Kills Teams");
		}
		public OpenKillsPlayerSheet(IWorkbook workbook, Demo demo)
		{
			Headers = new Dictionary<string, CellType>(){
				{ "Name", CellType.String },
				{ "SteamID", CellType.String },
				{ "Total", CellType.Numeric },
				{ "Win", CellType.Numeric },
				{ "Loss", CellType.Numeric },
				{ "Ratio", CellType.String }
			};
			Demo = demo;
			Sheet = workbook.CreateSheet("Open Kills Players");
		}
		public async Task<List<PlayerRoundStats>> GetRoundStats(Demo demo, Round round)
		{
			List<PlayerRoundStats> data = new List<PlayerRoundStats>();
			Dictionary<PlayerExtended, PlayerRoundStats> playerRoundStats = new Dictionary<PlayerExtended, PlayerRoundStats>();

			await Task.Factory.StartNew(() =>
			{
				foreach (PlayerExtended player in demo.Players)
				{
					if (!playerRoundStats.ContainsKey(player))
					{
						playerRoundStats.Add(player, new PlayerRoundStats());
						playerRoundStats[player].Name = player.Name;
						if (!player.StartMoneyRounds.ContainsKey(round.Number)) player.StartMoneyRounds[round.Number] = 0;
						if (!player.EquipementValueRounds.ContainsKey(round.Number)) player.EquipementValueRounds[round.Number] = 0;
						playerRoundStats[player].StartMoneyValue = player.StartMoneyRounds[round.Number];
						playerRoundStats[player].EquipementValue = player.EquipementValueRounds[round.Number];
					}

					foreach (WeaponFire e in demo.WeaponFired)
					{
						if (e.RoundNumber == round.Number && e.ShooterSteamId == player.SteamId)
						{
							playerRoundStats[player].ShotCount++;
						}
					}

					foreach (PlayerHurtedEvent e in demo.PlayersHurted)
					{
						if (e.RoundNumber == round.Number && e.AttackerSteamId != 0 && e.AttackerSteamId == player.SteamId)
						{
							playerRoundStats[player].DamageArmorCount += e.ArmorDamage;
							playerRoundStats[player].DamageHealthCount += e.HealthDamage;
							playerRoundStats[player].HitCount++;
						}
					}

					foreach (KillEvent e in round.Kills)
					{
						if (e.KillerSteamId == player.SteamId)
						{
							playerRoundStats[player].KillCount++;
							if (e.KillerVelocityZ > 0) playerRoundStats[player].JumpKillCount++;
						}
					}
				}

				data.AddRange(playerRoundStats.Select(keyValuePair => keyValuePair.Value));
			});
			return data;
		}
		public EntryKillsRoundSheet(IWorkbook workbook, Demo demo)
		{
			Headers = new Dictionary<string, CellType>(){
				{ "Number", CellType.Numeric },
				{ "Killer Name", CellType.String},
				{ "Killer SteamID", CellType.String },
				{ "Killed Name", CellType.String },
				{ "Killed SteamID", CellType.String },
				{ "Weapon", CellType.String },
				{ "Result", CellType.String }
			};
			Demo = demo;
			Sheet = workbook.CreateSheet("Entry Kills Rounds");
		}
		public PlayersSheet(IWorkbook workbook, Demo demo)
		{
			Headers = new Dictionary<string, CellType>(){
				{ "Name", CellType.String },
				{ "SteamID", CellType.String },
				{ "Rank", CellType.Numeric },
				{ "Team", CellType.String },
				{ "Kills", CellType.Numeric },
				{ "Assists", CellType.Numeric },
				{ "Deaths", CellType.Numeric },
				{ "K/D", CellType.Numeric },
				{ "HS", CellType.Numeric },
				{ "HS%", CellType.Numeric },
				{ "Team kill", CellType.Numeric },
				{ "Entry kill", CellType.Numeric },
				{ "Bomb planted", CellType.Numeric },
				{ "Bomb defused", CellType.Numeric },
				{ "MVP", CellType.Numeric },
				{ "Score", CellType.Numeric },
				{ "Rating", CellType.Numeric },
				{ "KPR", CellType.Numeric },
				{ "APR", CellType.Numeric },
				{ "DPR", CellType.Numeric },
				{ "ADR", CellType.Numeric },
				{ "TDH", CellType.Numeric },
				{ "TDA", CellType.Numeric },
				{ "5K", CellType.Numeric },
				{ "4K", CellType.Numeric },
				{ "3K", CellType.Numeric },
				{ "2K", CellType.Numeric },
				{ "1K", CellType.Numeric },
				{ "Crouch kill", CellType.Numeric },
				{ "Jump kill", CellType.Numeric },
				{ "1v1", CellType.Numeric },
				{ "1v2", CellType.Numeric },
				{ "1v3", CellType.Numeric },
				{ "1v4", CellType.Numeric },
				{ "1v5", CellType.Numeric },
				{ "Flashbang", CellType.Numeric },
				{ "Smoke", CellType.Numeric },
				{ "HE", CellType.Numeric },
				{ "Decoy", CellType.Numeric },
				{ "Molotov", CellType.Numeric },
				{ "Incendiary", CellType.Numeric },
				{ "VAC", CellType.Boolean },
				{ "OW", CellType.Boolean },
			};
			Demo = demo;
			Sheet = workbook.CreateSheet("Players");
		}
		public GeneralSheet(IWorkbook workbook, Demo demo)
		{
			Headers = new Dictionary<string, CellType>(){
				{ "Filename", CellType.String },
				{ "Type", CellType.String },
				{ "Source", CellType.String },
				{ "Map", CellType.String },
				{ "Hostname", CellType.String },
				{ "Client", CellType.String },
				{ "Server Tickrate", CellType.Numeric },
				{ "Framerate", CellType.Numeric },
				{ "Duration", CellType.Numeric },
				{ "Name team 1", CellType.String },
				{ "Name team 2", CellType.String },
				{ "Score team 1", CellType.Numeric },
				{ "Score team 2", CellType.Numeric },
				{ "Score 1st half team 1", CellType.Numeric },
				{ "Score 1st half team 2", CellType.Numeric },
				{ "Score 2nd half team 1", CellType.Numeric },
				{ "Score 2nd half team 2", CellType.Numeric },
				{ "Winner", CellType.String },
				{ "Kills", CellType.Numeric },
				{ "Assists", CellType.Numeric },
				{ "5K", CellType.Numeric },
				{ "4K", CellType.Numeric },
				{ "3K", CellType.Numeric },
				{ "2K", CellType.Numeric },
				{ "1K", CellType.Numeric },
				{ "Average Damage Per Round", CellType.Numeric },
				{ "Total Damage Health", CellType.Numeric },
				{ "Total Damage Armor", CellType.Numeric },
				{ "Clutch", CellType.Numeric },
				{ "Bomb Defused", CellType.Numeric },
				{ "Bomb Exploded", CellType.Numeric },
				{ "Bomb Planted", CellType.Numeric },
				{ "Flashbang", CellType.Numeric },
				{ "Smoke", CellType.Numeric },
				{ "HE", CellType.Numeric },
				{ "Decoy", CellType.Numeric },
				{ "Molotov", CellType.Numeric },
				{ "Incendiary", CellType.Numeric },
				{ "Shots", CellType.Numeric },
				{ "Hits", CellType.Numeric },
				{ "Round", CellType.Numeric },
				{ "Comment", CellType.String },
				{ "Cheater", CellType.Boolean }
			};
			Demo = demo;
			Sheet = workbook.CreateSheet("General");
		}
		public async Task<Demo> AnalyzeDemo(Demo demo)
		{
			if (!File.Exists(demo.Path))
			{
				// Demo may be moved to an other folder, just clear cache
				await _cacheService.RemoveDemo(demo);
			}

			DemoAnalyzer analyzer = DemoAnalyzer.Factory(demo);
			
			demo = await analyzer.AnalyzeDemoAsync();

			await _cacheService.WriteDemoDataCache(demo);

			return demo;
		}
		public static DemoAnalyzer Factory(Demo demo)
		{
			switch (demo.SourceName)
			{
				case "valve":
					return new ValveAnalyzer(demo);
				case "esea":
					return new EseaAnalyzer(demo);
				case "ebot":
				case "faceit":
					return new EbotAnalyzer(demo);
				case "cevo":
					return new CevoAnalyzer(demo);
				case "pov":
					return null;
				default:
					return null;
			}
		}
		public Task<List<RoundEvent>> GetTimeLineEventList(Demo demo, Round round)
		{
			List<RoundEvent> roundEventList = new List<RoundEvent>();
			Random ran = new Random();
			for (int i = 0; i < 7; i++)
			{
				int timeEvent = ran.Next(1, 100);
				roundEventList.Add(new RoundEvent
				{
					StartTime = DateTime.Today.AddSeconds(timeEvent),
					EndTime = DateTime.Today.AddSeconds(timeEvent + 1),
					Category = "Kill",
					Message = "Player killed Player",
					Type = "kill"
				});
			}

			return Task.FromResult(roundEventList);
		}
		private static void UpdateMapStats(Demo demo, Models.Map map)
		{
			map.MatchCount++;
			map.RoundCount += demo.Rounds.Count();
			map.BombPlantedCount += demo.BombPlantedCount;
			map.BombDefusedCount += demo.BombDefusedCount;
			map.BombExplodedCount += demo.BombExplodedCount;
			foreach (Round round in demo.Rounds)
			{
				if (round.WinnerSide == Team.CounterTerrorist) map.WinCounterTerroritsCount++;
				if (round.WinnerSide == Team.Terrorist) map.WinTerroristCount++;
				if (round.SideTrouble != Team.Spectate)
				{
					switch (round.Type)
					{
						case RoundType.ECO:
							map.WinEcoRoundCount++;
							break;
						case RoundType.SEMI_ECO:
							map.WinSemiEcoCount++;
							break;
						case RoundType.FORCE_BUY:
							map.WinForceBuyCount++;
							break;
						case RoundType.PISTOL_ROUND:
							map.WinPistolRoundCount++;
							break;
					}
				}
			}
			foreach (BombPlantedEvent plantedEvent in demo.BombPlanted)
			{
				if (plantedEvent.Site == "A")
				{
					map.BombPlantedOnACount++;
				}
				else
				{
					map.BombPlantedOnBCount++;
				}
			}
		}
		public Task<Demo> GetDemoDataFromCache(Demo demo)
		{
			demo.Id = "de_dust25445648778447878";
			demo.Name = "esea_nip_vs_titan.dem";
			demo.Tickrate = 128;
			demo.MapName = "de_dust2";
			demo.ClientName = "localhost";
			demo.Hostname = "local";
			demo.OneKillCount = 190;
			demo.TwoKillCount = 80;
			demo.ThreeKillCount = 25;
			demo.FourKillCount = 3;
			demo.FiveKillCount = 1;
			demo.Path = "C:\\mydemo.dem";
			demo.ScoreTeam1 = 16;
			demo.ScoreTeam2 = 6;
			demo.Type = "GOTV";

			return Task.FromResult(demo);
		}
		public async Task<double> GetHitGroupDamageAsync(Demo demo, Hitgroup hitGroup, List<long> steamIdList, List<int> roundNumberList)
		{
			double result = 0;
			await Task.Factory.StartNew(() =>
			{
				// get the total damage made at the specific hitgroup
				switch (hitGroup)
				{
					case Hitgroup.Chest:
						result += demo.PlayersHurted.Where(e => steamIdList.Contains(e.AttackerSteamId) && e.HitGroup == Hitgroup.Chest
						&& roundNumberList.Contains(e.RoundNumber)).Sum(e => e.HealthDamage);
						break;
					case Hitgroup.Head:
						result += demo.PlayersHurted.Where(e => steamIdList.Contains(e.AttackerSteamId) && e.HitGroup == Hitgroup.Head
						&& roundNumberList.Contains(e.RoundNumber)).Sum(e => e.HealthDamage);
						break;
					case Hitgroup.LeftArm:
						result += demo.PlayersHurted.Where(e => steamIdList.Contains(e.AttackerSteamId) && e.HitGroup == Hitgroup.LeftArm
						&& roundNumberList.Contains(e.RoundNumber)).Sum(e => e.HealthDamage);
						break;
					case Hitgroup.RightArm:
						result += demo.PlayersHurted.Where(e => steamIdList.Contains(e.AttackerSteamId) && e.HitGroup == Hitgroup.RightArm
						&& roundNumberList.Contains(e.RoundNumber)).Sum(e => e.HealthDamage);
						break;
					case Hitgroup.LeftLeg:
						result += demo.PlayersHurted.Where(e => steamIdList.Contains(e.AttackerSteamId) && e.HitGroup == Hitgroup.LeftLeg
						&& roundNumberList.Contains(e.RoundNumber)).Sum(e => e.HealthDamage);
						break;
					case Hitgroup.RightLeg:
						result += demo.PlayersHurted.Where(e => steamIdList.Contains(e.AttackerSteamId) && e.HitGroup == Hitgroup.RightLeg
						&& roundNumberList.Contains(e.RoundNumber)).Sum(e => e.HealthDamage);
						break;
					case Hitgroup.Stomach:
						result += demo.PlayersHurted.Where(e => steamIdList.Contains(e.AttackerSteamId) && e.HitGroup == Hitgroup.Stomach
						&& roundNumberList.Contains(e.RoundNumber)).Sum(e => e.HealthDamage);
						break;
				}
			});

			return result;
		}
		public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
		{
			List<Demo> demos = new List<Demo>();

			while (reader.Read())
			{
				if (reader.TokenType != JsonToken.StartObject) break;

				JObject obj = (JObject)serializer.Deserialize(reader);

				Demo demo = new Demo
				{
					Id = Convert.ToString(((JValue) obj["Id"]).Value),
					Comment = Convert.ToString(((JValue) obj["Comment"]).Value),
					Status = Convert.ToString(((JValue)obj["status"]).Value)
				};
				if(!string.IsNullOrEmpty(demo.Comment) || !string.IsNullOrEmpty(demo.Status)) demos.Add(demo);
			}

			return demos;
		}
		public Task<List<PlayerRoundStats>> GetRoundStats(Demo demo, Round round)
		{
			List<PlayerRoundStats> stats = new List<PlayerRoundStats>();
			Random ran = new Random();
			for (int i = 0; i < 10; i++)
			{
				stats.Add(new PlayerRoundStats
				{
					Name = "Player " + (i + 1),
					DamageArmorCount = ran.Next(300),
					DamageHealthCount = ran.Next(300),
					EquipementValue = ran.Next(8000),
					HitCount = ran.Next(8),
					JumpKillCount = 0,
					KillCount = ran.Next(9),
					ShotCount = ran.Next(200),
					StartMoneyValue = ran.Next(16000)
				});
			}

			return Task.FromResult(stats);
		}
		public RoundsSheet(IWorkbook workbook, Demo demo)
		{
			Headers = new Dictionary<string, CellType>(){
				{ "Number", CellType.Numeric },
				{ "Tick", CellType.Numeric},
				{ "Duration (s)", CellType.Numeric},
				{ "Winner Clan Name", CellType.String },
				{ "Winner", CellType.String },
				{ "End reason", CellType.String },
				{ "Type", CellType.String },
				{ "Side", CellType.String },
				{ "Team", CellType.String },
				{ "Kills", CellType.Numeric },
				{ "1K", CellType.Numeric },
				{ "2K", CellType.Numeric },
				{ "3K", CellType.Numeric },
				{ "4K", CellType.Numeric },
				{ "5K", CellType.Numeric },
				{ "Jump kills", CellType.Numeric },
				{ "ADP", CellType.Numeric },
				{ "TDH", CellType.Numeric },
				{ "TDA", CellType.Numeric },
				{ "Bomb Exploded", CellType.Numeric },
				{ "Bomb planted", CellType.Numeric },
				{ "Bomb defused", CellType.Numeric },
				{ "Start money team 1", CellType.Numeric },
				{ "Start money team 2", CellType.Numeric },
				{ "Equipement value team 1", CellType.Numeric },
				{ "Equipement value team 2", CellType.Numeric },
				{ "Flashbang", CellType.Numeric },
				{ "Smoke", CellType.Numeric },
				{ "HE", CellType.Numeric },
				{ "Decoy", CellType.Numeric },
				{ "Molotov", CellType.Numeric },
				{ "Incendiary", CellType.Numeric }
			};
			Demo = demo;
			Sheet = workbook.CreateSheet("Rounds");
		}
		public OpenKillsRoundSheet(IWorkbook workbook, Demo demo)
		{
			_demo = demo;
			_sheet = workbook.CreateSheet("Open Kills Rounds");
		}
		public async Task<List<RoundEvent>> GetTimeLineEventList(Demo demo, Round round)
		{
			List<RoundEvent> roundEventList = new List<RoundEvent>();
			await Task.Factory.StartNew(() =>
			{
				foreach (KillEvent e in demo.Kills)
				{
					if (e.RoundNumber == round.Number)
					{
						roundEventList.Add(new RoundEvent
						{
							StartTime = DateTime.Today.AddSeconds(e.Seconds - round.StartTimeSeconds),
							EndTime = DateTime.Today.AddSeconds(e.Seconds - round.StartTimeSeconds + 1),
							Category = "Kills",
							Message = e.KillerName + " killed " + e.KilledName,
							Type = "kill"
						});
					}
				}
				foreach (WeaponFire e in demo.WeaponFired)
				{
					if (e.RoundNumber == round.Number)
					{
						string type = string.Empty;
						string message = string.Empty;
						string category = string.Empty;
						switch (e.Weapon.Element)
						{
							case EquipmentElement.Flash:
								type = "flash";
								category = "Flashbang";
								message = e.ShooterName + " thrown a flashbang";
								break;
							case EquipmentElement.Smoke:
								type = "smoke";
								category = "Smoke";
								message = e.ShooterName + " thrown a smoke";
								break;
							case EquipmentElement.Decoy:
								type = "decoy";
								category = "Decoy";
								message = e.ShooterName + " thrown a decoy";
								break;
							case EquipmentElement.HE:
								type = "he";
								category = "HE";
								message = e.ShooterName + " thrown a HE grenade";
								break;
							case EquipmentElement.Molotov:
								type = "molotov";
								category = "Molotov";
								message = e.ShooterName + " thrown a molotov";
								break;
							case EquipmentElement.Incendiary:
								type = "incendiary";
								category = "Molotov";
								message = e.ShooterName + " thrown an incendiary";
								break;
						}

						if (type != string.Empty)
						{
							roundEventList.Add(new RoundEvent
							{
								StartTime = DateTime.Today.AddSeconds(e.Seconds - round.StartTimeSeconds),
								EndTime = DateTime.Today.AddSeconds(e.Seconds - round.StartTimeSeconds + 1),
								Category = category,
								Message = message,
								Type = type
							});
						}
					}
				}

				if (round.BombPlanted != null)
				{
					roundEventList.Add(new RoundEvent
					{
						StartTime = DateTime.Today.AddSeconds(round.BombPlanted.Seconds - round.StartTimeSeconds),
						EndTime = DateTime.Today.AddSeconds(round.BombPlanted.Seconds - round.StartTimeSeconds + 1),
						Category = "Bomb",
						Message = round.BombPlanted.PlanterName + " planted the bomb on bomb site " + round.BombPlanted.Site,
						Type = "bomb_planted"
					});
				}

				if (round.BombDefused != null)
				{
					roundEventList.Add(new RoundEvent
					{
						StartTime = DateTime.Today.AddSeconds(round.BombDefused.Seconds - round.StartTimeSeconds),
						EndTime = DateTime.Today.AddSeconds(round.BombDefused.Seconds - round.StartTimeSeconds + 1),
						Category = "Bomb",
						Message = round.BombDefused.DefuserName + " defused the bomb on bomb site " + round.BombDefused.Site,
						Type = "bomb_defused"
					});
				}
				if (round.BombExploded != null)
				{
					roundEventList.Add(new RoundEvent
					{
						StartTime = DateTime.Today.AddSeconds(round.BombExploded.Seconds - round.StartTimeSeconds),
						EndTime = DateTime.Today.AddSeconds(round.BombExploded.Seconds - round.StartTimeSeconds + 1),
						Category = "Bomb",
						Message = "The bomb exploded on bomb site " + round.BombExploded.Site,
						Type = "bomb_exploded"
					});
				}
			});
			return roundEventList;
		}
		public Task<double> GetTotalDamageAsync(Demo demo, List<long> steamIdList, List<int> roundNumberList)
		{
			return Task.FromResult(500.5);
		}
		/// <summary>
		/// Return PositionPoint for each players determined by selection
		/// </summary>
		/// <param name="demo"></param>
		/// <param name="teamSelector"></param>
		/// <param name="selectedPlayer"></param>
		/// <param name="round"></param>
		/// <returns></returns>
		public async Task<List<List<PositionPoint>>> GetPoints(Demo demo, ComboboxSelector teamSelector, PlayerExtended selectedPlayer, Round round)
		{
			List<List<PositionPoint>> points = new List<List<PositionPoint>>();

			if (teamSelector != null)
			{
				switch (teamSelector.Id)
				{
					case "CT":
						demo.PositionsPoint.Reverse();

						foreach (PlayerExtended playerExtended in demo.Players)
						{
							List<PositionPoint> playerPoints = new List<PositionPoint>();

							for (int i = demo.PositionsPoint.Count - 1; i >= 0; i--)
							{
								if (!demo.PositionsPoint[i].Round.Equals(round)) continue;

								// Keep kills from terrorists
								if (demo.PositionsPoint[i].Event != null
									&& demo.PositionsPoint[i].Team == Team.Terrorist
									&& demo.PositionsPoint[i].Event.GetType() == typeof(KillEvent))
								{
									KillEvent e = (KillEvent)demo.PositionsPoint[i].Event;
									if (e.DeathPerson.Equals(playerExtended))
									{
										playerPoints.Add(demo.PositionsPoint[i]);
										demo.PositionsPoint.RemoveAt(i);
										continue;
									}
								}

								if (demo.PositionsPoint[i].Team != Team.CounterTerrorist) continue;

								// Molotov started
								if (demo.PositionsPoint[i].Event != null
									&& demo.PositionsPoint[i].Event.GetType() == typeof(MolotovFireStartedEvent))
								{
									MolotovFireStartedEvent e = (MolotovFireStartedEvent)demo.PositionsPoint[i].Event;
									if (e.Thrower.Equals(playerExtended))
									{
										playerPoints.Add(demo.PositionsPoint[i]);
										demo.PositionsPoint.RemoveAt(i);
										continue;
									}
								}

								// Molotov ended
								if (demo.PositionsPoint[i].Event != null
									&& demo.PositionsPoint[i].Event.GetType() == typeof(MolotovFireEndedEvent))
								{
									MolotovFireEndedEvent e = (MolotovFireEndedEvent)demo.PositionsPoint[i].Event;
									if (e.Thrower.Equals(playerExtended))
									{
										playerPoints.Add(demo.PositionsPoint[i]);
										demo.PositionsPoint.RemoveAt(i);
										continue;
									}
								}

								if (demo.PositionsPoint[i].Player != null
								&& demo.PositionsPoint[i].Player.Equals(playerExtended))
								{
									playerPoints.Add(demo.PositionsPoint[i]);
									demo.PositionsPoint.RemoveAt(i);
								}
							}
							if (playerPoints.Any()) points.Add(playerPoints);
						}
						break;
					case "T":
						demo.PositionsPoint.Reverse();

						foreach (PlayerExtended playerExtended in demo.Players)
						{
							List<PositionPoint> playerPoints = new List<PositionPoint>();

							for (int i = demo.PositionsPoint.Count - 1; i >= 0; i--)
							{
								if (!demo.PositionsPoint[i].Round.Equals(round)) continue;

								// Keep kills from CT
								if (demo.PositionsPoint[i].Event != null
									&& demo.PositionsPoint[i].Team == Team.CounterTerrorist
									&& demo.PositionsPoint[i].Event.GetType() == typeof(KillEvent))
								{
									KillEvent e = (KillEvent)demo.PositionsPoint[i].Event;
									if (e.DeathPerson.Equals(playerExtended))
									{
										playerPoints.Add(demo.PositionsPoint[i]);
										demo.PositionsPoint.RemoveAt(i);
										continue;
									}
								}

								if (demo.PositionsPoint[i].Team != Team.Terrorist) continue;

								// Molotov started
								if (demo.PositionsPoint[i].Event != null
									&& demo.PositionsPoint[i].Event.GetType() == typeof(MolotovFireStartedEvent))
								{
									MolotovFireStartedEvent e = (MolotovFireStartedEvent)demo.PositionsPoint[i].Event;
									if (e.Thrower.Equals(playerExtended))
									{
										playerPoints.Add(demo.PositionsPoint[i]);
										demo.PositionsPoint.RemoveAt(i);
										continue;
									}
								}

								// Molotov ended
								if (demo.PositionsPoint[i].Event != null
									&& demo.PositionsPoint[i].Event.GetType() == typeof(MolotovFireEndedEvent))
								{
									MolotovFireEndedEvent e = (MolotovFireEndedEvent)demo.PositionsPoint[i].Event;
									if (e.Thrower.Equals(playerExtended))
									{
										playerPoints.Add(demo.PositionsPoint[i]);
										demo.PositionsPoint.RemoveAt(i);
										continue;
									}
								}

								if (demo.PositionsPoint[i].Player != null
								&& demo.PositionsPoint[i].Player.Equals(playerExtended))
								{
									playerPoints.Add(demo.PositionsPoint[i]);
									demo.PositionsPoint.RemoveAt(i);
								}
							}
							if (playerPoints.Any()) points.Add(playerPoints);
						}
						break;
					case "BOTH":
						points.AddRange(
							demo.Players.Select(
								playerExtended => demo.PositionsPoint.Where(
									point => point.Round.Number == round.Number
									&& point.Player.SteamId == playerExtended.SteamId).ToList())
									.Where(pts => pts.Any()));
						break;
				}
			}

			if (selectedPlayer != null)
			{
				await Task.Run(delegate
				{
					List<PositionPoint> pt = demo.PositionsPoint.ToList().Where(
						positionPoint => positionPoint.Player.SteamId == selectedPlayer.SteamId
						&& positionPoint.Round.Number == round.Number
						|| (positionPoint.Event != null
						&& positionPoint.Event.GetType() == typeof(KillEvent))
						&& positionPoint.Round.Number == round.Number).ToList();
					if (pt.Any()) points.Add(pt);
				});
			}

			// Set players color
			await Task.Run(delegate
			{
				int index = 0;
				foreach (List<PositionPoint> positionPoints in points.ToList())
				{
					foreach (PositionPoint positionPoint in positionPoints)
					{
						positionPoint.X = MapService.CalculatePointToResolutionX(positionPoint.X);
						positionPoint.Y = MapService.CalculatePointToResolutionY(positionPoint.Y);
						positionPoint.Color = ColorToInt(_colors[index]);
					}
					index++;
				}
			});

			return points;
		}