using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Emgu.CV;
using Emgu.CV.Structure;
using Emgu.CV.UI;
using Emgu.CV.CvEnum;
using System.Drawing;
using System.Windows.Forms;
using System.Diagnostics;



namespace Foto_Scanner_New
{
    /// <summary>
    /// Utils for image processing
    /// </summary>
    class CVUtils
    {
        private static double CONT_AREA = 300;
        private static double CONT_PERIMETER_MIN = 0.0;
        private static double CONT_PERIMETER_MAX = 0.0;
        private static double FAK_MIN = 0.33;
        private static double FAK_MAX = 0.48;
 
        
        /// <summary>
        /// Method to detect photos in the scanned images. 
        /// Returns list with detected imaged and indices.
        /// </summary>
        /// <param name="img">Input image</param>
        /// <param name="threshhold">Threshold segmentation</param>
        /// <param name="list">List with detected photos</param>
        /// <param name="proc_50">Search for photos bigger 50 percent</param>
        /// <param name="perimeterUse">Search photos with given perimeter (value)</param>
        /// <param name="perimeterBool">Search photos with given perimeter</param>
        /// <returns></returns>
        public static Dictionary<int, DetectedRectangles> detectRectangles(Image<Gray, Byte> img, int threshhold, Dictionary<int, DetectedRectangles> list, bool proc_50, int perimeterUse, bool perimeterBool)
        {
            int umfang = 0;
            if (perimeterBool)
            {
                if (perimeterUse > 100)
                {
                    umfang = perimeterUse;
                    CONT_PERIMETER_MIN = umfang - 300;
                    CONT_PERIMETER_MAX = umfang + 300;
                    CONT_AREA = umfang * 0.05;
                }
                else {
                    umfang = img.Width + img.Width + img.Height + img.Height;
                }
            }

            else
            {
                umfang = img.Width + img.Width + img.Height + img.Height;

                if (!proc_50)
                {
                    CONT_PERIMETER_MIN = umfang * FAK_MIN;
                    CONT_PERIMETER_MAX = umfang * FAK_MAX;
                    CONT_AREA = umfang * 0.09;
                }
                else
                {
                    CONT_PERIMETER_MIN = umfang * 0.5;
                    CONT_PERIMETER_MAX = umfang * 0.8;
                    CONT_AREA = umfang * 0.09;
                }
            }


            // -- Algorith --------------------------------------------

            using (MemStorage storage = new MemStorage())
            {   
                for (Contour<Point> contours = img.FindContours(); contours != null; contours = contours.HNext)
                {
                    // Contour size > AREA
                    if (contours.Area > CONT_AREA)
                    {
                        Contour<Point> currentContour = contours.ApproxPoly(contours.Perimeter * 0.05, storage);
                        if (currentContour.Perimeter > CONT_PERIMETER_MIN && currentContour.Perimeter < CONT_PERIMETER_MAX)
                       
                        if (currentContour.Total == 4) //The contour has 4 vertices.
                        {
                           
                            bool isRectangle = true;
                            Point[] pts = currentContour.ToArray();
                            LineSegment2D[] edges = PointCollection.PolyLine(pts, true);

                            for (int i = 0; i < edges.Length; i++)
                            {
                                double angle = Math.Abs(
                                    edges[(i + 1) % edges.Length].GetExteriorAngleDegree(edges[i]));
                                if (angle < 80 || angle > 100)
                                {
                                    isRectangle = false;
                                    break;
                                }
                            }

                            if (1==1) // could use (if(isRectangle))
                            {
                                DetectedRectangles drect = new DetectedRectangles();
                                Rectangle rTMP = currentContour.BoundingRectangle;
                                drect.Rect = rTMP;
                                drect.Use = false;
                               
                                int index = 1;
                                foreach (KeyValuePair<int, DetectedRectangles> pair in list)
                                {
                                    int key = pair.Key;
                                    DetectedRectangles val = pair.Value;
                                    if (val==null)
                                    {
                                        index = pair.Key;
                                        break;
                                    }
                                    else index++;
                                }
                                
                                bool insert = true;
                                if (index == 1) {
                                    insert = true;
                                    drect.Number = 1;
                                }
                                
                                foreach (KeyValuePair<int, DetectedRectangles> pair in list)
                                {
                                    
                                    DetectedRectangles current = pair.Value;
                                    if (current != null)
                                    {
                                        //insert = true;
                                        if (check(current.Rect, rTMP))
                                        {
                                            insert = false;
                                        }
                                       
                                    }
                                }

                                if (insert){
                                    //Console.Out.WriteLine(" ------------- INSERT NEW ---------------");
                                    Console.Out.WriteLine("PERIMETER: " + currentContour.Perimeter);
                                    Console.Out.WriteLine("MIN: " + CONT_PERIMETER_MIN);
                                    Console.Out.WriteLine("MAX: " + CONT_PERIMETER_MAX);
                                    list[index] = drect;
                                    drect.Number = index;
                                }

                            }
                        }
                    }
                }
            }

            return list;
        }

