/// <summary>
        /// Save the uploaded photo as an image on the user folder
        /// with a greater size than usual but limited,
        /// for later use on cropping and resizing tasks.
        /// </summary>
        /// <param name="photo"></param>
        /// <param name="virtualPath"></param>
        public static void SaveEditableProfilePicture(int userID, Stream photo, float angle)
        {
            // Check folder or create
            string virtualPath = LcUrl.RenderAppPath + GetUserPhotoFolder(userID);
            var    folder      = System.Web.HttpContext.Current.Server.MapPath(virtualPath);

            if (!Directory.Exists(folder))
            {
                Directory.CreateDirectory(folder);
            }

            // Use file as image
            using (var srcImg = System.Drawing.Image.FromStream(photo))
            {
                // Resize to maximum allowed size (but not upscale) to allow user cropping later
                var auxImg = LcImaging.Rotate(srcImg, angle, DefaultBackground);
                var img    = LcImaging.Resize(auxImg, profilePictureFixedSizeWidth * profilePictureOriginalScale, profilePictureFixedSizeHeight * profilePictureOriginalScale, profilePictureSizeMode, LcImaging.AnchorPosition.Center, DefaultBackground);
                auxImg.Dispose();

                // Save:
                img.Save(folder + avatarName + ".jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
                img.Dispose();
            }

            photo.Dispose();
        }
        /// <summary>
        /// Resize, rotate, incoming image, disposing it and saving transformations to disk
        /// </summary>
        /// <param name="image"></param>
        /// <param name="pathFileName"></param>
        /// <param name="angle"></param>
        private static void PrepareAndSaveEditablePhoto(Image image, string pathFileName, float angle)
        {
            Image img = null;

            try
            {
                // Resize to maximum allowed size (but not upscale) to allow user cropping later
                var auxImg = LcImaging.Rotate(image, angle, DefaultBackground);
                var w      = FixedSizeWidth * WorkPhotoOriginalScale;
                var h      = FixedSizeHeight * WorkPhotoOriginalScale;
                if (auxImg.Width < auxImg.Height)
                {
                    // It's portrait, keep original in portrait to prevent the problem of photo getting smaller
                    // with each rotation; further processing creating optimized photos will make it landscape.
                    var aux = w;
                    w = h;
                    h = aux;
                }
                img = LcImaging.Resize(auxImg, w, h, LcImaging.SizeMode.Contain, LcImaging.AnchorPosition.Center, DefaultBackground);
                auxImg.Dispose();

                // Free source, no needed any more and if comes from file, saving will crash
                image.Dispose();

                // Save:
                img.Save(pathFileName, System.Drawing.Imaging.ImageFormat.Jpeg);
            }
            finally
            {
                if (img != null)
                {
                    img.Dispose();
                }
            }
        }
        /// <summary>
        /// Create from the 'editable image' processed versions: cropped with the given values and
        /// optimized sizes.
        /// </summary>
        /// <param name="virtualPath"></param>
        private static string ProcessWorkPhoto(string path, string fileName, int x, int y, int width, int height)
        {
            var regularFileName = "";

            // fileName could be given by a previous save including suffixes,
            // we need it without suffixes in order to work properly:
            fileName = System.IO.Path.GetFileNameWithoutExtension(LcUtils.GetNameWithoutSuffix(fileName));

            // Remove previous cropped/sized/adapted photos (except editable one), all start with fileName plus dash
            // File.Delete doesn't allow wildcards, find and delete each one
            foreach (var f in Directory.GetFiles(path, fileName + "-*", SearchOption.TopDirectoryOnly))
            {
                File.Delete(f);
            }

            Image cropImg = null;

            // Create optimized files
            using (var img = System.Drawing.Image.FromFile(path + fileName + ".jpg")) {
                // Crop, if any size
                // On cropping:
                // User used the reduced image (fixed size) to choose what to crop,
                // while we will crop the 'original', x larger image, so multiply every unit
                cropImg = width > 0 ? LcImaging.Crop(img, x * WorkPhotoOriginalScale, y * WorkPhotoOriginalScale, width * WorkPhotoOriginalScale, height * WorkPhotoOriginalScale) : img;

                // Size prefix
                var sizeName = "-" + FixedSizeWidth.ToString() + "x" + FixedSizeHeight.ToString();

                // Save image with regular size

                using (var modImg = LcImaging.Resize(cropImg, FixedSizeWidth, FixedSizeHeight, LcImaging.SizeMode.Contain, LcImaging.AnchorPosition.Center, DefaultBackground))
                {
                    regularFileName = fileName + sizeName + ".jpg";
                    modImg.Save(path + regularFileName, System.Drawing.Imaging.ImageFormat.Jpeg);
                }

                // Same as previous but for hi-res 2x devices: (real pixel sizes is double but preserve the original size name to recognize it better adding the @2x suffix)
                using (var modImg = LcImaging.Resize(cropImg, FixedSizeWidth * 2, FixedSizeHeight * 2, LcImaging.SizeMode.Contain, LcImaging.AnchorPosition.Center, DefaultBackground)) {
                    modImg.Save(path + fileName + sizeName + "@2x.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
                }

                // NOTE Creation of images with more sizes (for small user widgets on reviews/bookings/etc) or filters go here
            }

            // if there was a crop:
            if (width > 0)
            {
                // TODO To Review when we enable cropping at UI again, because this gets more complicated with rotation and keeping original orientation
                // at the 'editable' photo; maybe the cropping must happen when saving the editable version, and not here at each optimized image
                //// Replace original image with the cropped version, for future 'crop again' tasks
                //using (var replacedEditableImg = LcImaging.Resize(cropImg, FixedSizeWidth * 4, FixedSizeHeight * 4, LcImaging.SizeMode.Contain, LcImaging.AnchorPosition.Center, DefaultBackground))
                //{
                //    replacedEditableImg.Save(path + fileName + ".jpg");
                //}
            }
            cropImg.Dispose();

            return(regularFileName);
        }
        /// <summary>
        /// Sets the previously uploaded 'editable avatar' image as the
        /// current user avatar, cropped with the given values and
        /// optimized sizes.
        /// </summary>
        /// <param name="virtualPath"></param>
        public static void SaveProfilePicture(int userID, int x, int y, int width, int height)
        {
            string virtualPath = LcUrl.RenderAppPath + GetUserPhotoFolder(userID);
            var    folder      = System.Web.HttpContext.Current.Server.MapPath(virtualPath);

            // Remove previous cropped/sized/adapted photos (except editable one), all start with avatarNamePrefix
            // File.Delete doesn't allow wildcards, find and delete each one
            foreach (var f in Directory.GetFiles(folder, avatarNamePrefix + "*", SearchOption.TopDirectoryOnly))
            {
                File.Delete(f);
            }

            // Create optimized files
            using (var img = System.Drawing.Image.FromFile(folder + avatarName + ".jpg"))
            {
                // Crop
                var cropImg = LcImaging.Crop(img, x, y, width, height);
                img.Dispose();

                // Size prefix
                var sizeName = profilePictureFixedSizeWidth.ToString() + "x" + profilePictureFixedSizeHeight.ToString();

                // Save image with profile size and original color
                using (var modImg = LcImaging.Resize(cropImg, profilePictureFixedSizeWidth, profilePictureFixedSizeHeight, LcImaging.SizeMode.Cover, LcImaging.AnchorPosition.Center))
                {
                    modImg.Save(folder + avatarNamePrefix + sizeName + ".jpg");
                }

                // Save image with profile size and grayscale (-gray)
                using (var modImg = LcImaging.Grayscale(LcImaging.Resize(cropImg, profilePictureFixedSizeWidth, profilePictureFixedSizeHeight, LcImaging.SizeMode.Cover, LcImaging.AnchorPosition.Center)))
                {
                    modImg.Save(folder + avatarNamePrefix + sizeName + "-gray.jpg", System.Drawing.Imaging.ImageFormat.Jpeg);
                }

                // Same as previous but for hi-res 2x devices: (real pixel sizes is double but preserve the original size name to recognize it better adding the @2x suffix
                using (var modImg = LcImaging.Grayscale(LcImaging.Resize(cropImg, profilePictureFixedSizeWidth * 2, profilePictureFixedSizeHeight * 2, LcImaging.SizeMode.Cover, LcImaging.AnchorPosition.Center)))
                {
                    modImg.Save(folder + avatarNamePrefix + sizeName + "*****@*****.**", System.Drawing.Imaging.ImageFormat.Jpeg);
                }
                // NOTE Creation of images with more sizes (for small user widgets on reviews/bookings/etc) or filters go here

                cropImg.Dispose();
            }
        }
        /// <summary>
        /// Ask to edit the original saved profile picture applying a rotation.
        /// </summary>
        /// <param name="userID"></param>
        /// <param name="angle"></param>
        /// <returns>True when exists and everything fine, false if no photo exists so couldn't perform task. Throw exception if error</returns>
        public static bool EditEditableProfilePicture(int userID, float angle)
        {
            string virtualPath = LcUrl.RenderAppPath + GetUserPhotoFolder(userID);
            var    folder      = System.Web.HttpContext.Current.Server.MapPath(virtualPath);

            if (!Directory.Exists(folder))
            {
                return(false);
            }

            var file = folder + avatarName + ".jpg";

            if (!File.Exists(file))
            {
                return(false);
            }

            // Use file as image
            Image img = null;

            try
            {
                using (var srcImg = System.Drawing.Image.FromFile(file))
                {
                    // Resize to maximum allowed size (but not upscale) to allow user cropping later
                    var auxImg = LcImaging.Rotate(srcImg, angle, DefaultBackground);
                    img = LcImaging.Resize(auxImg, profilePictureFixedSizeWidth * profilePictureOriginalScale, profilePictureFixedSizeHeight * profilePictureOriginalScale, profilePictureSizeMode, LcImaging.AnchorPosition.Center, DefaultBackground);
                    auxImg.Dispose();
                }
                // Save:
                img.Save(file, System.Drawing.Imaging.ImageFormat.Jpeg);
            }
            finally
            {
                if (img != null)
                {
                    img.Dispose();
                }
            }

            return(true);
        }
        /// <summary>
        /// Sends user profile picture, or default image, to the Web Response Output.
        /// </summary>
        /// <param name="userID"></param>
        /// <param name="type"></param>
        public static void OutputProfilePicture(int userID, string type)
        {
            if (type == "2x")
            {
                type = "@2x";
            }
            else if (!String.IsNullOrWhiteSpace(type))
            {
                type = "-" + type;
            }

            var  Response     = HttpContext.Current.Response;
            var  userFolder   = GetUserPhotoFolder(userID);
            bool imageAdapted = false;
            var  sizeName     = profilePictureFixedSizeWidth.ToString() + "x" + profilePictureFixedSizeHeight.ToString();

            // And it happens again, new size change, so the next comment, just two times:
            // To fix #558, because the change of size (so file name changed too), we do double try
            // for a while
            // TODO FUTURE: When there are no more oldSize (or a few, convert it manually), remove this and its use
            const string veryOldSizeName = "176x184";
            const string oldSizeName     = "112x118";

            // "$avatar"
            // Standard name of pre-adapted image, just
            // serve ever the profile adapted photo for now instead the big colored original:
            var userPhoto = "$avatar-" + sizeName + "-gray" + type + ".jpg";

            imageAdapted = true;
            // Physical image path to userPhoto
            var path = HttpContext.Current.Request.MapPath(LcUrl.RenderAppPath + userFolder + userPhoto);

            // Try the OLD size if it doesn't exists the new:
            if (!System.IO.File.Exists(path))
            {
                imageAdapted = false;
                // Update name and path
                userPhoto = "$avatar-" + oldSizeName + "-gray" + type + ".jpg";
                path      = HttpContext.Current.Request.MapPath(LcUrl.RenderAppPath + userFolder + userPhoto);
            }
            // Try the VERY OLD size if it doesn't exists the new:
            if (!System.IO.File.Exists(path))
            {
                imageAdapted = false;
                // Update name and path
                userPhoto = "$avatar-" + veryOldSizeName + "-gray" + type + ".jpg";
                path      = HttpContext.Current.Request.MapPath(LcUrl.RenderAppPath + userFolder + userPhoto);
            }

            // Last fallback: default image
            if (!System.IO.File.Exists(path))
            {
                imageAdapted = true;
                if (type == "@2x")
                {
                    userPhoto = "img/userphotos/u0/avatar-2x.png";
                }
                else
                {
                    userPhoto = "img/userphotos/u0/avatar.png";
                }
                path = HttpContext.Current.Server.MapPath(LcUrl.RenderAppPath + userPhoto);
            }

            try
            {
                if (imageAdapted)
                {
                    new WebImage(path).Write();
                }
                else
                {
                    // Transform image to Grayscale and profile photo size to avoid big images:
                    // Is inside a using block because ensure close the image ressources and the file
                    using (var img = LcImaging.Grayscale(LcImaging.Resize(new System.Drawing.Bitmap(path), profilePictureFixedSizeWidth, profilePictureFixedSizeHeight, profilePictureSizeMode, LcImaging.AnchorPosition.Center)))
                    {
                        // Telling to the browser that this is a JPG image
                        Response.ContentType = "image/jpg";
                        // Send the transformed image to the browser
                        img.Save(Response.OutputStream, ImageFormat.Jpeg);
                    }
                }
            }
            catch { }
        }