using System;
using System.Collections.Generic;
using System.Text;
using Lumenera.USB;
using ImageRelated;
using System.Drawing;
using System.Drawing.Imaging;


namespace ImageRelated
{
/// <summary>
/// List of function frequently use for image manipulation.
/// </summary>
    public static class FrequentOperation
    {
        /// <summary>
        /// Calculate image intensity on RAW data
        /// </summary>
        /// <param name="pdata">Image byte array.</param>
        /// <param name="width">Width of the image.</param>
        /// <param name="height">Height of the image.</param>
        /// <param name="pf">Pixel format PF8 or PF16.</param>
        /// <returns>Image Intensity.</returns>
        public static float RawIntensity(ref byte[] pdata, int width, int height, dll.LucamPixelFormat pf)
        {
            int intensity = 0;
            int imageSize = width * height;  // Calculate image size.
            
            if (pf == dll.LucamPixelFormat.PF_16) // Chech if 16 bits images.
            {
                imageSize *= 2;             // Double the image size.
            }
            for (int i = 0; i < imageSize; i++)
            {
                intensity += pdata[i];
            }
            return (intensity / imageSize);
        }
        
        /// <summary>
        /// Calculate image intensity on RAW Data
        /// </summary>
        /// <param name="pdata">Image byte array.</param>
        /// <returns>Image Intensity.</returns>
        public static float RawIntensity(ref byte[] pdata)
        {
            int intensity = 0;
            foreach (byte a in pdata)
            {
                intensity += a;
            }
            return (intensity / pdata.Length);
        }
        
        /// <summary>
        /// Calculate image intensity of RAW data
        /// </summary>
        /// <param name="pdata">Image byte array.</param>
        /// <param name="width">Image width</param>
        /// <param name="height">Image Height</param>
        /// <returns>Image Intensity</returns>
        public static float RawIntensity(ref byte[] pdata, int width, int height)
        {
            int intensity = 0;
            int imageSize = width * height;  // Calculate image size.
            for (int i = 0; i < imageSize; i++)
            {
                intensity += pdata[i];
            }
            return (intensity / imageSize);
        }
        
        
        /// <summary>
        /// Will take a frame from the live video feed and calculate the image intensity on the RAW data.
        /// Then the image intensity is compare with the threshold value.
        /// </summary>
        /// <param name="camera">Camera Handle.</param>
        /// <param name="blackThreshold">Intensity Threshold to validate for failed or pass</param>
        /// <param name="ff">Reference to the current frame format.</param>
        /// <returns>false  -->if image intensity bellow the threshold value or an api called was unsuccessfull.
        ///          true   -->image was not considered black.</returns>
        /// <remarks>1) Because of noise and the capability of trying to boost gains when dark images on some image sensor, it is not 
        ///             recommended to use a threshold of 0.</remarks>
        public static bool ImageFrameBlackTest(IntPtr camera, int blackThreshold, ref dll.LucamFrameFormat ff)
        {
            int imageSize = 0;
            //Image size calculation.
            imageSize = (ff.Width / ff.SubSampleX) * (ff.Height / ff.SubSampleY);
            if (ff.PixelFormat == dll.LucamPixelFormat.PF_16)
                imageSize *= 2;          // Double image size if in 16 bits mode;

            // Memory allocation
            byte[] pdata = new byte[imageSize];


            if (!dll.LucamTakeVideo(camera, 1, pdata)) return (false); // Image Caption.
            int intensity = (int) FrequentOperation.RawIntensity( ref pdata);      // Calculate image intensity.
            if (intensity < blackThreshold)
                return (false);
            return (true);
        }

