diff --git a/README.md b/README.md index f264375..3228583 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,168 @@ -# EntityFramework_LoL +# prepaLoL +## Diagramme de classes du modèle +```mermaid +classDiagram +class LargeImage{ + +/Base64 : string +} +class Champion{ + +/Name : string + +/Bio : string + +/Icon : string + +/Characteristics : Dictionary~string, int~ + ~ AddSkin(skin : Skin) bool + ~ RemoveSkin(skin: Skin) bool + + AddSkill(skill: Skill) bool + + RemoveSkill(skill: Skill) bool + + AddCharacteristics(someCharacteristics : params Tuple~string, int~[]) + + RemoveCharacteristics(label : string) bool + + this~label : string~ : int? +} +Champion --> "1" LargeImage : Image +class ChampionClass{ + <> + Unknown, + Assassin, + Fighter, + Mage, + Marksman, + Support, + Tank, +} +Champion --> "1" ChampionClass : Class +class Skin{ + +/Name : string + +/Description : string + +/Icon : string + +/Price : float +} +Skin --> "1" LargeImage : Image +Champion "1" -- "*" Skin +class Skill{ + +/Name : string + +/Description : string +} +class SkillType{ + <> + Unknown, + Basic, + Passive, + Ultimate, +} +Skill --> "1" SkillType : Type +Champion --> "*" Skill +class Rune{ + +/Name : string + +/Description : string +} +Rune --> "1" LargeImage : Image +class RuneFamily{ + <> + Unknown, + Precision, + Domination +} +Rune --> "1" RuneFamily : Family +class Category{ + <> + Major, + Minor1, + Minor2, + Minor3, + OtherMinor1, + OtherMinor2 +} +class RunePage{ + +/Name : string + +/this[category : Category] : Rune? + - CheckRunes(newRuneCategory : Category) + - CheckFamilies(cat1 : Category, cat2 : Category) bool? + - UpdateMajorFamily(minor : Category, expectedValue : bool) +} +RunePage --> "*" Rune : Dictionary~Category,Rune~ +``` + +## Diagramme de classes des interfaces de gestion de l'accès aux données +```mermaid +classDiagram +direction LR; +class IGenericDataManager~T~{ + <> + GetNbItems() Task~int~ + GetItems(index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~T~~ + GetNbItemsByName(substring : string) + GetItemsByName(substring : string, index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~T~~ + UpdateItem(oldItem : T, newItem : T) Task~T~~ + AddItem(item : T) Task~T~ + DeleteItem(item : T) Task~bool~ +} +class IChampionsManager{ + <> + GetNbItemsByCharacteristic(charName : string) + GetItemsByCharacteristic(charName : string, index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~Champion?~~ + GetNbItemsByClass(championClass : ChampionClass) + GetItemsByClass(championClass : ChampionClass, index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~Champion?~~ + GetNbItemsBySkill(skill : Skill?) + GetItemsBySkill(skill : Skill?, index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~Champion?~~ + GetNbItemsBySkill(skill : string) + GetItemsBySkill(skill : string, index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~Champion?~~ + GetNbItemsByRunePage(runePage : RunePage?) + GetItemsByRunePage(runePage : RunePage?, index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~Champion?~~ +} +class ISkinsManager{ + <> + GetNbItemsByChampion(champion : Champion?) + GetItemsByChampion(champion : Champion?, index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~Skin?~~ +} +class IRunesManager{ + <> + GetNbItemsByFamily(family : RuneFamily) + GetItemsByFamily(family : RuneFamily, index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~Rune?~~ +} +class IRunePagesManager{ + <> + GetNbItemsByRune(rune : Rune?) + GetItemsByRune(rune : Rune?, index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~RunePage?~~ + GetNbItemsByChampion(champion : Champion?) + GetItemsByChampion(champion : Champion?, index : int, count : int, orderingPropertyName : string?, descending : bool) Task~IEnumerable~RunePage?~~ +} + +IGenericDataManager~Champion?~ <|.. IChampionsManager : T--Champion? +IGenericDataManager~Skin?~ <|.. ISkinsManager : T--Skin? +IGenericDataManager~Rune?~ <|.. IRunesManager : T--Rune? +IGenericDataManager~RunePage?~ <|.. IRunePagesManager : T--RunePage? +class IDataManager{ + <> +} +IChampionsManager <-- IDataManager : ChampionsMgr +ISkinsManager <-- IDataManager : SkinsMgr +IRunesManager <-- IDataManager : RunesMgr +IRunePagesManager <-- IDataManager : RunePagesMgr +``` + +## Diagramme de classes simplifié du Stub +```mermaid +classDiagram +direction TB; + +IDataManager <|.. StubData + +ChampionsManager ..|> IChampionsManager +StubData --> ChampionsManager + +RunesManager ..|> IRunesManager +StubData --> RunesManager + +RunePagesManager ..|> IRunePagesManager +StubData --> RunePagesManager + +SkinsManager ..|> ISkinsManager +StubData --> SkinsManager + +StubData --> RunesManager +StubData --> "*" Champion +StubData --> "*" Rune +StubData --> "*" RunePages +StubData --> "*" Skins +``` \ No newline at end of file diff --git a/Sources/LeagueOfLegends.sln b/Sources/LeagueOfLegends.sln new file mode 100644 index 0000000..0ea57ff --- /dev/null +++ b/Sources/LeagueOfLegends.sln @@ -0,0 +1,51 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 25.0.1704.2 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{2960F9BA-49DE-494D-92E3-CE5A794BA1A9}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{C76D0C23-1FFA-4963-93CD-E12BD643F030}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleTests", "Tests\ConsoleTests\ConsoleTests.csproj", "{1889FA6E-B7C6-416E-8628-9449FB9070B9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Shared", "Shared\Shared.csproj", "{3B720C0C-53FE-4642-A2DB-87FD8634CD74}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Stub", "Stub", "{2C607793-B163-4731-A4D1-AFE8A7C4C170}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StubLib", "StubLib\StubLib.csproj", "{B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2960F9BA-49DE-494D-92E3-CE5A794BA1A9}.Release|Any CPU.Build.0 = Release|Any CPU + {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1889FA6E-B7C6-416E-8628-9449FB9070B9}.Release|Any CPU.Build.0 = Release|Any CPU + {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3B720C0C-53FE-4642-A2DB-87FD8634CD74}.Release|Any CPU.Build.0 = Release|Any CPU + {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {92F3083D-793F-4552-8A9A-0AD6534159C9} + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {1889FA6E-B7C6-416E-8628-9449FB9070B9} = {C76D0C23-1FFA-4963-93CD-E12BD643F030} + {B01D7EF2-2D64-409A-A29A-61FB7BB7A9DB} = {2C607793-B163-4731-A4D1-AFE8A7C4C170} + EndGlobalSection +EndGlobal diff --git a/Sources/Model/Champion.cs b/Sources/Model/Champion.cs new file mode 100644 index 0000000..3a50658 --- /dev/null +++ b/Sources/Model/Champion.cs @@ -0,0 +1,151 @@ +using System.Collections.Immutable; +using System.Collections.ObjectModel; +using System.Numerics; +using System.Text; + +namespace Model; +public class Champion : IEquatable +{ + public string Name + { + get => name; + private init + { + if(string.IsNullOrWhiteSpace(value)) + { + name = "Unknown"; + return; + } + name = value; + } + } + private readonly string name = null!; + + public string Bio + { + get => bio; + set + { + if(value == null) + { + bio = ""; + return; + } + bio = value; + } + } + private string bio = ""; + + public ChampionClass Class { get; set; } + + public string Icon { get; set; } + + public LargeImage Image { get; set; } + + public Champion(string name, ChampionClass champClass = ChampionClass.Unknown, string icon = "", string image = "", string bio = "") + { + Name = name; + Class = champClass; + Icon = icon; + Image = new LargeImage(image); + Bio = bio; + Characteristics = new ReadOnlyDictionary(characteristics); + Skins = new ReadOnlyCollection(skins); + } + + public ReadOnlyCollection Skins { get; private set; } + private List skins = new (); + + public ReadOnlyDictionary Characteristics { get; private set; } + private readonly Dictionary characteristics = new Dictionary(); + + public ImmutableHashSet Skills => skills.ToImmutableHashSet(); + private HashSet skills = new HashSet(); + + internal bool AddSkin(Skin skin) + { + if (skins.Contains(skin)) + return false; + skins.Add(skin); + return true; + } + + internal bool RemoveSkin(Skin skin) + => skins.Remove(skin); + + public bool AddSkill(Skill skill) + => skills.Add(skill); + + public bool RemoveSkill(Skill skill) + => skills.Remove(skill); + + public void AddCharacteristics(params Tuple[] someCharacteristics) + { + foreach(var c in someCharacteristics) + { + characteristics[c.Item1] = c.Item2; + } + } + + public bool RemoveCharacteristics(string label) + => characteristics.Remove(label); + + public int? this[string label] + { + get + { + if(!characteristics.TryGetValue(label, out int value)) return null; + else return value; + } + set + { + if(!value.HasValue) + { + RemoveCharacteristics(label); + return; + } + characteristics[label] = value.Value; + } + } + + public override bool Equals(object? obj) + { + if(ReferenceEquals(obj, null)) return false; + if(ReferenceEquals(obj, this)) return true; + if(GetType() != obj.GetType()) return false; + return Equals(obj as Champion); + } + + public override int GetHashCode() + => Name.GetHashCode() % 997; + + public bool Equals(Champion? other) + => Name.Equals(other?.Name); + + public override string ToString() + { + StringBuilder sb = new StringBuilder($"{Name} ({Class})"); + if(!string.IsNullOrWhiteSpace(bio)) + { + sb.AppendLine($"\t{bio}"); + } + if(characteristics.Any()) + { + sb.AppendLine("\tCharacteristics:"); + foreach(var characteristic in characteristics) + { + sb.AppendLine($"\t\t{characteristic.Key} - {characteristic.Value}"); + } + } + if(skills.Any()) + { + sb.AppendLine("\tSkills:"); + foreach(var skill in Skills) + { + sb.AppendLine($"\t\t{skill.Name} - {skill.Description}"); + } + } + return sb.ToString(); + } +} + diff --git a/Sources/Model/IDataManager.cs b/Sources/Model/IDataManager.cs new file mode 100644 index 0000000..a185e15 --- /dev/null +++ b/Sources/Model/IDataManager.cs @@ -0,0 +1,53 @@ +using System; +using Shared; + +namespace Model +{ + public interface IDataManager + { + IChampionsManager ChampionsMgr { get; } + ISkinsManager SkinsMgr { get; } + IRunesManager RunesMgr { get; } + IRunePagesManager RunePagesMgr { get; } + } + + public interface IChampionsManager : IGenericDataManager + { + Task GetNbItemsByCharacteristic(string charName); + Task> GetItemsByCharacteristic(string charName, int index, int count, string? orderingPropertyName = null, bool descending = false); + + Task GetNbItemsByClass(ChampionClass championClass); + Task> GetItemsByClass(ChampionClass championClass, int index, int count, string? orderingPropertyName = null, bool descending = false); + + Task GetNbItemsBySkill(Skill? skill); + Task> GetItemsBySkill(Skill? skill, int index, int count, string? orderingPropertyName = null, bool descending = false); + + Task GetNbItemsByRunePage(RunePage? runePage); + Task> GetItemsByRunePage(RunePage? runePage, int index, int count, string? orderingPropertyName = null, bool descending = false); + + Task GetNbItemsBySkill(string skill); + Task> GetItemsBySkill(string skill, int index, int count, string? orderingPropertyName = null, bool descending = false); + } + + public interface ISkinsManager : IGenericDataManager + { + Task GetNbItemsByChampion(Champion? champion); + Task> GetItemsByChampion(Champion? champion, int index, int count, string? orderingPropertyName = null, bool descending = false); + } + + public interface IRunesManager : IGenericDataManager + { + Task GetNbItemsByFamily(RuneFamily family); + Task> GetItemsByFamily(RuneFamily family, int index, int count, string? orderingPropertyName = null, bool descending = false); + } + + public interface IRunePagesManager : IGenericDataManager + { + Task GetNbItemsByRune(Rune? rune); + Task> GetItemsByRune(Rune? rune, int index, int count, string? orderingPropertyName = null, bool descending = false); + + Task GetNbItemsByChampion(Champion? champion); + Task> GetItemsByChampion(Champion? champion, int index, int count, string? orderingPropertyName = null, bool descending = false); + } +} + diff --git a/Sources/Model/LargeImage.cs b/Sources/Model/LargeImage.cs new file mode 100644 index 0000000..56d6696 --- /dev/null +++ b/Sources/Model/LargeImage.cs @@ -0,0 +1,28 @@ +using System; +namespace Model +{ + public class LargeImage : IEquatable + { + public string Base64 { get; set; } + + public LargeImage(string base64) + { + Base64 = base64; + } + + public bool Equals(LargeImage? other) + => other != null && other.Base64.Equals(Base64); + + public override bool Equals(object? obj) + { + if(ReferenceEquals(obj, null)) return false; + if(ReferenceEquals(obj!, this)) return true; + if(GetType() != obj!.GetType()) return false; + return Equals(obj! as LargeImage); + } + + public override int GetHashCode() + => Base64.Substring(0, 10).GetHashCode(); + } +} + diff --git a/Sources/Model/Model.csproj b/Sources/Model/Model.csproj new file mode 100644 index 0000000..27b2839 --- /dev/null +++ b/Sources/Model/Model.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + + + + + + + + + + + + diff --git a/Sources/Model/Rune.cs b/Sources/Model/Rune.cs new file mode 100644 index 0000000..7b5047b --- /dev/null +++ b/Sources/Model/Rune.cs @@ -0,0 +1,69 @@ +using System; + +namespace Model +{ + public class Rune : IEquatable + { + public string Name + { + get => name; + private init + { + if(string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("a Rune must have a name"); + } + name = value; + } + } + private readonly string name = null!; + + public string Description + { + get => description; + set + { + if(string.IsNullOrWhiteSpace(value)) + { + description = ""; + return; + } + description = value; + } + } + private string description = ""; + + public RuneFamily Family { get; set; } + + public string Icon { get; set; } + + public LargeImage Image { get; set; } + + public Rune(string name, RuneFamily family, string icon = "", string image = "", string description = "") + { + Name = name; + Family = family; + Icon = icon; + Image = new LargeImage(image); + Description = description; + } + + public override bool Equals(object? obj) + { + if(ReferenceEquals(obj, null)) return false; + if(ReferenceEquals(obj, this)) return true; + if(GetType() != obj.GetType()) return false; + return Equals(obj as Rune); + } + + public bool Equals(Rune? other) + => Name.Equals(other?.Name); + + public override int GetHashCode() + => Name.GetHashCode() % 281; + + public override string ToString() + => $"{Name} ({Family})"; + } +} + diff --git a/Sources/Model/RunePage.Category.cs b/Sources/Model/RunePage.Category.cs new file mode 100644 index 0000000..1047c0e --- /dev/null +++ b/Sources/Model/RunePage.Category.cs @@ -0,0 +1,17 @@ +using System; +namespace Model +{ + public partial class RunePage + { + public enum Category + { + Major, + Minor1, + Minor2, + Minor3, + OtherMinor1, + OtherMinor2 + } + } +} + diff --git a/Sources/Model/RunePage.cs b/Sources/Model/RunePage.cs new file mode 100644 index 0000000..cf56628 --- /dev/null +++ b/Sources/Model/RunePage.cs @@ -0,0 +1,95 @@ +using System; +using System.Collections.ObjectModel; + +namespace Model +{ + public partial class RunePage + { + public string Name + { + get => name; + private init + { + if(string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("a Rune Page must have a name"); + } + name = value; + } + } + private readonly string name = null!; + + public ReadOnlyDictionary Runes { get; private set; } + private Dictionary runes = new Dictionary(); + + public RunePage(string name) + { + Name = name; + Runes = new ReadOnlyDictionary(runes); + } + + public Rune? this[Category category] + { + get + { + if(runes.TryGetValue(category, out Rune? rune)) + { + return rune; + } + return null; + } + set + { + if(value == null) + { + runes.Remove(category); + } + runes[category] = value!; + CheckRunes(category); + } + } + + private void CheckRunes(Category newRuneCategory) + { + switch(newRuneCategory) + { + case Category.Major: + UpdateMajorFamily(Category.Minor1, true); + UpdateMajorFamily(Category.Minor2, true); + UpdateMajorFamily(Category.Minor3, true); + UpdateMajorFamily(Category.OtherMinor1, false); + UpdateMajorFamily(Category.OtherMinor2, false); + break; + case Category.Minor1: + case Category.Minor2: + case Category.Minor3: + UpdateMajorFamily(newRuneCategory, true); + break; + case Category.OtherMinor1: + case Category.OtherMinor2: + UpdateMajorFamily(newRuneCategory, false); + break; + } + } + + private bool? CheckFamilies(Category cat1, Category cat2) + { + runes.TryGetValue(cat1, out Rune? rune1); + runes.TryGetValue(cat2, out Rune? rune2); + if(rune1 == null || rune2 == null) + { + return null; + } + return rune1.Family == rune2.Family; + } + + private void UpdateMajorFamily(Category minor, bool expectedValue) + { + if(CheckFamilies(Category.Major, minor).GetValueOrDefault(expectedValue) == expectedValue) + { + runes.Remove(minor); + } + } + } +} + diff --git a/Sources/Model/Skill.cs b/Sources/Model/Skill.cs new file mode 100644 index 0000000..0679ea6 --- /dev/null +++ b/Sources/Model/Skill.cs @@ -0,0 +1,63 @@ +using System; + +namespace Model +{ + public class Skill : IEquatable + { + public SkillType Type { get; private set; } + + public string Name + { + get => name; + private init + { + if (string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("a Skill needs a name"); + } + name = value; + } + } + private readonly string name = null!; + + public string Description + { + get => description; + set + { + if(string.IsNullOrWhiteSpace(value)) + { + description = ""; + return; + } + description = value; + } + } + private string description = ""; + + public Skill(string name, SkillType type, string description = "") + { + Name = name; + Type = type; + Description = description ?? ""; + } + + public override bool Equals(object? obj) + { + if(ReferenceEquals(obj, null)) return false; + if(ReferenceEquals(obj, this)) return true; + if(GetType() != obj.GetType()) return false; + return Equals(obj as Skill); + } + + public bool Equals(Skill? other) + => Name.Equals(other?.Name) && Type == other.Type; + + public override int GetHashCode() + => Name.GetHashCode() % 281; + + public override string ToString() + => $"{Name} ({Type})"; + } +} + diff --git a/Sources/Model/Skin.cs b/Sources/Model/Skin.cs new file mode 100644 index 0000000..2d8fb78 --- /dev/null +++ b/Sources/Model/Skin.cs @@ -0,0 +1,83 @@ +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Model +{ + public class Skin : IEquatable + { + public string Name + { + get => name; + private init + { + if(string.IsNullOrWhiteSpace(value)) + { + throw new ArgumentException("A skin must have a name"); + } + name = value; + } + } + private readonly string name = null!; + + public string Description + { + get => description; + set + { + if (string.IsNullOrWhiteSpace(value)) + { + description = ""; + return; + } + description = value; + } + } + private string description = ""; + + public string Icon { get; set; } + public LargeImage Image { get; set; } + + public float Price { get; set; } + + public Champion Champion + { + get => champion; + private init + { + if (value == null) + throw new ArgumentNullException("A skill can't have a null champion"); + champion = value; + } + } + private readonly Champion champion = null!; + + public Skin(string name, Champion champion, float price = 0.0f, string icon = "", string image = "", string description = "") + { + Name = name; + Champion = champion; + Champion.AddSkin(this); + Price = price; + Icon = icon; + Image = new LargeImage(image); + Description = description; + } + + public override bool Equals(object? obj) + { + if(ReferenceEquals(obj, null)) return false; + if(ReferenceEquals(obj, this)) return true; + if(GetType() != obj.GetType()) return false; + return Equals(obj as Skin); + } + + public bool Equals(Skin? other) + => Name.Equals(other?.Name); + + public override int GetHashCode() + => Name.GetHashCode() % 997; + + public override string ToString() + => $"{Name}"; + } +} + diff --git a/Sources/Model/enums/ChampionClass.cs b/Sources/Model/enums/ChampionClass.cs new file mode 100644 index 0000000..d169512 --- /dev/null +++ b/Sources/Model/enums/ChampionClass.cs @@ -0,0 +1,15 @@ +using System; +namespace Model +{ + public enum ChampionClass + { + Unknown, + Assassin, + Fighter, + Mage, + Marksman, + Support, + Tank, + } +} + diff --git a/Sources/Model/enums/RuneFamily.cs b/Sources/Model/enums/RuneFamily.cs new file mode 100644 index 0000000..07a232c --- /dev/null +++ b/Sources/Model/enums/RuneFamily.cs @@ -0,0 +1,11 @@ +using System; +namespace Model +{ + public enum RuneFamily + { + Unknown, + Precision, + Domination + } +} + diff --git a/Sources/Model/enums/SkillType.cs b/Sources/Model/enums/SkillType.cs new file mode 100644 index 0000000..d7fc8da --- /dev/null +++ b/Sources/Model/enums/SkillType.cs @@ -0,0 +1,12 @@ +using System; +namespace Model +{ + public enum SkillType + { + Unknown, + Basic, + Passive, + Ultimate + } +} + diff --git a/Sources/Shared/IGenericDataManager.cs b/Sources/Shared/IGenericDataManager.cs new file mode 100644 index 0000000..ed3ac48 --- /dev/null +++ b/Sources/Shared/IGenericDataManager.cs @@ -0,0 +1,11 @@ +namespace Shared; +public interface IGenericDataManager +{ + Task GetNbItems(); + Task> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false); + Task GetNbItemsByName(string substring); + Task> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false); + Task UpdateItem(T oldItem, T newItem); + Task AddItem(T item); + Task DeleteItem(T item); +} diff --git a/Sources/Shared/Shared.csproj b/Sources/Shared/Shared.csproj new file mode 100644 index 0000000..bafd05b --- /dev/null +++ b/Sources/Shared/Shared.csproj @@ -0,0 +1,9 @@ + + + + net6.0 + enable + enable + + + diff --git a/Sources/StubLib/Extensions.cs b/Sources/StubLib/Extensions.cs new file mode 100644 index 0000000..ef2e82b --- /dev/null +++ b/Sources/StubLib/Extensions.cs @@ -0,0 +1,65 @@ +using System; +using Model; + +namespace StubLib +{ + static class Extensions + { + internal static Task> GetItemsWithFilterAndOrdering(this IEnumerable collection, + Func filter, int index, int count, string? orderingPropertyName = null, bool descending = false) + { + IEnumerable temp = collection; + temp = temp.Where(item => filter(item)); + if(orderingPropertyName != null) + { + var prop = typeof(T).GetProperty(orderingPropertyName!); + if (prop != null) + { + temp = descending ? temp.OrderByDescending(item => prop.GetValue(item)) + : temp.OrderBy(item => prop.GetValue(item)); + } + } + return Task.FromResult>(temp.Skip(index*count).Take(count)); + } + + internal static Task GetNbItemsWithFilter(this IEnumerable collection, Func filter) + { + return Task.FromResult(collection.Count(item => filter(item))); + } + + internal static Task AddItem(this IList collection, T? item) + { + if(item == null || collection.Contains(item)) + { + return Task.FromResult(default(T)); + } + collection.Add(item); + return Task.FromResult(item); + } + + internal static Task DeleteItem(this IList collection, T? item) + { + if(item == null) + { + return Task.FromResult(false); + } + bool result = collection.Remove(item!); + return Task.FromResult(result); + } + + internal static Task UpdateItem(this IList collection, T? oldItem, T? newItem) + { + if(oldItem == null || newItem == null) return Task.FromResult(default(T)); + + if(!collection.Contains(oldItem)) + { + return Task.FromResult(default(T)); + } + + collection.Remove(oldItem!); + collection.Add(newItem!); + return Task.FromResult(newItem); + } + } +} + diff --git a/Sources/StubLib/StubData.Champions.cs b/Sources/StubLib/StubData.Champions.cs new file mode 100644 index 0000000..ad19275 --- /dev/null +++ b/Sources/StubLib/StubData.Champions.cs @@ -0,0 +1,100 @@ +using System; +using Model; + +namespace StubLib +{ + public partial class StubData + { + private List champions = new() + { + new Champion("Akali", ChampionClass.Assassin), + new Champion("Aatrox", ChampionClass.Fighter), + new Champion("Ahri", ChampionClass.Mage), + new Champion("Akshan", ChampionClass.Marksman), + new Champion("Bard", ChampionClass.Support), + new Champion("Alistar", ChampionClass.Tank), + }; + + public class ChampionsManager : IChampionsManager + { + private readonly StubData parent; + + public ChampionsManager(StubData parent) + => this.parent = parent; + + public Task AddItem(Champion? item) + => parent.champions.AddItem(item); + + public Task DeleteItem(Champion? item) + => parent.champions.DeleteItem(item); + + public Task GetNbItems() + => Task.FromResult(parent.champions.Count); + + public Task> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.champions.GetItemsWithFilterAndOrdering( + c => true, + index, count, + orderingPropertyName, descending); + + private Func filterByCharacteristic = (champ, charName) => champ.Characteristics.Keys.Any(k => k.Contains(charName, StringComparison.InvariantCultureIgnoreCase)); + + public Task GetNbItemsByCharacteristic(string charName) + => parent.champions.GetNbItemsWithFilter(champ => filterByCharacteristic(champ, charName)); + + public Task> GetItemsByCharacteristic(string charName, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.champions.GetItemsWithFilterAndOrdering( + champ => filterByCharacteristic(champ, charName), + index, count, orderingPropertyName, descending); + + private Func filterByClass = (champ, championClass) => champ.Class == championClass; + + public Task GetNbItemsByClass(ChampionClass championClass) + => parent.champions.GetNbItemsWithFilter( + champ => filterByClass(champ, championClass)); + + public Task> GetItemsByClass(ChampionClass championClass, int index, int count, string? orderingPropertyName, bool descending = false) + => parent.champions.GetItemsWithFilterAndOrdering( + champ => filterByClass(champ, championClass), + index, count, orderingPropertyName, descending); + + private Func filterBySkill = (champ, skill) => skill != null && champ.Skills.Contains(skill!); + + public Task GetNbItemsBySkill(Skill? skill) + => parent.champions.GetNbItemsWithFilter(champ => filterBySkill(champ, skill)); + + public Task> GetItemsBySkill(Skill? skill, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.champions.GetItemsWithFilterAndOrdering(champ => filterBySkill(champ, skill), index, count, orderingPropertyName, descending); + + private static Func filterBySkillSubstring = (champ, skill) => champ.Skills.Any(s => s.Name.Contains(skill, StringComparison.InvariantCultureIgnoreCase)); + + public Task GetNbItemsBySkill(string skillSubstring) + => parent.champions.GetNbItemsWithFilter(champ => filterBySkillSubstring(champ, skillSubstring)); + + public Task> GetItemsBySkill(string skillSubstring, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.champions.GetItemsWithFilterAndOrdering(champ => filterBySkillSubstring(champ, skillSubstring), index, count, orderingPropertyName, descending); + + public Task GetNbItemsByRunePage(RunePage? runePage) + => Task.FromResult(parent.championsAndRunePages.Count(tuple => tuple.Item2.Equals(runePage))); + + public Task> GetItemsByRunePage(RunePage? runePage, int index, int count, string? orderingPropertyName = null, bool descending = false) + => Task.FromResult> + (parent.championsAndRunePages + .Where(tuple => tuple.Item2.Equals(runePage)) + .Select(tuple => tuple.Item1) + .Skip(index*count).Take(count)); + + private Func filterByName = (champ, substring) => champ.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase); + + public Task GetNbItemsByName(string substring) + => parent.champions.GetNbItemsWithFilter(champ => filterByName(champ, substring)); + + public Task> GetItemsByName(string substring, int index, int count, string? orderingPropertyName, bool descending = false) + => parent.champions.GetItemsWithFilterAndOrdering(champ => filterByName(champ, substring), index, count, orderingPropertyName, descending); + + public Task UpdateItem(Champion? oldItem, Champion? newItem) + => parent.champions.UpdateItem(oldItem, newItem); + } + } +} + diff --git a/Sources/StubLib/StubData.RunePages.cs b/Sources/StubLib/StubData.RunePages.cs new file mode 100644 index 0000000..a08a947 --- /dev/null +++ b/Sources/StubLib/StubData.RunePages.cs @@ -0,0 +1,83 @@ +using System; +using Model; + +namespace StubLib +{ + public partial class StubData + { + private readonly List runePages = new(); + + private void InitRunePages() + { + var runePage1 = new RunePage("rune page 1"); + runePage1[RunePage.Category.Major] = runes[0]; + runePage1[RunePage.Category.Minor1] = runes[1]; + runePage1[RunePage.Category.Minor2] = runes[2]; + runePage1[RunePage.Category.Minor3] = runes[3]; + runePage1[RunePage.Category.OtherMinor1] = runes[4]; + runePage1[RunePage.Category.OtherMinor2] = runes[5]; + runePages.Add(runePage1); + } + + public class RunePagesManager : IRunePagesManager + { + private readonly StubData parent; + + public RunePagesManager(StubData parent) + => this.parent = parent; + + private static Func filterByName + = (rp, substring) => rp.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase); + + private static Func filterByRune + = (rp, rune) => rune != null && rp.Runes.Values.Contains(rune!); + + public Task AddItem(RunePage? item) + => parent.runePages.AddItem(item); + + public Task DeleteItem(RunePage? item) + => parent.runePages.DeleteItem(item); + + public Task> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.runePages.GetItemsWithFilterAndOrdering( + rp => true, + index, count, orderingPropertyName, descending); + + public Task> GetItemsByChampion(Champion? champion, int index, int count, string? orderingPropertyName = null, bool descending = false) + => Task.FromResult>( + parent.championsAndRunePages + .Where(tuple => tuple.Item1.Equals(champion)) + .Select(tuple => tuple.Item2) + .Skip(index*count).Take(count)); + + public Task> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.runePages.GetItemsWithFilterAndOrdering( + rp => filterByName(rp, substring), + index, count, orderingPropertyName, descending); + + public Task> GetItemsByRune(Rune? rune, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.runePages.GetItemsWithFilterAndOrdering( + rp => filterByRune(rp, rune), + index, count, orderingPropertyName, descending); + + public Task GetNbItems() + => parent.runePages.GetNbItemsWithFilter( + rp => true); + + public Task GetNbItemsByChampion(Champion? champion) + => Task.FromResult(parent.championsAndRunePages.Count(tuple => tuple.Item1.Equals(champion))); + + public Task GetNbItemsByName(string substring) + => parent.runePages.GetNbItemsWithFilter( + rp => filterByName(rp, substring)); + + public Task GetNbItemsByRune(Rune? rune) + => parent.runePages.GetNbItemsWithFilter( + rp => filterByRune(rp, rune)); + + public Task UpdateItem(RunePage? oldItem, RunePage? newItem) + => parent.runePages.UpdateItem(oldItem, newItem); + } + } +} + diff --git a/Sources/StubLib/StubData.Runes.cs b/Sources/StubLib/StubData.Runes.cs new file mode 100644 index 0000000..f0e8802 --- /dev/null +++ b/Sources/StubLib/StubData.Runes.cs @@ -0,0 +1,69 @@ +using System; +using Model; + +namespace StubLib +{ + public partial class StubData + { + private readonly List runes = new() + { + new Rune("Conqueror", RuneFamily.Precision), + new Rune("Triumph", RuneFamily.Precision), + new Rune("Legend: Alacrity", RuneFamily.Precision), + new Rune("Legend: Tenacity", RuneFamily.Precision), + new Rune("last stand", RuneFamily.Domination), + new Rune("last stand 2", RuneFamily.Domination), + }; + + public class RunesManager : IRunesManager + { + private readonly StubData parent; + + public RunesManager(StubData parent) + => this.parent = parent; + + public Task AddItem(Rune? item) + => parent.runes.AddItem(item); + + public Task DeleteItem(Rune? item) + => parent.runes.DeleteItem(item); + + public Task> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.runes.GetItemsWithFilterAndOrdering( + r => true, + index, count, orderingPropertyName, descending); + + private static Func filterByRuneFamily + = (rune, family) => rune.Family == family; + + private static Func filterByName + = (rune, substring) => rune.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase); + + public Task> GetItemsByFamily(RuneFamily family, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.runes.GetItemsWithFilterAndOrdering( + rune => filterByRuneFamily(rune, family), + index, count, orderingPropertyName, descending); + + public Task> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.runes.GetItemsWithFilterAndOrdering( + rune => filterByName(rune, substring), + index, count, orderingPropertyName, descending); + + public Task GetNbItems() + => parent.runes.GetNbItemsWithFilter( + rune => true); + + public Task GetNbItemsByFamily(RuneFamily family) + => parent.runes.GetNbItemsWithFilter( + rune => filterByRuneFamily(rune, family)); + + public Task GetNbItemsByName(string substring) + => parent.runes.GetNbItemsWithFilter( + rune => filterByName(rune, substring)); + + public Task UpdateItem(Rune? oldItem, Rune? newItem) + => parent.runes.UpdateItem(oldItem, newItem); + } + } +} + diff --git a/Sources/StubLib/StubData.Skins.cs b/Sources/StubLib/StubData.Skins.cs new file mode 100644 index 0000000..ff5fc08 --- /dev/null +++ b/Sources/StubLib/StubData.Skins.cs @@ -0,0 +1,80 @@ +using System; +using Model; + +namespace StubLib +{ + public partial class StubData + { + private readonly List skins = new(); + + private void InitSkins() + { + skins.Add(new Skin("Stinger", champions[0])); + skins.Add(new Skin("Infernal", champions[0])); + skins.Add(new Skin("All-Star", champions[0])); + skins.Add(new Skin("Justicar", champions[1])); + skins.Add(new Skin("Mecha", champions[1])); + skins.Add(new Skin("Sea Hunter", champions[1])); + skins.Add(new Skin("Dynasty", champions[2])); + skins.Add(new Skin("Midnight", champions[2])); + skins.Add(new Skin("Foxfire", champions[2])); + skins.Add(new Skin("Cyber Pop", champions[3])); + skins.Add(new Skin("Crystal Rose", champions[3])); + skins.Add(new Skin("Elderwood", champions[4])); + skins.Add(new Skin("Snow Day", champions[4])); + skins.Add(new Skin("Bard", champions[4])); + skins.Add(new Skin("Black", champions[5])); + skins.Add(new Skin("Golden", champions[5])); + skins.Add(new Skin("Matador", champions[5])); + } + + public class SkinsManager : ISkinsManager + { + private readonly StubData parent; + + public SkinsManager(StubData parent) + => this.parent = parent; + + public Task AddItem(Skin? item) + => parent.skins.AddItem(item); + + public Task DeleteItem(Skin? item) + => parent.skins.DeleteItem(item); + + public Task> GetItems(int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.skins.GetItemsWithFilterAndOrdering( + skin => true, + index, count, orderingPropertyName, descending); + + private static Func filterByChampion = (skin, champion) => champion != null && skin.Champion.Equals(champion!); + + private static Func filterByName = (skin, substring) => skin.Name.Contains(substring, StringComparison.InvariantCultureIgnoreCase); + + public Task> GetItemsByChampion(Champion? champion, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.skins.GetItemsWithFilterAndOrdering( + skin => filterByChampion(skin, champion), + index, count, orderingPropertyName, descending); + + public Task> GetItemsByName(string substring, int index, int count, string? orderingPropertyName = null, bool descending = false) + => parent.skins.GetItemsWithFilterAndOrdering( + skin => filterByName(skin, substring), + index, count, orderingPropertyName, descending); + + public Task GetNbItems() + => parent.skins.GetNbItemsWithFilter( + c => true); + + public Task GetNbItemsByChampion(Champion? champion) + => parent.skins.GetNbItemsWithFilter( + skin => filterByChampion(skin, champion)); + + public Task GetNbItemsByName(string substring) + => parent.skins.GetNbItemsWithFilter( + skin => filterByName(skin, substring)); + + public Task UpdateItem(Skin? oldItem, Skin? newItem) + => parent.skins.UpdateItem(oldItem, newItem); + } + } +} + diff --git a/Sources/StubLib/StubData.cs b/Sources/StubLib/StubData.cs new file mode 100644 index 0000000..8e34486 --- /dev/null +++ b/Sources/StubLib/StubData.cs @@ -0,0 +1,35 @@ +using Model; + +namespace StubLib; +public partial class StubData : IDataManager +{ + public StubData() + { + ChampionsMgr = new ChampionsManager(this); + SkinsMgr = new SkinsManager(this); + RunesMgr = new RunesManager(this); + RunePagesMgr = new RunePagesManager(this); + + InitSkins(); + InitRunePages(); + } + + public IChampionsManager ChampionsMgr { get; } + + public ISkinsManager SkinsMgr { get; } + + public IRunesManager RunesMgr { get; } + + public IRunePagesManager RunePagesMgr { get; } + + private List> championsAndRunePages = new(); + + private void InitChampionsAndRunePages() + { + championsAndRunePages.Add(Tuple.Create(champions[0], runePages[0])); + } + + + +} + diff --git a/Sources/StubLib/StubLib.csproj b/Sources/StubLib/StubLib.csproj new file mode 100644 index 0000000..f05027c --- /dev/null +++ b/Sources/StubLib/StubLib.csproj @@ -0,0 +1,12 @@ + + + + net6.0 + enable + enable + + + + + + diff --git a/Sources/Tests/ConsoleTests/ConsoleTests.csproj b/Sources/Tests/ConsoleTests/ConsoleTests.csproj new file mode 100644 index 0000000..1602b94 --- /dev/null +++ b/Sources/Tests/ConsoleTests/ConsoleTests.csproj @@ -0,0 +1,20 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + + + + + + + diff --git a/Sources/Tests/ConsoleTests/Program.cs b/Sources/Tests/ConsoleTests/Program.cs new file mode 100644 index 0000000..28dc26b --- /dev/null +++ b/Sources/Tests/ConsoleTests/Program.cs @@ -0,0 +1,338 @@ +using System.Collections.Immutable; +using System.Diagnostics; +using Microsoft.Extensions.DependencyInjection; +using Model; +using StubLib; +using static System.Console; + +namespace ConsoleTests +{ + static class Program + { + static IDataManager dataManager = null!; + + static async Task Main(string[] args) + { + try + { + using var servicesProvider = new ServiceCollection() + .AddSingleton() + .BuildServiceProvider(); + + dataManager = servicesProvider.GetRequiredService(); + + await DisplayMainMenu(); + + Console.ReadLine(); + } + catch (Exception ex) + { + Debug.WriteLine(ex, "Stopped program because of exception"); + throw; + } + } + + public static async Task DisplayMainMenu() + { + Dictionary choices = new Dictionary() + { + [1] = "1- Manage Champions", + [2] = "2- Manage Skins", + [3] = "3- Manage Runes", + [4] = "4- Manage Rune Pages", + [99] = "99- Quit" + }; + + while(true) + { + int input = DisplayAMenu(choices); + + switch(input) + { + case 1: + await DisplayChampionsMenu(); + break; + case 2: + break; + case 3: + break; + case 4: + break; + case 99: + WriteLine("Bye bye!"); + return; + default: + break; + } + } + } + + private static int DisplayAMenu(Dictionary choices) + { + int input=-1; + while(true) + { + WriteLine("What is your choice?"); + WriteLine("--------------------"); + foreach(var choice in choices.OrderBy(kvp => kvp.Key).Select(kvp => kvp.Value)) + { + WriteLine(choice); + } + if(!int.TryParse(ReadLine(), out input) || input == -1) + { + WriteLine("I do not understand what your choice is. Please try again."); + continue; + } + break; + } + WriteLine($"You have chosen: {choices[input]}"); + WriteLine(); + return input; + } + + public static async Task DisplayChampionsMenu() + { + Dictionary choices = new Dictionary() + { + [0] = "0- Get number of champions", + [1] = "1- Get champions", + [2] = "2- Find champions by name", + [3] = "3- Find champions by characteristic", + [4] = "4- Find champions by class", + [5] = "5- Find champions by skill", + [6] = "6- Add new champion", + [7] = "7- Delete a champion", + [8] = "8- Update a champion", + }; + + int input = DisplayAMenu(choices); + + switch(input) + { + case 0: + int nb = await dataManager.ChampionsMgr.GetNbItems(); + WriteLine($"There are {nb} champions"); + WriteLine("**********************"); + break; + case 1: + { + int index = ReadAnInt("Please enter the page index"); + int count = ReadAnInt("Please enter the number of elements to display"); + WriteLine($"{count} champions of page {index+1}"); + var champions = await dataManager.ChampionsMgr.GetItems(index, count, nameof(Champion.Name)); + foreach(var champion in champions) + { + WriteLine($"\t{champion}"); + } + WriteLine("**********************"); + } + break; + case 2: + { + string substring = ReadAString("Please enter the substring to look for in the name of a champion"); + int index = ReadAnInt("Please enter the page index"); + int count = ReadAnInt("Please enter the number of elements to display"); + var champions = await dataManager.ChampionsMgr.GetItemsByName(substring, index, count, nameof(Champion.Name)); + foreach(var champion in champions) + { + WriteLine($"\t{champion}"); + } + WriteLine("**********************"); + } + break; + case 3: + { + string substring = ReadAString("Please enter the substring to look for in the characteristics of champions"); + int index = ReadAnInt("Please enter the page index"); + int count = ReadAnInt("Please enter the number of elements to display"); + var champions = await dataManager.ChampionsMgr.GetItemsByCharacteristic(substring, index, count, nameof(Champion.Name)); + foreach(var champion in champions) + { + WriteLine($"\t{champion}"); + } + WriteLine("**********************"); + } + break; + case 4: + { + ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); + int index = ReadAnInt("Please enter the page index"); + int count = ReadAnInt("Please enter the number of elements to display"); + var champions = await dataManager.ChampionsMgr.GetItemsByClass(championClass, index, count, nameof(Champion.Name)); + foreach(var champion in champions) + { + WriteLine($"\t{champion}"); + } + WriteLine("**********************"); + } + break; + case 5: + { + string substring = ReadAString("Please enter the substring to look for in the skills of champions"); + int index = ReadAnInt("Please enter the page index"); + int count = ReadAnInt("Please enter the number of elements to display"); + var champions = await dataManager.ChampionsMgr.GetItemsBySkill(substring, index, count, nameof(Champion.Name)); + foreach(var champion in champions) + { + WriteLine($"\t{champion}"); + } + WriteLine("**********************"); + } + break; + case 6: + { + WriteLine("You are going to create a new champion."); + string name = ReadAString("Please enter the champion name:"); + ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); + string bio = ReadAString("Please enter the champion bio:"); + Champion champion = new Champion(name, championClass, bio: bio); + DisplayCreationChampionMenu(champion); + _ = await dataManager.ChampionsMgr.AddItem(champion); + } + break; + case 7: + { + WriteLine("You are going to delete a champion."); + string name = ReadAString("Please enter the champion name:"); + var somechampions = await dataManager.ChampionsMgr.GetItemsByName(name, 0, 10, nameof(Champion.Name)); + var someChampionNames = somechampions.Select(c => c!.Name); + var someChampionNamesAsOneString = someChampionNames.Aggregate("", (name, chaine) => $"{chaine} {name}"); + string champName = ReadAStringAmongPossibleValues($"Who do you want to delete among these champions? (type \"Cancel\" to ... cancel) {someChampionNamesAsOneString}", + someChampionNames.ToArray()); + if(champName != "Cancel") + { + await dataManager.ChampionsMgr.DeleteItem(somechampions.Single(c => c!.Name == champName)); + } + } + break; + case 8: + { + WriteLine("You are going to update a champion."); + string name = ReadAString("Please enter the champion name:"); + var somechampions = await dataManager.ChampionsMgr.GetItemsByName(name, 0, 10, nameof(Champion.Name)); + var someChampionNames = somechampions.Select(c => c!.Name); + var someChampionNamesAsOneString = someChampionNames.Aggregate("", (name, chaine) => $"{chaine} {name}"); + string champName = ReadAStringAmongPossibleValues($"Who do you want to update among these champions? (type \"Cancel\" to ... cancel) {someChampionNamesAsOneString}", + someChampionNames.ToArray()); + if(champName == "Cancel") break; + ChampionClass championClass = ReadAnEnum($"Please enter the champion class (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); + string bio = ReadAString("Please enter the champion bio:"); + Champion champion = new Champion(champName, championClass, bio: bio); + DisplayCreationChampionMenu(champion); + await dataManager.ChampionsMgr.UpdateItem(somechampions.Single(c => c!.Name == champName), champion); + } + break; + default: + break; + } + + } + + public static void DisplayCreationChampionMenu(Champion champion) + { + Dictionary choices = new Dictionary() + { + [1] = "1- Add a skill", + [2] = "2- Add a skin", + [3] = "3- Add a characteristic", + [99] = "99- Finish" + }; + + while(true) + { + int input = DisplayAMenu(choices); + + switch(input) + { + case 1: + string skillName = ReadAString("Please enter the skill name:"); + SkillType skillType = ReadAnEnum($"Please enter the skill type (possible values are: {Enum.GetNames().Aggregate("", (name, chaine) => $"{chaine} {name}")}):"); + string skillDescription = ReadAString("Please enter the skill description:"); + Skill skill = new Skill(skillName, skillType, skillDescription); + champion.AddSkill(skill); + break; + case 2: + string skinName = ReadAString("Please enter the skin name:"); + string skinDescription = ReadAString("Please enter the skin description:"); + float skinPrice = ReadAFloat("Please enter the price of this skin:"); + Skin skin = new Skin(skinName, champion, skinPrice, description: skinDescription); + break; + case 3: + string characteristic = ReadAString("Please enter the characteristic:"); + int value = ReadAnInt("Please enter the value associated to this characteristic:"); + champion.AddCharacteristics(Tuple.Create(characteristic, value)); + break; + case 99: + return; + default: + break; + } + } + } + + private static int ReadAnInt(string message) + { + while(true) + { + WriteLine(message); + if(!int.TryParse(ReadLine(), out int result)) + { + continue; + } + return result; + } + } + + private static float ReadAFloat(string message) + { + while(true) + { + WriteLine(message); + if(!float.TryParse(ReadLine(), out float result)) + { + continue; + } + return result; + } + } + + private static string ReadAString(string message) + { + while(true) + { + WriteLine(message); + string? line = ReadLine(); + if(line == null) + { + continue; + } + return line!; + } + } + + private static TEnum ReadAnEnum(string message) where TEnum :struct + { + while(true) + { + WriteLine(message); + if(!Enum.TryParse(ReadLine(), out TEnum result)) + { + continue; + } + return result; + } + } + + private static string ReadAStringAmongPossibleValues(string message, params string[] possibleValues) + { + while(true) + { + WriteLine(message); + string? result = ReadLine(); + if(result == null) continue; + if(result != "Cancel" && !possibleValues.Contains(result!)) continue; + return result!; + } + } + } +} \ No newline at end of file