        /// <summary>
        /// Ckecks if rectangle in inside a rectangle or has the same coordinates.
        /// </summary>
        /// <param name="inList">Rectangles in list</param>
        /// <param name="testRect">New rectangle</param>
        /// <returns>True, if rectangle exists</returns>
        private static bool check(Rectangle inList, Rectangle testRect) 
        {
            bool ok = false;

            if (inList.Location == testRect.Location) {
                ok = true;
            }
           
            if (testRect.IntersectsWith(inList)) {
                ok = true;
            }

            if (testRect.Contains(inList)) {
                ok = true;
            }
            
            int xPos = inList.Location.X;
            int yPos = inList.Location.Y;

            int testXPos = testRect.Location.X;
            int testYPos = testRect.Location.Y;

            if ( (( testXPos >= (xPos-5) ) && ( testXPos <= (xPos + 5) )) &&
                    (( testYPos >= (yPos - 5)) && (testYPos <= (yPos + 5) ))
                )
            {
                ok = true;
            }

            
            return ok;
        }

        /// <summary>
        /// Cuts out the detected images from the scanned photo.
        /// </summary>
        /// <param name="?">Detected images</param>
        /// <param name="img">Scanned image</param>
        public static List<Image<Bgr, Byte>> getDetectedImagesFromInput(Dictionary<int, DetectedRectangles> detectedRects, Image<Bgr, Byte> img) 
        {
            var listRectNew = new List<DetectedRectangles>();
            List<Image<Bgr, Byte>> images = null; 
            List<DetectedRectangles> decList = new List<DetectedRectangles>();

            if (detectedRects != null)
            {
                listRectNew = new List<DetectedRectangles>();

                foreach (KeyValuePair<int, DetectedRectangles> pair in detectedRects)
                {

                    DetectedRectangles tmpRect = pair.Value;

                    if (tmpRect != null)
                    {
                        decList.Add(tmpRect);
                    }
                }
            }

            if (decList.Count > 0) {
            
                foreach (DetectedRectangles n in decList) if (n.Use) listRectNew.Add(n);

                if (listRectNew == null || listRectNew.Count == 0) {
                    MessageBox.Show("No photos selected!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                    return null;
                }
                else if (detectedRects.Count > 0)
                {
                    images= new List<Image<Bgr, Byte>>();                     
                    foreach (DetectedRectangles dr in listRectNew)
                    {
                        Image<Bgr, Byte> sector = new Image<Bgr, byte>(dr.Rect.Width, dr.Rect.Height);
                        sector = img.GetSubRect(dr.Rect); ;
                        images.Add(sector);
                    }
                    return images;
                }
            }
            else{
                MessageBox.Show("No photos selected!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return null;
            }

            return null;
        }

        /// <summary>
        /// Runs external program IrfanView.
        /// </summary>
        /// <param name="img">Input image path</param>
        /// <returns>True if program exists</returns>
        public static bool runIV(string img, string direction, int w, int h) 
        {
            bool ok = false;

            Process ivProcess = new Process();
            string iv = MainFormPropertiescs.getDirIV();
            ivProcess.StartInfo.FileName = iv + "i_view32.exe";

            string argument = "";
            argument = img;
            argument = argument.Replace("/", "\\");
            argument = argument.Replace("\\\\", "\\");
            string oldArg = "";
            oldArg = argument;

            if (!direction.Equals("") && direction.Equals("R")) {
                argument += " /rotate_r /resize=("+w+", "+h+") /convert=" + oldArg + "";            
            }

            if (!direction.Equals("") && direction.Equals("L"))
            {
                argument += " /rotate_l /resize=(" + w + ", " + h + ") /convert=" + oldArg + "";
            }

            ivProcess.StartInfo.Arguments = argument;
            ivProcess.StartInfo = ivProcess.StartInfo;

            Console.Out.WriteLine(argument);
            try
            {
                ivProcess.Start();
                ivProcess.WaitForExit();
                ivProcess.Close();
                ivProcess = null;
            }
            catch (System.InvalidOperationException e1)
            {
                Console.Out.WriteLine(e1);
                return ok;
            }
            catch (System.ComponentModel.Win32Exception e2)
            {
                Console.Out.WriteLine(e2);
                return ok;
            }
            finally {
                ok = true;
            }

            return ok;
        }

        /// <summary>
        /// Runs external program IrfanView.
        /// </summary>
        /// <param name="img">Input image path</param>
        /// <returns>True if program exists</returns>
        public static string runIVScan(string img, int _res, bool details)
        {
            string fbackname = "";

            bool ok = false;

            Process ivProcess = new Process();
            string iv = MainFormPropertiescs.getDirIV();
            ivProcess.StartInfo.FileName = iv + "i_view32.exe";

            string argument = "";
            argument = img;
            argument = argument.Replace("/", "\\");
            argument = argument.Replace("\\\\", "\\");
            string oldArg = "";
            oldArg = argument;

            if (details)
            {
                //int res = _res;
                //argument += "/convert=" + oldArg + "";
            }
            else
            {
                if (true)
                {
                    int res = _res;
                    argument += " /dpi=(" + res + "," + res + ") /scanhidden /convert=" + oldArg + "";
                    //argument += " /dpi=(" + res + "," + res + ") /convert=" + oldArg + "";
                }
            }

            Console.Out.WriteLine(":: " + argument);
            
            ivProcess.StartInfo.Arguments = argument;
            ivProcess.StartInfo = ivProcess.StartInfo;

            Console.Out.WriteLine(argument);
            try
            {
                ivProcess.Start();
                ivProcess.WaitForExit();
                ivProcess.Close();
                ivProcess = null;
                fbackname = img;
            }
            catch (System.InvalidOperationException e1)
            {
                Console.Out.WriteLine(e1);
                return fbackname;
            }
            catch (System.ComponentModel.Win32Exception e2)
            {
                Console.Out.WriteLine(e2);
                return fbackname;
            }
            finally
            {
                ok = true;
            }

            return fbackname;
        }
    }


}