        /// <summary>
        /// Will take a frames from the live video feed stream and calculate the image intensity on the RAW data. 
        /// then the image intensity is compare with the threshold value.
        /// </summary>
        /// <param name="camera">Camera Handle</param>
        /// <param name="nbImage">Number of image to take.</param>
        /// <param name="blackThreshold">Intensity threshold to validate for failed or pass</param>
        /// <param name="ff">Reference to the current frame format.</param>
        /// <param name="ellaspedTimed">Output Time it took to capture the video frames</param>
        /// <param name="errorString">List invalid frame number in a string.</param>
        /// <returns>Return the number of invalid frames.</returns>
        public static int ImageFrameBlackTest(IntPtr camera, int nbImage, int blackThreshold, ref dll.LucamFrameFormat ff, ref TimeSpan ellaspedTimed, ref string errorString)
        {
            byte[] pdata;
            byte[] tmpImage;
            int imageSize = 0;
            int FrameErrorCounter = 0;
            DateTime start = new DateTime();
            DateTime stop = new DateTime();
            TimeSpan timeAction = new TimeSpan();
            start = DateTime.Now;
            stop = DateTime.Now;
            timeAction = stop - start;
            //Image size calculation.
            imageSize = (ff.Width / ff.SubSampleX) * (ff.Height / ff.SubSampleY);
            if (ff.PixelFormat == dll.LucamPixelFormat.PF_16)
                imageSize *= 2;          // Double image size if in 16 bits mode;

            // Memory allocation
            try
            {
                pdata = new byte[imageSize * nbImage];
                tmpImage = new byte[imageSize];  
            }
            catch
            {
                errorString +="Cannot allocate memory to capture images.";
                return(-1);
            }
            // Image Caption.
            start = DateTime.Now;
            if (!dll.LucamTakeVideo(camera, nbImage, pdata)) return (nbImage);
            stop = DateTime.Now;
            ellaspedTimed = stop - start;
            ellaspedTimed -=timeAction;
            
            // Calculatine Images intensity 
            int intensity = 0;
            for (int i = 0; i < nbImage; i++)
            {
                intensity = 0;
                
                // copy image in an array to be copied.
                for (int nbByte=0; nbByte<imageSize;nbByte++)
                {
                    tmpImage[nbByte] = pdata[(imageSize*i)+nbByte];
                }
                
                intensity = (int)RawIntensity( ref tmpImage);
                if (intensity < blackThreshold )
                {
                    FrameErrorCounter++;
                    errorString += string.Format("{0}, ", i);
                }
            }

            return (FrameErrorCounter);
        }


        /// <summary>
        /// Will take snapshots from the still stream and calculate the image intensity on the RAW data.
        /// then the image intensity is compare with the threshold value.
        /// </summary>
        /// <param name="camera">Camera Handle</param>
        /// <param name="nbImage">Number of snapshot to take.</param>
        /// <param name="blackThreshold">pass of failed intensity criteria.</param>
        /// <param name="snap">Reference to the snapshot data structure.</param>
        /// <param name="ellaspedTimed">Image acquisitions time</param>
        /// <param name="errorString">List of the invalid frames.</param>
        /// <returns></returns>
        public static int ImageFrameBlackTest(IntPtr camera, int nbImage, int blackThreshold, ref dll.LucamSnapshot snap, ref TimeSpan ellaspedTimed, ref string errorString)
        {
            int imageSize = 0;
            int FrameErrorCounter = 0;
            DateTime start = new DateTime();
            DateTime stop = new DateTime();
            TimeSpan timeTravel = new TimeSpan();
            TimeSpan timeAction = new TimeSpan();
            start = DateTime.Now;
            stop = DateTime.Now;
            timeAction = stop - start;

            //Image size calculation.
            imageSize = (snap.Format.Width / snap.Format.SubSampleX) * (snap.Format.Height / snap.Format.SubSampleY);
            if ( snap.Format.PixelFormat == dll.LucamPixelFormat.PF_16)
                imageSize *= 2;          // Double image size if in 16 bits mode;

            // Memory allocation
            byte[][] rawData = new byte[nbImage][];
            for (int i = 0; i < nbImage; i++)
            {
                rawData[i] = new byte[imageSize];
            }

            // Image Caption.
            dll.LucamEnableFastFrames(camera, ref snap);
            start = DateTime.Now;
            for (int i = 0; i < nbImage; i++)
            {
                if (!dll.LucamTakeFastFrame(camera, rawData[i]))
                {
                    dll.LucamDisableFastFrames(camera);
                    return (i);
                }
            }
            stop = DateTime.Now;
            timeTravel = (stop - start);
            timeTravel -= timeAction;
            dll.LucamDisableFastFrames(camera);


            int intensity = 0;
            for (int i = 0; i < nbImage; i++)
            {
                intensity = (int)RawIntensity(ref rawData[i]);
                if (intensity < blackThreshold)
                {
                    FrameErrorCounter++;
                    errorString  += string.Format("{0}, ", i);
                }
            }

            return (FrameErrorCounter);
        }
        
