public static async Task Run([TimerTrigger("0 */5 * * * *")] TimerInfo myTimer,
                                     [CosmosDB(
                                          databaseName: "FreeCosmosDB",
                                          collectionName: "TrackMe",
                                          ConnectionStringSetting = "CosmosDBForFree"
                                          )]
                                     IAsyncCollector <KMLInfo> asyncCollectorKMLInfo,
                                     [CosmosDB(
                                          databaseName: "FreeCosmosDB",
                                          collectionName: "TrackMe",
                                          ConnectionStringSetting = "CosmosDBForFree"
                                          )] DocumentClient documentClient,
                                     [CosmosDB(
                                          databaseName: "FreeCosmosDB",
                                          collectionName: "TrackMe",
                                          ConnectionStringSetting = "CosmosDBForFree",
                                          SqlQuery = "SELECT * FROM c WHERE c.groupid = 'user'"
                                          )] IEnumerable <InReachUser> inReachUsers,
                                     ExecutionContext context)
        {
            var config = new ConfigurationBuilder()
                         .SetBasePath(context.FunctionAppDirectory)
                         .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                         .AddEnvironmentVariables()
                         .Build();
            var SendEmailFunctionKey             = config["SendEmailInReachFunctionKey"];
            var SendEmailFunctionUrl             = config["SendEmailFunctionUrl"];
            var WebSiteUrl                       = config["WebSiteUrl"];
            var TodayTrackId                     = config["TodayTrackId"];
            var StorageContainerConnectionString = config["StorageContainerConnectionString"];

            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageContainerConnectionString);
            CloudBlobClient     blobClient     = storageAccount.CreateCloudBlobClient();

            Uri           collectionUri = UriFactory.CreateDocumentCollectionUri("FreeCosmosDB", "TrackMe");
            List <Emails> emails        = new List <Emails>();

            DateTime dateTimeUTC = DateTime.UtcNow.ToUniversalTime();

            //getting active tracks from CosmosDB
            var query = new SqlQuerySpec("SELECT * FROM c WHERE (c.d1 < @dateTimeUTC and c.d2 > @dateTimeUTC) or c.id = @TodayTrack",
                                         new SqlParameterCollection(new SqlParameter[] { new SqlParameter {
                                                                                             Name = "@dateTimeUTC", Value = dateTimeUTC
                                                                                         }, new SqlParameter {
                                                                                             Name = "@TodayTrack", Value = TodayTrackId
                                                                                         } }));
            IEnumerable <KMLInfo> kMLInfos = documentClient.CreateDocumentQuery <KMLInfo>(collectionUri, query, new FeedOptions {
                EnableCrossPartitionQuery = true
            }).AsEnumerable();

            //remove all duplicates by LastPointTimestamp and groupid field to make only one query to Garmin per user (if user has multiple Live tracks in same time)
            IEnumerable <KMLInfo> kMLInfoDeDups = kMLInfos.GroupBy(x => new { x.LastPointTimestamp, x.groupid }).Select(x => x.First()).ToList();

            //getting feed from garmin, one feed for each user if LastpointTimestamp is same
            foreach (var kMLInfoDeDup in kMLInfoDeDups)
            {
                DateTime lastd1 = DateTime.SpecifyKind(DateTime.Parse(kMLInfoDeDup.d1, CultureInfo.InvariantCulture), DateTimeKind.Utc);
                DateTime today  = DateTime.UtcNow.ToUniversalTime().AddDays(-1);

                //set d1 to LastPointTimestamp + 1 second (if LastTimestamp exist) to download the feed from that point forward from Garmin
                if (!string.IsNullOrEmpty(kMLInfoDeDup.LastPointTimestamp))
                {
                    kMLInfoDeDup.d1 = DateTime.Parse(kMLInfoDeDup.LastPointTimestamp, CultureInfo.InvariantCulture).AddSeconds(1).ToString("yyyy-MM-ddTHH:mm:ssZ");
                }
                else
                {
                    kMLInfoDeDup.d1 = DateTime.Parse(kMLInfoDeDup.d1, CultureInfo.InvariantCulture).ToString("yyyy-MM-ddTHH:mm:ssZ");
                }

                //resetting Today's track, once at night according to user Timezone
                if (lastd1 < today && kMLInfoDeDup.id == TodayTrackId)
                {
                    var dated1     = DateTime.UtcNow.ToUniversalTime().AddHours(kMLInfoDeDup.UserTimezone).ToString("yyyy-MM-dd");
                    var dateTimed1 = DateTime.Parse(dated1).AddHours(-kMLInfoDeDup.UserTimezone).ToString("yyyy-MM-ddTHH:mm:ssZ");
                    var dateTimed2 = DateTime.Parse(dated1).AddDays(1).AddHours(-kMLInfoDeDup.UserTimezone).ToString("yyyy-MM-ddTHH:mm:ssZ");
                    kMLInfoDeDup.d1 = dateTimed1;
                    kMLInfoDeDup.d2 = dateTimed2;
                    kMLInfoDeDup.LastPointTimestamp = "";
                    kMLInfoDeDup.LastLatitude       = 0;
                    kMLInfoDeDup.LastLongitude      = 0;
                    kMLInfoDeDup.LastTotalDistance  = 0;
                    kMLInfoDeDup.LastTotalTime      = "";
                    kMLInfoDeDup.TrackStartTime     = "";
                    await asyncCollectorKMLInfo.AddAsync(kMLInfoDeDup);

                    //delete Today's blobs
                    foreach (var blob in helperKMLParse.Blobs)
                    {
                        var blobName = $"{kMLInfoDeDup.groupid}/{kMLInfoDeDup.id}/{blob.BlobName}.kml";
                        await helperKMLParse.RemoveBlobAsync(blobName, blobClient);
                    }
                }
                //getting always only last point from garmin (except if new day with active tracking has started)
                HelperGetKMLFromGarmin GetKMLFromGarmin = new HelperGetKMLFromGarmin();
                var kmlFeedresult = await GetKMLFromGarmin.GetKMLAsync(kMLInfoDeDup);

                kMLInfoDeDup.LastPoint = kmlFeedresult;
            }

            foreach (var kMLInfo in kMLInfos)
            {
                var kmlFeedresult = kMLInfoDeDups.First(x => x.groupid == kMLInfo.groupid).LastPoint;
                //if there are new points, then load whole track from database and add the point
                if (helperKMLParse.IsThereNewPoints(kmlFeedresult, kMLInfo))
                {
                    var user = new InReachUser();
                    foreach (var usr in inReachUsers)
                    {
                        if (usr.userWebId == kMLInfo.groupid)
                        {
                            user = usr;
                            break;
                        }
                    }
                    //open KML feeds from Blobstorage
                    var blobs = helperKMLParse.Blobs;
                    foreach (var blob in blobs)
                    {
                        var blobName = $"{kMLInfo.groupid}/{kMLInfo.id}/{blob.BlobName}.kml";
                        blob.BlobValue = await helperKMLParse.GetFromBlobAsync(blobName, blobClient);
                    }

                    //process the full track
                    helperKMLParse.ParseKMLFile(kmlFeedresult, kMLInfo, blobs, emails, user, WebSiteUrl);

                    kMLInfo.d1 = DateTime.Parse(kMLInfo.d1, CultureInfo.InvariantCulture).ToString("yyyy-MM-ddTHH:mm:ssZ");
                    kMLInfo.d2 = DateTime.Parse(kMLInfo.d2, CultureInfo.InvariantCulture).ToString("yyyy-MM-ddTHH:mm:ssZ");

                    await asyncCollectorKMLInfo.AddAsync(kMLInfo);

                    //save blobs
                    foreach (var blob in blobs)
                    {
                        var blobName = $"{kMLInfo.groupid}/{kMLInfo.id}/{blob.BlobName}.kml";
                        await helperKMLParse.AddToBlobAsync(blobName, blob.BlobValue, blobClient);
                    }
                }
            }

            //remove all duplicates by DateTime field and sending the list to SendEmailFunction
            if (emails.Any())
            {
                List <Emails> emailList            = emails.GroupBy(x => x.DateTime).Select(x => x.First()).ToList();
                HttpClient    client               = new HttpClient();
                Uri           SendEmailFunctionUri = new Uri($"{SendEmailFunctionUrl}?code={SendEmailFunctionKey}");
                var           returnMessage        = await client.PostAsJsonAsync(SendEmailFunctionUri, emailList);
            }
        }
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req,
            [CosmosDB(
                 databaseName: "FreeCosmosDB",
                 collectionName: "TrackMe",
                 ConnectionStringSetting = "CosmosDBForFree"
                 )]
            IAsyncCollector <KMLInfo> asyncCollectorKMLInfo,
            [CosmosDB(
                 databaseName: "FreeCosmosDB",
                 collectionName: "TrackMe",
                 ConnectionStringSetting = "CosmosDBForFree",
                 SqlQuery = "SELECT * FROM c WHERE c.groupid = 'user'"
                 )] IEnumerable <InReachUser> inReachUsers,
            ExecutionContext context
            )
        {
            var config = new ConfigurationBuilder()
                         .SetBasePath(context.FunctionAppDirectory)
                         .AddJsonFile("local.settings.json", optional: true, reloadOnChange: true)
                         .AddEnvironmentVariables()
                         .Build();
            var StorageContainerConnectionString = config["StorageContainerConnectionString"];

            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(StorageContainerConnectionString);
            CloudBlobClient     blobClient     = storageAccount.CreateCloudBlobClient();

            ClaimsPrincipal Identities      = req.HttpContext.User;
            var             checkUser       = new HelperCheckUser();
            var             LoggedInUser    = checkUser.LoggedInUser(inReachUsers, Identities);
            var             IsAuthenticated = false;

            if (LoggedInUser.status == Status.ExistingUser)
            {
                IsAuthenticated = true;
            }

            if (IsAuthenticated)
            {
                string requestBody = await new StreamReader(req.Body).ReadToEndAsync();
                if (requestBody != "")
                {
                    var kMLFull = JsonConvert.DeserializeObject <KMLFull>(requestBody);

                    KMLInfo kMLInfo = new KMLInfo()
                    {
                        id                 = kMLFull.id,
                        Title              = kMLFull.Title,
                        d1                 = kMLFull.d1,
                        d2                 = kMLFull.d2,
                        InReachWebAddress  = kMLFull.InReachWebAddress,
                        InReachWebPassword = kMLFull.InReachWebPassword,
                        UserTimezone       = kMLFull.UserTimezone
                    };
                    var blobs = helperKMLParse.Blobs;
                    blobs.ForEach(x => x.BlobValue = "");
                    blobs.First(x => x.BlobName == "plannedtrack").BlobValue = kMLFull.PlannedTrack;

                    //1. replace ö->o ä->a etc
                    //2. first: UrlEncode is removing all weird charactes and spaces
                    //3. second: HttpUtility.UrlEncode is removing some not named weird characters, just in case
                    //4. third: UrlEncode again is removing possible %-marks
                    //setting id field only on initial track creation
                    if (string.IsNullOrEmpty(kMLInfo.id))
                    {
                        string id = RemoveDiacritics(kMLInfo.Title);
                        id         = UrlEncode(HttpUtility.UrlEncode(UrlEncode(id)));
                        kMLInfo.id = id;
                    }
                    kMLInfo.LastPointTimestamp = "";
                    kMLInfo.groupid            = LoggedInUser.userWebId;
                    kMLInfo.IsLongTrack        = false;

                    var      dateD1   = DateTime.Parse(kMLInfo.d1).AddHours(-kMLInfo.UserTimezone);
                    var      dateD2   = DateTime.Parse(kMLInfo.d2).AddDays(1).AddHours(-kMLInfo.UserTimezone);
                    TimeSpan timeSpan = dateD2 - dateD1;
                    //this setting affects of parsing all points (slow) or not. Depending on the duration of the track
                    if (timeSpan.TotalDays > 2)
                    {
                        kMLInfo.IsLongTrack = true;
                    }

                    kMLInfo.d1 = dateD1.ToString("yyyy-MM-ddTHH:mm:ssZ");
                    kMLInfo.d2 = dateD2.ToString("yyyy-MM-ddTHH:mm:ssZ");

                    HelperGetKMLFromGarmin helperGetKMLFromGarmin = new HelperGetKMLFromGarmin();
                    //get feed grom garmin
                    var kmlFeedresult = await helperGetKMLFromGarmin.GetKMLAsync(kMLInfo);

                    //parse and transform the feed and save to database
                    helperKMLParse.ParseKMLFile(kmlFeedresult, kMLInfo, blobs, new List <Emails>(), LoggedInUser);
                    await asyncCollectorKMLInfo.AddAsync(kMLInfo);

                    //save blobs
                    foreach (var blob in blobs)
                    {
                        var blobName = $"{kMLInfo.groupid}/{kMLInfo.id}/{blob.BlobName}.kml";
                        await helperKMLParse.AddToBlobAsync(blobName, blob.BlobValue, blobClient);
                    }
                }
            }
            return(new OkObjectResult(IsAuthenticated));
        }
        static async Task ManageTodayTrack(InReachUser LoggedInUser, IAsyncCollector <KMLInfo> addDocuments, DocumentClient client, Uri collectionUri, string TodayTrackId, string SendEmailFunctionUrl, string SendEmailFunctionKey, string WebSiteUrl, CloudBlobClient blobClient)
        {
            var dated1     = DateTime.UtcNow.ToUniversalTime().ToString("yyyy-MM-dd");
            var dated2     = DateTime.UtcNow.ToUniversalTime().ToString("yyyy-MM-dd");
            var dateTimed1 = DateTime.Parse(dated1).AddHours(-LoggedInUser.UserTimezone).ToString("yyyy-MM-ddTHH:mm:ssZ");
            var dateTimed2 = DateTime.Parse(dated2).AddDays(1).AddHours(-LoggedInUser.UserTimezone).ToString("yyyy-MM-ddTHH:mm:ssZ");

            KMLInfo kMLInfo = new KMLInfo()
            {
                id                 = TodayTrackId,
                Title              = "Today's Live Track",
                d1                 = dateTimed1,
                d2                 = dateTimed2,
                groupid            = LoggedInUser.userWebId,
                InReachWebAddress  = LoggedInUser.InReachWebAddress,
                InReachWebPassword = LoggedInUser.InReachWebPassword,
                UserTimezone       = LoggedInUser.UserTimezone,
                IsLongTrack        = false
            };

            //create Today's track
            if (LoggedInUser.Active)
            {
                HelperGetKMLFromGarmin helperGetKMLFromGarmin = new HelperGetKMLFromGarmin();

                var emails = new List <Emails>();
                //get feed grom garmin
                var kmlFeedresult = await helperGetKMLFromGarmin.GetKMLAsync(kMLInfo);

                var blobs = helperKMLParse.Blobs;
                //parse and transform the feed and save to database
                helperKMLParse.ParseKMLFile(kmlFeedresult, kMLInfo, blobs, emails, LoggedInUser, WebSiteUrl);
                await addDocuments.AddAsync(kMLInfo);

                //save blobs
                foreach (var blob in blobs)
                {
                    var blobName = $"{kMLInfo.groupid}/{kMLInfo.id}/{blob.BlobName}.kml";
                    await helperKMLParse.AddToBlobAsync(blobName, blob.BlobValue, blobClient);
                }

                //sending out emails
                if (emails.Any())
                {
                    HttpClient httpClient           = new HttpClient();
                    Uri        SendEmailFunctionUri = new Uri($"{SendEmailFunctionUrl}?code={SendEmailFunctionKey}");
                    var        returnMessage        = await httpClient.PostAsJsonAsync(SendEmailFunctionUri, emails);
                }
            }
            //delete Today's track
            if (!LoggedInUser.Active)
            {
                //select and delete document
                var queryOne = new SqlQuerySpec("SELECT c._self, c.groupid, c.id FROM c WHERE c.id = @id",
                                                new SqlParameterCollection(new SqlParameter[] { new SqlParameter {
                                                                                                    Name = "@id", Value = kMLInfo.id
                                                                                                } }));
                KMLInfo kML = client.CreateDocumentQuery(collectionUri, queryOne, new FeedOptions {
                    PartitionKey = new PartitionKey(kMLInfo.groupid)
                }).AsEnumerable().FirstOrDefault();
                if (!(kML is null))
                {
                    //delete metadata
                    await client.DeleteDocumentAsync(kML._self, new RequestOptions { PartitionKey = new PartitionKey(kML.groupid) });

                    //delete blobs
                    foreach (var blob in helperKMLParse.Blobs)
                    {
                        var blobName = $"{kML.groupid}/{kML.id}/{blob.BlobName}.kml";
                        await helperKMLParse.RemoveBlobAsync(blobName, blobClient);
                    }
                }
            }
        }