using System;
using System.Collections.Generic;
using System.Drawing;
using System.Net;
using System.Windows.Forms;
using System.IO;
using System.Threading.Tasks;
using upScreenLib.LogConsole;

namespace upScreenLib
{
    public class CaptureControl
    {
        // Event raised when the image(s) has been captured
        public static event EventHandler CaptureComplete;

        // Event raised when the given url cannot be processed correctly
        public static event EventHandler<EventArgs> UrlCaptureFailed;

        // The screen which we're going to capture, defaults to the primary screen
        public static Screen CaptureScreen = Screen.PrimaryScreen;

        // List of files passed in as arguements
        public static List<string> ArgFiles = new List<string>();

        // All images captured in this session
        public static List<CapturedImage> CapturedImages = new List<CapturedImage>();

        // Screenshot of the entire CaptureScreen
        private static Bitmap FullScreen;

        #region Capture Full Screen

        public static void CaptureFullScreen()
        {
            GetBackgroundImage();

            Log.Write(l.Info, "Full screeen mode");

            SaveImage(FullScreen);
        }

        #endregion

        #region Capture Area

        public static void CaptureArea(Rectangle area)
        {
            Log.Write(l.Info, $"Area mode, x: {area.X} y: {area.Y} width: {area.Width} height: {area.Height}");

            var image = Crop(FullScreen, area);

            SaveImage(image);
        }

        #endregion

        #region Capture Window

        /// <summary>
        /// Find and capture the window 'behind' the specified point
        /// </summary>
        public static void CaptureWindow(Point p)
        {
            IntPtr window = ApiWrapper.Window.WindowFromPoint(p);

            if (window == IntPtr.Zero) return;

            var windowText = ApiWrapper.Window.GetWindowText(window);
            Log.Write(l.Info, $"Window mode: {windowText}");

            // Find the window from the click point
            while (ApiWrapper.Window.IsChild(ApiWrapper.Window.GetParent(window), window))
                window = ApiWrapper.Window.GetParent(window);

            // rectangle at the position and size of the selected window
            Rectangle windowRect = ApiWrapper.Window.GetWindowRect(window);

            // find the height of the windows taskbar
            int tbheight = CaptureScreen.Bounds.Height - CaptureScreen.WorkingArea.Height;

            // fix for when the window height is maximum
            if (windowRect.Height + windowRect.Y + tbheight + windowRect.Y == CaptureScreen.Bounds.Height)
            {
                windowRect.Y = 0;
                windowRect.Height = CaptureScreen.WorkingArea.Height;
            }

            // Make the rectangle location relative to the screen we are capturing
            windowRect.Location = windowRect.Location.Substract(CaptureScreen.Bounds.Location);

            // Fix negative locations
            if (windowRect.Location.X < 0)
            {
                windowRect.Width = windowRect.Width + windowRect.X;
                windowRect.X = 0;
            }
            if (windowRect.Location.Y < 0)
            {
                windowRect.Height = windowRect.Height + windowRect.Y;
                windowRect.Y = 0;
            }

            // Crop the full-screen image at the position/dimmensions of the selected window
            var image = Crop(FullScreen, windowRect);

            Common.OtherFormOpen = true;

            SaveImage(image);
        }

        #endregion

        #region Capture from Arguements

        /// <summary>
        /// Used when a file path is passed as an arguement to upScreen (when uploading from the context menus)
        /// Loads the image file into CapturedImage.Image
        /// </summary>
        public static void CaptureFromArgs()
        {
            Log.Write(l.Info, $"Context menu mode: {ArgFiles.Count} files");

            foreach (string filePath in ArgFiles)
            {
                Common.OtherFormOpen = true;

                SaveImage(filePath);
            }

            // raise CaptureComplete to start uploading
            CaptureComplete(null, EventArgs.Empty);
        }

        #endregion        

        #region Image Editing and Finalizing

        /// <summary>
        /// Crop the given image at the given rectangle's positon/dimmensions
        /// </summary>
        /// <param name="img">The image to crop</param>
        /// <param name="rect">The rectangle that defines the crop starting point and dimmensions</param>
        /// <returns></returns>
        public static Image Crop(Image img, Rectangle rect)
        {
            var bmp = new Bitmap(img);
            Rectangle frect = FinalizeRectangle(rect);
            Image bmpCropped = bmp.Clone(frect, bmp.PixelFormat);
            return bmpCropped;
        }

        /// <summary>
        /// Check if the rectangle gets outside the screen boundaries
        /// </summary>
        private static Rectangle FinalizeRectangle(Rectangle rect)
        {
            if (rect.Location.X + rect.Size.Width > CaptureScreen.Bounds.Width)
                rect.Width = CaptureScreen.Bounds.Width - rect.Location.X;

            if (rect.Location.Y + rect.Size.Height > CaptureScreen.Bounds.Height)
                rect.Height = CaptureScreen.Bounds.Height - rect.Location.Y;

            return rect;
        }