        /// <summary>
        /// Capture and image and convert on all useful format
        /// </summary>
        /// <param name="cam">Camera handle.</param>
        /// <param name="rawImage">Buffer that will contain the RAW image.</param>
        /// <param name="rgbImage">Buffer that will contain the RGB image.</param>
        /// <param name="winImage">Buffer that will contain the Windows Bitmap image.</param>
        /// <param name="ff">Lumenera frame format.</param>
        /// <param name="conversion">Lumenera color convertion parameters.</param>
        /// <param name="ellaspedTimed">Ellasped time between capture.</param>
        /// <param name="errorString">buffer that will contain the error message.</param>
        /// <returns>true if successful</returns>
        public static bool ImageCapture(IntPtr cam, ref byte[] rawImage, ref byte[] rgbImage, ref Bitmap winImage, ref dll.LucamFrameFormat ff, ref dll.LucamConversion conversion, ref TimeSpan ellaspedTimed, ref string errorString)
        {
            BitmapData myImageData;
            Rectangle lockdata;
            dll.LucamColorFormat  colorFormat = dll.LucamColorFormat.MONO ;
            // Image size calculation.
            int imageSize = (ff.Width / ff.SubSampleX) * (ff.Height / ff.SubSampleY);
            if (ff.PixelFormat == dll.LucamPixelFormat.PF_16)
                imageSize *= 2;                  // Double image size if in 16 bits mode;
            int rgbImageSize = imageSize * 3;
            
            // Get the color format of the camera.
            try
            {
                colorFormat =(dll.LucamColorFormat) api.GetProperty(cam, dll.LucamProperty.COLOR_FORMAT);
            }
            catch
            {
                errorString += "Cannot read the color format from camera";
                return (false);
            }
                  
            //Memory allocation.
            rawImage = new byte[imageSize];         // Resize raw image buffer to the appropriate size.
            rgbImage = new byte[rgbImageSize];      // Resize rbg buffer to the appropriate size.

            // Image Caption.
            if (!dll.LucamTakeVideo(cam, 1, rawImage)) return (false);
            //imageDateTime = DateTime.Now;

            if (colorFormat != dll.LucamColorFormat.MONO)  // Check if this is a color camera.
            {
                // Color camera.
                //RGB Converstion
                if (ff.PixelFormat == dll.LucamPixelFormat.PF_8)
                {
                    if (!dll.LucamConvertFrameToRgb24(cam, rgbImage, rawImage, ff.Width / ff.SubSampleX, ff.Height / ff.SubSampleY, dll.LucamPixelFormat.PF_8, ref conversion)) return (false);
                }
                else
                {
                    if (!dll.LucamConvertFrameToRgb48(cam, rgbImage, rawImage, ff.Width / ff.SubSampleX, ff.Height / ff.SubSampleY, dll.LucamPixelFormat.PF_16, ref conversion)) return (false);
                }

                // Create bitmap with RGB
                if (ff.PixelFormat == dll.LucamPixelFormat.PF_8)
                    winImage = new Bitmap(ff.Width / ff.SubSampleX, ff.Height / ff.SubSampleY, PixelFormat.Format24bppRgb);
                else
                    winImage = new Bitmap(ff.Width / ff.SubSampleX, ff.Height / ff.SubSampleY, PixelFormat.Format48bppRgb);
                myImageData = new BitmapData();
                lockdata = new Rectangle(0, 0, ff.Width / ff.SubSampleX, ff.Height / ff.SubSampleY);
                myImageData = winImage.LockBits(lockdata, ImageLockMode.ReadWrite, winImage.PixelFormat, myImageData);
                System.Runtime.InteropServices.Marshal.Copy(rgbImage, 0, myImageData.Scan0, rgbImageSize);
                winImage.UnlockBits(myImageData);
                winImage.RotateFlip(RotateFlipType.RotateNoneFlipY);

                //setup panel.
                //imagePanel.Width= myImage.Width;
                //imagePanel.Height = myImage.Height;
                //imagePanel.Image = myImage;
            }
            else
            {
                //Monochrome camera
                if (ff.PixelFormat != dll.LucamPixelFormat.PF_8)
                {
                    // Camera is 16 bit
                    if (!api.IsFormatLittleEndian(cam))
                    {
                        // Convert big endian data to little endian data.
                    }
                    winImage = new Bitmap(ff.Width / ff.SubSampleX, ff.Height / ff.SubSampleY, PixelFormat.Format16bppGrayScale);
                }
                else
                {
                    // 8 bit image.
                    if (!dll.LucamConvertFrameToRgb24(cam, rgbImage, rawImage, ff.Width / ff.SubSampleX, ff.Height / ff.SubSampleY, dll.LucamPixelFormat.PF_8, ref conversion))
                    {
                        errorString += "Cannot make rgb convertion of the colorimage.";
                        return (false);
                    }
                    winImage = new Bitmap(ff.Width / ff.SubSampleX, ff.Height / ff.SubSampleY, PixelFormat.Format24bppRgb);
                }
                myImageData = new BitmapData();
                lockdata = new Rectangle(0, 0, ff.Width / ff.SubSampleX, ff.Height / ff.SubSampleY);
                myImageData = winImage.LockBits(lockdata, ImageLockMode.ReadWrite, winImage.PixelFormat, myImageData);
                System.Runtime.InteropServices.Marshal.Copy(rgbImage, 0, myImageData.Scan0, rgbImageSize);
                winImage.UnlockBits(myImageData);
                //myImage.RotateFlip(RotateFlipType.RotateNoneFlipY);
                 //imagePanel.Image = myImage;
                
            }
            
            return (true);
        }
    
    }
}
