private void SendEmail(UploadSet uploadSet, string subject, string body)
        {
            using (var httpClient = new HttpClient())
            {
                httpClient.Timeout = TimeSpan.FromMinutes(uploadSet.Setup.UploadTimeoutMinutes);
                //httpClient.BaseAddress = new Uri(uploadSet.Setup.EmailEndpoint);

                using (var content = new MultipartFormDataContent())
                {
                    Console.WriteLine("Deactivating images");

                    Dictionary <string, string> svcMetadata = new Dictionary <string, string>();

                    svcMetadata.Clear();

                    var jsonData = "{" + String.Join(",", svcMetadata.Values) + "}";

                    content.Add(new StringContent("update"), "action");
                    content.Add(new StringContent(CurrentEnvironment.UserName), "transactionUser");
                    content.Add(new StringContent(jsonData), "images");

                    Logger.LogCustomEvent("sendEmail", svcMetadata);

                    /*
                     * //var response = httpClient.PostAsync(uploadSet.Setup.EmailEndPoint, content).Result;
                     *
                     * if (response.StatusCode != HttpStatusCode.OK)
                     * {
                     *      var reason = response.ReasonPhrase;
                     *      //response.Content.ReadAsStringAsync().Result;
                     * }
                     */
                }
            }
        }
        private void MoveFiles(UploadSet uploadSet, string[] loadFiles, bool isError = false)
        {
            string eventName = isError ? "ArchivingInvalidItemImages" : "ArchivingImages";


            //move files
            if (!String.IsNullOrEmpty(uploadSet.Setup.ArchiveFolder))
            {
                Dictionary <string, string> filesToMove = new Dictionary <string, string>();

                foreach (var item in loadFiles)
                {
                    if (isError)
                    {
                        filesToMove.Add(item, item.ToErrorFolder(uploadSet.Setup.ArchiveFolder));
                    }
                    else
                    {
                        filesToMove.Add(item, item.ToDestinationFolder(uploadSet.Setup.ArchiveFolder));
                    }
                }

                Logger.LogCustomEvent(eventName, uploadSet.ToProperties());

                MoveFiles(uploadSet.Setup.UserName, uploadSet.Setup.Password, uploadSet.Setup.FilePath, filesToMove);
            }
        }
        public void Execute(IJobExecutionContext context)
        {
            try
            {
                List <UploadSet> fileSetsToUpload = new List <UploadSet>();

                List <UploadInfo> filesToUpload = new List <UploadInfo>();

                Logger.LogCustomEvent("UploadFile.Execute");

                #region Find the files to upload

                //Implement Idempotency - this job can be run multiple times without any negative effect.
                //Files already uploaded will not be uploaded again even if the job executes multiple times.

                foreach (SetupConfigurationElement item in JobConfiguration.Setups)
                {
                    filesToUpload.Clear();

                    #region Find files for each setup

                    //get all files from the folder

                    AddSeqToFiles(item.UserName, item.Password, item.FilePath, "*." + item.FileExtension);

                    var filesInDirToUpload = GetFiles(item.ImagesPerCall).ToList();

                    //at this point we will either have new images or previously sucessfully uploaded images
                    //we need to include all the new ones and any previously successfully uploaded image that has been retaken
                    foreach (var file in filesInDirToUpload)
                    {
                        #region Add Files to Upload

                        FileInfo fi = new FileInfo(file);

                        UploadInfo ui = new UploadInfo
                        {
                            Id                  = Guid.NewGuid().ToString(),
                            FileName            = file,
                            UploadEndPoint      = item.UploadEndpoint,
                            Status              = UploadSet.UploadStatus.NotStarted,
                            SourceMachineName   = item.MachineName,
                            FileCreationTimeUtc = fi.LastWriteTimeUtc,
                            //mailEndPoint = item.UploadEndpoint,
                        };

                        filesToUpload.Add(ui);

                        #endregion
                    }

                    #endregion

                    //make sure the files to upload belong to a full set
                    var loadSets = filesToUpload.GroupBy(f => f.FileName.ToLoadNumber());

                    foreach (var set in loadSets)
                    {
                        fileSetsToUpload.Add(new UploadSet {
                            LoadNumber = set.Key, Setup = item, UploadInfoList = set.ToList(), IsRetake = set.First().IsRetake
                        });
                    }
                }

                #endregion


                //exclude any files that are currently being uploaded
                foreach (var file in UploadFileQueue.Instance.InProgress)
                {
                    var existing = fileSetsToUpload.Where(f => f.LoadNumber.Equals(file, StringComparison.CurrentCultureIgnoreCase)).FirstOrDefault();

                    if (existing != null)
                    {
                        fileSetsToUpload.Remove(existing);
                    }
                }

                //queue the file uploads
                foreach (var item in fileSetsToUpload)
                {
                    Logger.LogCustomEvent("QueueUpload", item.ToProperties());

                    UploadFileQueue.Instance.Queue.Enqueue(item);
                }

                if (fileSetsToUpload.Count == 0)
                {
                    Logger.LogCustomEvent("NoFilesToUpload");

                    Console.WriteLine("No file sets to upload: " + context.JobDetail.Description);
                }

                do
                {
                    List <UploadSet> filesToProcess = new List <UploadSet>();

                    for (int i = 0; i < JobConfiguration.NumberOfThreads; i++)
                    {
                        UploadSet res = null;

                        if (UploadFileQueue.Instance.Queue.TryDequeue(out res))
                        {
                            filesToProcess.Add(res);
                        }
                    }

                    if (filesToProcess.Count() == 0)
                    {
                        break;
                    }

                    List <Task> tasks = new List <Task>();
                    try
                    {
                        foreach (var file in filesToProcess)
                        {
                            tasks.Add(Task.Factory.StartNew(() =>
                            {
                                if (loadNumber.Length > 0 && loadFiles.Count() == 0)
                                {
                                    Console.WriteLine("No complete file for the load: " + loadNumber + " " + context.JobDetail.Description);
                                    Console.WriteLine("move all photos for load " + loadNumber + " to error folder");
                                    Logger.LogException(new Exception("No complete file for the load: " + loadNumber + " " + context.JobDetail.Description));
                                    MoveFiles(file, loadFiles, true);
                                }
                                else if (loadNumber.Length > 0 && loadFiles.Count() > 0 && loadFiles.Count() != totalPhotos)
                                {
                                    Console.WriteLine("file number on complete file is different from the total number of photos for load: " + loadNumber + " " + context.JobDetail.Description);
                                    Console.WriteLine("move all photos for load " + loadNumber + " to error folder");
                                    Logger.LogException(new Exception("file number on complete file is different from the total number of photos for load: " + loadNumber + " " + context.JobDetail.Description));
                                    MoveFiles(file, loadFiles, true);
                                    MoveFiles(file, completeFile, true);
                                }
                                else
                                {
                                    UploadFileToServer(file);
                                }
                            }));
                        }
                        Task.WaitAll(tasks.ToArray());
                    }
                    catch (AggregateException excep)
                    {
                        foreach (var task in tasks)
                        {
                            if (task.Exception != null)
                            {
                                UploadInfo chunk = task.AsyncState as UploadInfo;
                                chunk.Status = UploadSet.UploadStatus.Failed;
                            }
                        }
                    }
                }while (true);
            }
            catch (Exception ex)
            {
                //log exception
                Logger.LogException(ex);
            }
        }
        private void UploadFileToServer(UploadSet uploadSet)
        {
            var    startTimeUtc             = DateTime.UtcNow;
            string deActivationErrorMessage = String.Empty;

            try
            {
                UploadFileQueue.Instance.InProgress.Add(uploadSet.LoadNumber);

                Logger.LogCustomEvent("ProcessUpload", uploadSet.ToProperties());

                Console.WriteLine("Uploading load: " + uploadSet.LoadNumber + " " + DateTime.Now.ToString());

                //get the item id
                string itemId = GetItemId(uploadSet.LoadNumber);                 //TODO In case of PAS web service connectivity issue, don't move the files

                #region validate

                if (String.IsNullOrEmpty(JobConfiguration.UploadSource))
                {
                    throw new Exception("UploadSource not found in Configuration for load: " + uploadSet.LoadNumber);
                }

                if (uploadSet.Setup == null)
                {
                    throw new Exception("Configuration setup not found for load: " + uploadSet.LoadNumber);
                }

                if (String.IsNullOrEmpty(uploadSet.Setup.UploadEndpoint))
                {
                    throw new Exception("UploadEndpoint not found in Configuration setup for load: " + uploadSet.LoadNumber);
                }

                if (String.IsNullOrEmpty(uploadSet.Setup.SubscriptionId))
                {
                    throw new Exception("SubscriptionId not found in Configuration setup for load: " + uploadSet.LoadNumber);
                }

                if (!FilesExist(uploadSet.Setup.UserName, uploadSet.Setup.Password, uploadSet.Setup.FilePath, uploadSet.UploadInfoList.Select(u => u.FileName).ToList()))
                {
                    throw new NonLogException("Files not found.");
                }

                if (String.IsNullOrEmpty(itemId))
                {
                    List <string> filesToMove = new List <string>();

                    //take off _processed and add complete file
                    for (int i = 0; i < loadFiles.Count(); i++)
                    {
                        if (loadFiles[i].Contains("_processed"))
                        {
                            filesToMove.Add(loadFiles[i].Substring(0, loadFiles[i].IndexOf("_proessed")) + ".jpg");
                        }
                        else
                        {
                            filesToMove.Add(loadFiles[i]);
                        }
                    }

                    //add ready file
                    filesToMove.Add(completeFile[0]);

                    //Move to error folder
                    MoveFiles(uploadSet, filesToMove.ToArray(), true);


                    throw new NonLogException("Item Id not found for load: " + uploadSet.LoadNumber);
                }

                #endregion

                //Do the actual upload
                using (var httpClient = new HttpClient())
                {
                    httpClient.Timeout     = TimeSpan.FromMinutes(uploadSet.Setup.UploadTimeoutMinutes);
                    httpClient.BaseAddress = new Uri(uploadSet.Setup.UploadEndpoint);

                    httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", uploadSet.Setup.SubscriptionId);
                    httpClient.DefaultRequestHeaders.Add("Cache-Control", "no-cache");

                    List <string> itemPictureIds = new List <string>();

                    #region Get item pictures id for retakes

                    Logger.LogCustomEvent("GetItemId", uploadSet.ToProperties());

                    Console.WriteLine("Calling Get endpoint:" + uploadSet.Setup.GetEndpoint + " at:" + DateTime.Now.ToString());

                    var response = httpClient.GetAsync(uploadSet.Setup.GetEndpoint + "?item_id=" + itemId + "&stage=sale&active=true").Result;

                    if (response.StatusCode != HttpStatusCode.OK)
                    {
                        var reason       = response.ReasonPhrase;
                        var errorMessage = response.Content.ReadAsStringAsync().Result;

                        string error = "Upload for load: " + uploadSet.LoadNumber + " failed to get Item Picture Ids for retaken images. Reason: " + reason + ". Error: " + errorMessage +
                                       " endpoint=" + uploadSet.Setup.GetEndpoint + " item_id=" + itemId;
                        string subject = "Photo Booth Loading Error - Severity 2 - failed retrieve current photos";
                        //SendEmail(uploadSet, subject, error);

                        throw new Exception(error);
                    }

                    #endregion

                    Dictionary <string, string> svcMetadata = new Dictionary <string, string>();
                    Boolean saleStageOnly = true;

                    #region Deactivate images

                    if (processedPhotos == 0)
                    {
                        using (var content = new MultipartFormDataContent())
                        {
                            Console.WriteLine("Deactivating images");

                            svcMetadata.Clear();

                            var jsonResponse = response.Content.ReadAsStringAsync().Result;
                            //get item picture ids from Json
                            dynamic itemInfo = JObject.Parse(@jsonResponse);

                            foreach (var pair in itemInfo)
                            {
                                var metadata      = pair.First.metadata.ToString();
                                var metadata_json = JObject.Parse(metadata);
                                var stages        = metadata_json.stage;
                                var imageType     = metadata_json.image_type;

                                string stagesString = stages.ToString();
                                string newStages    = "";


                                if (stagesString.Contains("["))
                                {
                                    if (imageType == "photo")
                                    {
                                        saleStageOnly = false;
                                        newStages     = stagesString.Replace("sale", "");
                                    }
                                }
                                else
                                {
                                    saleStageOnly = true;
                                }


                                if (saleStageOnly)
                                {
                                    string id = pair.Path;
                                    svcMetadata.Add(id, @"""" + id + @""":{""metadata"":{""active"":""false""}}");
                                }
                                else
                                {
                                    string id = pair.Path;
                                    svcMetadata.Add(id, @"""" + id + @""":{""metadata"":{""stage"":" + newStages + "}}");
                                }
                            }

                            if (svcMetadata.Count() > 0)
                            {
                                var jsonData = "{" + String.Join(",", svcMetadata.Values) + "}";

                                content.Add(new StringContent("update"), "action");
                                content.Add(new StringContent(CurrentEnvironment.UserName), "transactionUser");
                                content.Add(new StringContent(jsonData), "images");

                                Logger.LogCustomEvent("DeactiviatingImages", uploadSet.ToProperties());
                                Console.WriteLine("Calling deactive upload endpoint:" + uploadSet.Setup.UploadEndpoint + " at:" + DateTime.Now.ToString());

                                response = httpClient.PostAsync(uploadSet.Setup.UploadEndpoint, content).Result;

                                if (response.StatusCode != HttpStatusCode.OK)
                                {
                                    var reason = response.ReasonPhrase;
                                    deActivationErrorMessage = response.Content.ReadAsStringAsync().Result;
                                }
                            }
                        }
                    }


                    #endregion

                    using (var content = new MultipartFormDataContent())
                    {
                        #region Generate metadata

                        string uploadFileName = String.Empty;

                        svcMetadata.Clear();

                        foreach (var item in uploadSet.UploadInfoList)
                        {
                            int sequenceNumber;
                            fileOrder.TryGetValue(item.FileName.ToLower(), out sequenceNumber);

                            var imgData = new
                            {
                                item_id         = itemId,
                                sequence_number = sequenceNumber.ToString(),
                                visibility      = "public",
                                sync            = "true",
                                usage           = GetUsage(item.FileName)
                            };

                            var s  = new JsonSerializer();
                            var sb = new StringBuilder();

                            using (var w = new StringWriter(sb))
                            {
                                s.Serialize(w, imgData);
                            }

                            uploadFileName = item.FileName.ToFileName().Replace(uploadSet.LoadNumber, itemId);

                            svcMetadata.Add(uploadFileName, @"""" + uploadFileName + @""":" + sb.ToString());
                        }

                        #endregion

                        string imagesJson = @"{" + String.Join(",", svcMetadata.Values) + "}";

                        content.Add(new StringContent(JobConfiguration.UploadSource), "source");

                        content.Add(new StringContent("upload"), "action");
                        content.Add(new StringContent(CurrentEnvironment.UserName), "transactionUser");
                        content.Add(new StringContent(imagesJson), "images");

                        Console.WriteLine(imagesJson);

                        //get image data
                        var imageData = GetImageData(uploadSet.Setup.UserName, uploadSet.Setup.Password, uploadSet.Setup.FilePath, uploadSet.UploadInfoList);

                        //mime type should be the same for all image
                        string mimeType = MimeMapping.GetMimeMapping(uploadSet.UploadInfoList.First().FileName);

                        for (int i = 0; i < imageData.Count; i++)
                        {
                            uploadFileName = uploadSet.UploadInfoList[i].FileName;
                            uploadFileName = uploadFileName.ToFileName().Replace(uploadSet.LoadNumber, itemId);

                            var streamContent = new ByteArrayContent(imageData[i]);

                            streamContent.Headers.Add("Content-Type", mimeType);

                            streamContent.Headers.Add("Content-Disposition", "form-data; name=\"file\"; filename=\"" + uploadFileName + "\"");

                            content.Add(streamContent, "files", uploadFileName);
                        }

                        Logger.LogCustomEvent("PostingImages", uploadSet.ToProperties());
                        Console.WriteLine("Calling upload endpoint:" + uploadSet.Setup.UploadEndpoint + " at:" + DateTime.Now.ToString());

                        response = httpClient.PostAsync(uploadSet.Setup.UploadEndpoint, content).Result;


                        if (response.StatusCode != HttpStatusCode.OK)
                        {
                            var reason       = response.ReasonPhrase;
                            var errorMessage = response.Content.ReadAsStringAsync().Result;

                            string error = "Upload for load: " + uploadSet.LoadNumber + " failed. Reason: " + reason + ". Error: " + errorMessage +
                                           " endpoint=" + uploadSet.Setup.UploadEndpoint;
                            string subject = "Photo Booth Loading Error - Severity 2 - failed upload photos";
                            //SendEmail(uploadSet, subject, error);

                            throw new Exception(error);
                        }
                    }
                }


                //Update status once uploaded
                foreach (var item in uploadSet.UploadInfoList)
                {
                    item.Status = UploadSet.UploadStatus.Successful;
                }

                Console.WriteLine("Upload " + uploadSet.LoadNumber + " successful " + DateTime.Now.ToString());

                //add processed into file names.
                if (processedPhotos + uploadSet.UploadInfoList.Count < totalPhotos)
                {
                    for (int i = 0; i < processFiles.Count(); i++)
                    {
                        System.IO.File.Move(processFiles[i],
                                            processFiles[i].Substring(0, processFiles[i].IndexOf(".jpg")) + "_processed" + ".jpg");
                    }
                }
                else
                {
                    //rename photo files -taking off processed flag.
                    for (int i = 0; i < processedFiles.Count(); i++)
                    {
                        System.IO.File.Move(processedFiles[i],
                                            processedFiles[i].Substring(0, processedFiles[i].IndexOf("_processed")) + ".jpg");
                    }

                    var filesToMove = new List <string>();

                    //take off _processed and add complete file
                    for (int i = 0; i < loadFiles.Count(); i++)
                    {
                        if (loadFiles[i].Contains("_processed"))
                        {
                            filesToMove.Add(loadFiles[i].Substring(0, loadFiles[i].IndexOf("_processed")) + ".jpg");
                        }
                        else
                        {
                            filesToMove.Add(loadFiles[i]);
                        }
                    }

                    //add ready file
                    filesToMove.Add(filesToMove[0].Substring(0, filesToMove[0].IndexOf(loadNumber)) + loadNumber + "_complete_" + totalPhotos + ".ready");

                    MoveFiles(uploadSet, filesToMove.ToArray());
                }
            }
            catch (Exception ex)
            {
                foreach (var item in uploadSet.UploadInfoList)
                {
                    //don't mark upload status failed if there is an error in archiving the files
                    if (ex.GetType() != typeof(FileArchiveException))
                    {
                        item.Status = UploadSet.UploadStatus.Failed;
                    }

                    item.ErrorMessage = ex.ToString();

                    //log exception
                    Logger.LogException(ex, item.ToProperties());
                }

                if (ex.GetType() == typeof(NonLogException))
                {
                    uploadSet.CanTrack = false;
                }

                Console.WriteLine("Upload " + uploadSet.LoadNumber + " exception: " + ex.ToString());
            }
            finally
            {
                //Log if required
                if (uploadSet.CanTrack)
                {
                    //log progress
                    foreach (var item in uploadSet.UploadInfoList)
                    {
                        Logger.LogEvent(item.ToProperties());
                    }

                    //log dectivation
                    if (!String.IsNullOrEmpty(deActivationErrorMessage))
                    {
                        string subject = "Photo Booth Loading Error - Severity 2 - failed deactive photos";
                        string error   = deActivationErrorMessage + " endpoint:" + uploadSet.Setup.UploadEndpoint + " content:" + uploadSet.ToProperties();
                        //SendEmail(uploadSet, subject, deActivationErrorMessage + );
                        Logger.LogException(new Exception(deActivationErrorMessage));
                    }
                }

                //remove from progress
                string l = uploadSet.LoadNumber;
                UploadFileQueue.Instance.InProgress.TryTake(out l);
            }
        }