/// <summary>
        /// Stuurt een lijst van SmsDTO's naar de caller
        /// </summary>
        /// <param name="includeCreated">worden de smsDTO's met status Created mee gestuurd</param>
        public void RequestSmsList(bool includeCreated)
        {
            List <SmsDTO> smsDTOList;

            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                if (!includeCreated)
                {
                    smsDTOList = db.Sms
                                 .Where(x => x.Status.Name != "Created")
                                 .OrderBy(x => x.TimeStamp)
                                 .AsEnumerable()
                                 .Select(x => new SmsDTO(x))
                                 .ToList();
                }
                else
                {
                    smsDTOList = db.Sms
                                 .OrderBy(x => x.TimeStamp)
                                 .AsEnumerable()
                                 .Select(x => new SmsDTO(x))
                                 .ToList();
                }
            }

            Clients.Caller.getSmsList(smsDTOList);
        }
        /// <summary>
        /// Algemene methode om clients te verwittigen van wijzigingen
        /// Zorgt voor volledige herinladen lijst
        /// Voor gebruik door server
        /// </summary>
        /// <param name="hubContext"></param>
        /// <param name="smsDTO"></param>
        /// <param name="operation"></param>
        public static async Task NotifyChange(IHubContext hubContext, SmsDTO smsDTO, string operation)
        {
            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                await UpdateLog(db, smsDTO, operation);
            }

            switch (operation)
            {
            case "POST":
                hubContext.Clients.All.notifyCreateSms(smsDTO);
                break;

            case "PUT":
                hubContext.Clients.All.notifyEditSms(smsDTO);
                break;

            case "DELETE":
                hubContext.Clients.All.notifyDeleteSms(smsDTO);
                break;

            default:
                hubContext.Clients.All.notifyChangeToSmsList();
                break;
            }

            hubContext.Clients.All.notifyChangeToCharts();
        }
        /// <summary>
        /// Importeert een lijst van Sms'en in tblImportSms
        /// </summary>
        /// <param name="smsDTOList">de te importeren sms</param>
        public async Task RequestCreateSmsBulk(List <SmsDTO> smsDTOList)
        {
            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                IEnumerable <ImportSms> smsImportList = smsDTOList.Select(x => new ImportSms()
                {
                    ContactNumber    = x.ContactNumber,
                    Message          = x.Message,
                    ContactFirstName = x.ContactFirstName,
                    ContactLastName  = x.ContactLastName
                })
                                                        .AsEnumerable();

                db.ImportSms.AddRange(smsImportList);

                try
                {
                    await db.SaveChangesAsync();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }
        }
        /// <summary>
        /// Past een Sms aan in de database.
        /// </summary>
        /// <param name="smsDTO">de aan te passen sms</param>
        public async Task RequestEditSms(SmsDTO smsDTO)
        {
            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                Sms sms = Sms.FindSmsById(smsDTO.Id, db);
                Debug.Print(sms.Status.Name);
                sms.CopyFromSmsDTO(smsDTO, db);
                Debug.Print(sms.Status.Name);

                //db.Set<Sms>().Attach(sms);
                //db.Entry(sms).State = EntityState.Modified;

                try
                {
                    await db.SaveChangesAsync();

                    await UpdateLog(db, smsDTO, "PUT");

                    NotifyEditSms(smsDTO, Clients);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }
        }
 /// <summary>
 /// Geeft aan telefoon de opdracht om een sms te zenden, ongeacht de huidige status
 /// </summary>
 /// <param name="smsID">Id van de sms</param>
 public async Task SendSelectedSms(Guid smsId)
 {
     using (ISendSMSHostContext db = new SendSMSHostContext())
     {
         var smsDTO = new SmsDTO(await db.Sms.SingleOrDefaultAsync(x => x.Id == smsId));
         Clients.Others.sendSelectedSms(smsDTO);
     }
 }
        /// <summary>
        /// Brengt andere clients op de hoogte dat een bepaalde SmsDTO aangepast is
        /// Zorgt voor volledige herinladen lijst
        /// </summary>
        /// <param name="smsDTOWithClient">de aangepaste SmsDTO met bewerkingsgegevens</param>
        public async void NotifyChange(SmsDTO smsDTO, string operation)
        {
            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                Clients.Others.notifyChangeToSmsList();
                Clients.All.notifyChangeToCharts();

                await UpdateLog(db, smsDTO, operation);
            }
        }
        /// <summary>
        /// Stuurt een HourChartData naar de caller
        /// </summary>
        public void RequestHourChart(bool includeDeleted = false)
        {
            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                IChartDataFactory chartDataFactory = new HourChartDataFactory();
                ChartData         chartdata        = chartDataFactory?.CreateChartData(db, includeDeleted);

                Clients.Caller.notifyChangeHourChart(chartdata);
            }
        }
        public async void Enqueue(int batchsize)
        {
            // nieuwe dbContext opvragen
            using (SendSMSHostContext db = new SendSMSHostContext())
            {
                // De lijst van sms'en met de status Queued of Pending aanvullen tot batchSize
                // aanpassen in database + clients verwittigen

                Status statusQueued  = db.Status.FirstOrDefault(x => x.Name == "Queued");
                Status statusPending = db.Status.FirstOrDefault(x => x.Name == "Pending");
                Status statusCreated = db.Status.FirstOrDefault(x => x.Name == "Created");

                int queuedCount  = statusQueued.Sms.Count();
                int createdCount = statusCreated.Sms.Count();

                int amountToChange = 0;
                if (batchsize > queuedCount)
                {
                    amountToChange = Math.Min(batchsize - queuedCount, createdCount);
                }

                if (amountToChange > 0)
                {
                    Debug.WriteLine($"[{DateTime.Now}] Enqueuing {amountToChange} sms");


                    var changeToQueuedSmsList = statusCreated.Sms
                                                .OrderBy(z => z.TimeStamp)
                                                .Take(amountToChange)
                                                .ToList();

                    var queuedSmsDTOList = new List <SmsDTO>();
                    foreach (var sms in changeToQueuedSmsList)
                    {
                        sms.Status    = statusQueued;
                        sms.TimeStamp = DateTime.Now;
                        SmsDTO smsDTO = new SmsDTO(sms);

                        try
                        {
                            await db.SaveChangesAsync();

                            await ServerSentEventsHub.NotifyChange(_signalRContext,
                                                                   smsDTO : smsDTO,
                                                                   operation : "PUT");
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine(ex.Message);
                        }
                    }
                }
            }
        }
        /// <summary>
        /// Stuurt een lijst van StatusDTO's naar de caller
        /// </summary>
        public void RequestStatusList()
        {
            List <StatusDTO> statusDTOList;

            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                statusDTOList = db.Status
                                .AsEnumerable()
                                .Select(x => new StatusDTO(x))
                                .ToList();
            }

            Clients.Caller.getStatusList(statusDTOList);
        }
        /// <summary>
        /// Stuurt een lijst van StatusDTO's naar de caller
        /// </summary>
        public void RequestContactList()
        {
            List <ContactDTO> contactDTOList;

            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                contactDTOList = db.Contacts
                                 .Where(x => !x.IsAnonymous)
                                 .AsEnumerable()
                                 .Select(x => new ContactDTO(x))
                                 .ToList();
            }

            Clients.Caller.getContactList(contactDTOList);
        }
        /// <summary>
        /// Maakt de tabel Logs leeg
        /// </summary>
        public async Task RequestClearLogs()
        {
            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                db.Log.RemoveRange(db.Log);

                try
                {
                    await db.SaveChangesAsync();

                    Clients.All.notifyChangeToCharts();
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }
        }
        /// <summary>
        /// Verwijdert een Sms in de database.
        /// </summary>
        /// <param name="smsDTO">de te verwijderen sms</param>
        public async Task RequestDeleteSms(SmsDTO smsDTO)
        {
            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                Sms sms = await db.Sms.FindAsync(Guid.Parse(smsDTO.Id));

                if (sms != null)
                {
                    db.Sms.Remove(sms);
                    try
                    {
                        await db.SaveChangesAsync();

                        await UpdateLog(db, smsDTO, "DELETE");

                        NotifyDeleteSms(smsDTO, Clients);
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex.Message);
                    }
                }
            }
        }
        /// <summary>
        /// Maakt een Sms in de database.
        /// </summary>
        /// <param name="smsDTO">de te maken sms</param>
        public async Task RequestCreateSms(SmsDTO smsDTO)
        {
            using (ISendSMSHostContext db = new SendSMSHostContext())
            {
                // Indien ContactId == null dan opzoeken of nieuw contact voorzien
                if (String.IsNullOrWhiteSpace(smsDTO.ContactId))
                {
                    // kijken of nummer al in gebruik is
                    Contact contact = db.Contacts
                                      .SingleOrDefault(x => x.Number == smsDTO.ContactNumber);
                    if (contact == null) // nieuw contact maken
                    {
                        contact = new Contact
                        {
                            Id          = Guid.NewGuid(),
                            FirstName   = "",
                            LastName    = "",
                            Number      = smsDTO.ContactNumber,
                            IsAnonymous = true
                        };

                        db.Contacts.Add(contact);
                        try
                        {
                            await db.SaveChangesAsync();
                        }
                        catch (Exception ex)
                        {
                            Debug.WriteLine(ex.Message);
                            throw;
                        }
                    }
                    smsDTO.ContactId        = contact.Id.ToString();
                    smsDTO.ContactFirstName = contact.FirstName;
                    smsDTO.ContactLastName  = contact.LastName;
                }

                Status statusCreated = db.Status.FirstOrDefault(x => x.Name == "Created");

                Sms sms = new Sms(smsDTO, db)
                {
                    TimeStamp = DateTime.Now,
                    Status    = statusCreated
                };
                db.Sms.Add(sms);

                try
                {
                    await db.SaveChangesAsync();

                    smsDTO = new SmsDTO(await db.Sms
                                        .SingleOrDefaultAsync(x => x.Id == sms.Id));


                    await UpdateLog(db, smsDTO, "POST");

                    NotifyCreateSms(smsDTO, Clients);
                }
                catch (Exception ex)
                {
                    Debug.WriteLine(ex.Message);
                }
            }
        }
        public async void Import()
        {
            // nieuwe context opvragen
            using (var db = new SendSMSHostContext())
            {
                int importSmsCount = db.ImportSms.Count();
                if (importSmsCount > 0)
                {
                    Debug.WriteLine($"[{DateTime.Now}] Importing {importSmsCount} sms");

                    Status statusCreated = db.Status.FirstOrDefault(x => x.Name == "Created");

                    // eerst alle bestaande contacten zonder contactgegevens updaten waar mogelijk
                    var contactUpdateList = db.ImportSms
                                            .Where(x => !((x.ContactFirstName == null || x.ContactFirstName == "") &&
                                                          (x.ContactLastName == null || x.ContactLastName == "")))
                                            .Select(x => new
                    {
                        Number    = x.ContactNumber,
                        FirstName = x.ContactFirstName ?? String.Empty,
                        LastName  = x.ContactLastName ?? String.Empty
                    })
                                            .GroupBy(k => k.Number)
                                            .Select(g => g.FirstOrDefault())
                                            .Where(x => db.Contacts
                                                   .Where(y => y.IsAnonymous)
                                                   .Select(z => z.Number)
                                                   .Contains(x.Number))
                                            .AsEnumerable()
                                            .Select(x => new Contact
                    {
                        Id          = Guid.NewGuid(),
                        Number      = x.Number,
                        IsAnonymous = false,
                        FirstName   = x.FirstName,
                        LastName    = x.LastName
                    });

                    foreach (var contact in contactUpdateList)
                    {
                        var oldContact = db.Contacts.Single(x => x.Number == contact.Number);
                        oldContact.FirstName   = contact.FirstName;
                        oldContact.LastName    = contact.LastName;
                        oldContact.IsAnonymous = contact.IsAnonymous;
                    }

                    int contactUpdateListCount = contactUpdateList.Count();

                    // dan alle nieuwe contacten met contactgegevens aanmaken
                    var contactNameList = db.ImportSms
                                          .Where(x => !((x.ContactFirstName == null || x.ContactFirstName == "") &&
                                                        (x.ContactLastName == null || x.ContactLastName == "")))
                                          .Select(x => new
                    {
                        Number    = x.ContactNumber,
                        FirstName = x.ContactFirstName ?? String.Empty,
                        LastName  = x.ContactLastName ?? String.Empty
                    })
                                          .GroupBy(k => k.Number)
                                          .Select(g => g.FirstOrDefault())
                                          .Where(x => !db.Contacts
                                                 .Select(y => y.Number)
                                                 .Contains(x.Number))
                                          .AsEnumerable()
                                          .Select(x => new Contact
                    {
                        Id          = Guid.NewGuid(),
                        Number      = x.Number,
                        IsAnonymous = false,
                        FirstName   = x.FirstName,
                        LastName    = x.LastName
                    });

                    db.Contacts.AddRange(contactNameList);
                    int contactNameListCount = contactNameList.Count();

                    // dan alle nieuwe contacten zonder contactgegevens aanmaken
                    var contactNumberList = db.ImportSms
                                            .Where(x => (x.ContactFirstName == null || x.ContactFirstName == "") &&
                                                   (x.ContactLastName == null || x.ContactLastName == ""))
                                            .Select(x => x.ContactNumber)
                                            .Distinct()
                                            .Where(x => !db.Contacts
                                                   .Select(y => y.Number)
                                                   .Contains(x))
                                            .AsEnumerable()
                                            .Select(x => new Contact
                    {
                        Id          = Guid.NewGuid(),
                        Number      = x,
                        IsAnonymous = true
                    });

                    db.Contacts.AddRange(contactNumberList);
                    int contactNumberListCount = contactNumberList.Count();

                    try
                    {
                        await db.SaveChangesAsync();

                        Debug.WriteLine($"[{DateTime.Now}] Updated {contactUpdateListCount} anonymous contacts");
                        Debug.WriteLine($"[{DateTime.Now}] Created {contactNameListCount} new contacts");
                        Debug.WriteLine($"[{DateTime.Now}] Created {contactNumberListCount} new anonymous contacts");
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex.Message);
                        throw ex;
                    }

                    // dan alle sms'en aanmaken
                    var smsToImport = db.ImportSms
                                      .AsEnumerable()
                                      .Select(x => new Sms
                    {
                        Id      = Guid.NewGuid(),
                        Contact = db.Contacts
                                  .SingleOrDefault(y => y.Number == x.ContactNumber),
                        Message   = x.Message,
                        Status    = statusCreated,
                        TimeStamp = DateTime.Now,
                    })
                                      .ToList();

                    db.Sms.AddRange(smsToImport);

                    // dan ImportSms leeg maken
                    db.ImportSms.RemoveRange(db.ImportSms);

                    try
                    {
                        await db.SaveChangesAsync();

                        Debug.WriteLine($"[{DateTime.Now}] Created {importSmsCount} new sms");

                        foreach (Sms s in smsToImport)
                        {
                            SmsDTO smsDTO = new SmsDTO(s);
                            await ServerSentEventsHub.NotifyChange(_signalRContext,
                                                                   smsDTO : smsDTO,
                                                                   operation : "POST");
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine(ex.Message);
                        throw ex;
                    }
                }
            }
        }