        public static void SaveImage(Image image)
        {
            var captureName = Common.RandomString(Common.Profile.FileLenght) + Common.GetFormat();

            var info = new CapturedImage
            {
                Name = captureName,
                LocalPath = Path.Combine(Settings.TempImageFolder, captureName),
                RemotePath = Common.Combine(Common.Profile.RemoteFolder, captureName)
            };

            Log.Write(l.Info, $"Saving image as: {info.LocalPath}");

            image.Save(info.LocalPath);
            CapturedImages.Add(info);

            // raise CaptureComplete to start uploading
            CaptureComplete(null, EventArgs.Empty);
        }

        public static void SaveImage(string localPath)
        {
            var image = Image.FromFile(localPath);
            var captureName = Common.RandomString(Common.Profile.FileLenght) + Common.GetFormat(image.RawFormat);

            var info = new CapturedImage
            {
                Name = captureName,
                LocalPath = Path.Combine(Settings.TempImageFolder, captureName),
                RemotePath = Common.Combine(Common.Profile.RemoteFolder, captureName)
            };

            Log.Write(l.Info, $"Saving image as: {info.LocalPath}");

            image.Save(info.LocalPath);
            CapturedImages.Add(info);
        }

        #endregion

        #region Image Uploading

        /// <summary>
        /// Start the upload if the client is connected and ready to go
        /// </summary>
        public static async Task CheckStartUpload()
        {
            if (!Client.taskCheckAccount.IsCompleted)
            {
                Log.Write(l.Info, "Image captured, client not ready to upload yet");
                Common.IsImageCaptured = true;
            }
            else
                await UploadImages();
        }

        /// <summary>
        /// Upload the captured images
        /// </summary>
        public static async Task UploadImages()
        {
            foreach (var image in CapturedImages)
            {
                await Client.UploadImage(image);
            }
        }

        #endregion        

        #region Image fetching

        /// <summary>
        /// Capture the entire screen into CapturedImage.Bmp
        /// </summary>
        public static void GetBackgroundImage()
        {
            var bmp = new Bitmap(CaptureScreen.Bounds.Width, CaptureScreen.Bounds.Height);
            var gfx = Graphics.FromImage(bmp);
            gfx.CopyFromScreen(CaptureScreen.Bounds.X, CaptureScreen.Bounds.Y, 0, 0, CaptureScreen.Bounds.Size, CopyPixelOperation.SourceCopy);

            FullScreen = bmp;
        }

        /// <summary>
        /// Set CapturedImage.Image as the image/image file copied to clipboard
        /// </summary>
        public static void GetFromClipboard()
        {
            Image image = null;

            // Load the image from a copied image
            if (Clipboard.ContainsImage())
            {
                Log.Write(l.Info, "Clipboard mode, copied image");
                image = Clipboard.GetImage();
            }
            // Load the image from a copied image file
            else if (Clipboard.ContainsFileDropList())
            {
                Log.Write(l.Info, "Clipboard mode, file list");
                foreach (var localFile in Clipboard.GetFileDropList())
                {
                    SaveImage(localFile);
                }
                // raise CaptureComplete to start uploading
                CaptureComplete(null, EventArgs.Empty);
            }
            // Load the image from a copied image url
            else if (Common.ImageUrlInClipboard)
            {
                var url = Clipboard.GetText();
                Log.Write(l.Info, $"Clipboard mode, url: {url}");
                image = GetFromUrl(url);
            }

            // Prevent null reference exception
            if (image == null) return;

            Common.OtherFormOpen = true;

            SaveImage(image);
        }

        /// <summary>
        /// Download and return image from url
        /// </summary>
        /// <param name="url">url to image</param>        
        private static Image GetFromUrl(string url)
        {
            var uri = new UriBuilder(url).Uri;
            try
            {
                var request = (HttpWebRequest)WebRequest.Create(uri);
                request.Method = "HEAD";
                request.ServicePoint.Expect100Continue = false;
                request.ContentType = "application/x-www-form-urlencoded";

                using (var response = request.GetResponse())
                {
                    var fileType = response.Headers[HttpResponseHeader.ContentType];
                    var allowedTypes = new List<string> { "image/png", "image/jpeg", "image/jpg", "image/gif" };

                    //Compare web file MIME to valid types
                    if (allowedTypes.Contains(fileType))
                    {
                        //Download file to MemoryStream
                        var imgBytes = new WebClient().DownloadData(uri);
                        var memStream = new MemoryStream(imgBytes);
                        return Image.FromStream(memStream);
                    }
                }
            }
            catch
            {
                // Something went wrong...
                UrlCaptureFailed(null, EventArgs.Empty);
            }
            return null;
        }

        #endregion
    }
}