/// <summary>
        /// Optimizes a list of files using a given optimizer
        /// </summary>
        /// <param name="optimizer">The optimizer to use.</param>
        /// <param name="file">The file to optimze.</param>
        /// <param name="outputPath">The path of the optimized file.</param>
        /// <param name="configFiles">The config files to compare against.</param>
        public OptimizedFile Optimize(string optimizer, IFile file, FilePath outputPath, IList <OptimizedFile> configFiles)
        {
            //Check Config
            OptimizedFile config = configFiles.FirstOrDefault(o => o.Path.FullPath.ToLower() == file.Path.FullPath.ToLower());

            OptimizedFile optimizedFile = null;
            string        hash          = this.GetHash(file);

            try
            {
                if ((config == null) || config.RequiresOptimization(hash) || config.DifferentService(optimizer))
                {
                    //Optimize
                    ImageOptimizerResult result = _OptimizerFactory.Optimize(optimizer, file.Path, outputPath);

                    if ((result != null) && !result.HasError)
                    {
                        //Optimzed
                        optimizedFile = new OptimizedFile(file.Path, result.Service, result.ModifiedDate, hash, result.SizeBefore, result.SizeAfter);

                        _ImagesOptimized.Add(file.Path.FullPath);
                        _SizeBefore += result.SizeBefore;
                        _SizeAfter  += result.SizeAfter;

                        _Log.Information("Optimized:  " + file.Path + " - Saved: " + result.SavedSize + " bytes (" + result.SavedPercent.ToString("N0") + "%)");
                    }
                    else if ((result.ErrorMessage == "Unsupported File") ||
                             (result.ErrorMessage == "Invalid FileSize") ||
                             (result.ErrorMessage == "Matching FileSize"))
                    {
                        //Skipped
                        _ImagesSkipped++;

                        _SizeBefore += file.Length;
                        _SizeAfter  += file.Length;

                        // Copy to destination
                        if (file.Path.FullPath.ToLower() != outputPath.FullPath.ToLower())
                        {
                            IDirectory parent = _FileSystem.GetDirectory(outputPath.GetDirectory());

                            if (!parent.Exists)
                            {
                                parent.Create();
                            }

                            file.Copy(outputPath, true);
                        }

                        _Log.Information("Skipped:  " + file.Path + " - " + result.ErrorMessage);
                    }
                    else
                    {
                        //Errored
                        _ImagesErrored++;

                        _Log.Information("Errored:  " + file.Path + " - " + result.ErrorMessage);
                    }
                }
                else
                {
                    //Skipped
                    _ImagesSkipped++;

                    if (config != null)
                    {
                        optimizedFile = new OptimizedFile(file.Path, config.Service, config.OptimizedDate, hash, config.SizeBefore, config.SizeAfter);

                        _SizeBefore += config.SizeBefore;
                        _SizeAfter  += config.SizeAfter;
                    }
                    else
                    {
                        _SizeBefore += file.Length;
                        _SizeAfter  += file.Length;
                    }

                    _Log.Information("Skipped:  " + file.Path + " - Saved: " + config.SavedSize + " bytes (" + config.SavedPercent.ToString("N0") + "%)");
                }
            }
            catch (Exception ex)
            {
                //Errored
                _ImagesErrored++;

                _Log.Information("Errored:  " + file.Path + " - " + ex.Message);
            }

            this.OnProgress(optimizedFile);

            return(optimizedFile);
        }