using Models; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; namespace LocalEndpoint.Data { /// /// Database implementation with catastrophic performances. /// This database implementation persists data in xml and will save all the data in their files on each mutable requests. /// internal class CatastrophicPerformancesDatabase : Database { private static readonly DataContractSerializer RECIPES_SERIALIZER = new DataContractSerializer(typeof(Dictionary)); private static readonly DataContractSerializer USERS_SERIALIZER = new DataContractSerializer(typeof(Dictionary)); private static readonly DataContractSerializer ACCOUNTS_SERIALIZER = new DataContractSerializer(typeof(Dictionary)); private static readonly string RECIPES_FILENAME = "recipes_data.xml"; private static readonly string USERS_FILENAME = "users_data.xml"; private static readonly string ACCOUNTS_FILENAME = "accounts_data.xml"; private readonly Dictionary recipesData; private readonly Dictionary usersData; private readonly Dictionary accountsData; private readonly string dbPath; public CatastrophicPerformancesDatabase(string folderPath) { dbPath = folderPath; if (!Directory.Exists(folderPath)) Directory.CreateDirectory(folderPath); usersData = Load(USERS_FILENAME, USERS_SERIALIZER); recipesData = Load(RECIPES_FILENAME, RECIPES_SERIALIZER); accountsData = Load(ACCOUNTS_FILENAME, ACCOUNTS_SERIALIZER); } public bool IsEmpty() { return recipesData.Count == 0 && usersData.Count == 0 && accountsData.Count == 0; } public Account? GetAccount(string email, string passwordHash) { if (!accountsData.TryGetValue(email, out AccountData? data)) return null; if (data.PasswordHash != passwordHash) return null; return new Account(usersData[data.UserId].User, data.Email); } public void InsertAccount(Account account, string passwordHash) { accountsData[account.Email] = new AccountData(account.User.Id, account.Email, passwordHash); Save(ACCOUNTS_FILENAME, ACCOUNTS_SERIALIZER, accountsData); } public Recipe GetRecipe(Guid id) { return ConvertRecipeDataToRecipe(recipesData[id]); } public RecipeRate GetRecipeRate(Guid user, Guid recipe) { return usersData[user].Rates[recipe]; } public void InsertInUserList(Guid userId, Guid recipeId, uint persAmount) { usersData[userId].RecipesList[recipeId] = persAmount; Save(USERS_FILENAME, USERS_SERIALIZER, usersData); } public void RemoveFromUserList(Guid userId, Guid recipeId) { usersData[userId].RecipesList.Remove(recipeId); Save(USERS_FILENAME, USERS_SERIALIZER, usersData); } public void InsertRecipe(Recipe recipe) { recipesData[recipe.Info.Id] = new RecipeData(recipe.Info, recipe.Owner.Id, recipe.Ingredients, recipe.Steps); Save(RECIPES_FILENAME, RECIPES_SERIALIZER, recipesData); } public void InsertUser(User user) { usersData[user.Id] = new UserData(user, new Dictionary(), new Dictionary()); Save(USERS_FILENAME, USERS_SERIALIZER, usersData); } public void InsertRate(Guid userId, Guid recipeId, RecipeRate rate) { usersData[userId].Rates[recipeId] = rate; Save(USERS_FILENAME, USERS_SERIALIZER, usersData); } public void RemoveRecipe(Guid id) { recipesData.Remove(id); Save(RECIPES_FILENAME, RECIPES_SERIALIZER, recipesData); } public ImmutableList ListAllRecipes() { return recipesData.Values.ToImmutableList().ConvertAll(ConvertRecipeDataToRecipe); } public ImmutableDictionary ListRatesOf(Guid user) { return usersData[user].Rates.ToImmutableDictionary(); } public ImmutableDictionary GetRecipeListOf(Guid user) { return usersData[user].RecipesList.ToImmutableDictionary(); } private Recipe ConvertRecipeDataToRecipe(RecipeData rd) { var owner = usersData[rd.OwnerID].User; return new Recipe(rd.Info, owner, rd.Ingredients, rd.Steps); } private Dictionary Load(string fileName, DataContractSerializer deserializer) { var file = dbPath + "/" + fileName; var fileInfo = new FileInfo(file); if (!fileInfo.Exists) fileInfo.Create(); if (fileInfo.Length == 0) return new Dictionary(); //file is empty thus there is nothing to deserialize Console.WriteLine(File.ReadAllText(file)); using (var stream = File.OpenRead(file)) return deserializer.ReadObject(stream) as Dictionary ?? throw new Exception("object read from " + file + " is not a dictionnary"); } private void Save(string fileName, DataContractSerializer serializer, Dictionary dict) { using (var stream = File.OpenWrite(dbPath + "/" + fileName)) { serializer.WriteObject(stream, dict); stream.Flush(); } } } }