using OpenCvSharp;
using System;

namespace OpenCV.OpenCV
{
    partial class OpenCVClass : IDisposable
    {
        IplImage _gray;
        IplImage _inversion;
        IplImage _bin;
        IplImage _blur;
        IplImage _zoomin;
        IplImage _zoomout;
        IplImage _resize;
        IplImage _slice;
        IplImage _symm;
        IplImage _rotate;
        IplImage _affine;
        IplImage _perspective;
        IplImage _draw;
        IplImage _hsv;
        IplImage _hsvRed;
        IplImage _morp;

        IplConvKernel _convKernel;

        public IplImage GrayScale(IplImage src)
        {
            _gray = new IplImage(src.Size, BitDepth.U8, 1);
            Cv.CvtColor(src, _gray, ColorConversion.BgrToGray);
            return _gray;
        }

        public IplImage InversionImage(IplImage src)
        {
            _inversion = new IplImage(src.Size, BitDepth.U8, 3);
            Cv.Not(src, _inversion);
            return _inversion;
        }

        public IplImage Binary(IplImage src, int threshold)
        {
            _bin = GrayScale(src);
            Cv.Threshold(_bin, _bin, threshold, 255, ThresholdType.Binary);
            return _bin;
        }

        public IplImage Blur(IplImage src)
        {
            _blur = new IplImage(src.Size, BitDepth.U8, 3);
            Cv.Smooth(src, _blur, SmoothType.Blur, 9); // param1은 홀수, 중간 픽셀 선택을 위함
            return _blur;
        }

        public IplImage ZoomIn(IplImage src)
        {
            _zoomin = new IplImage(new CvSize(src.Width * 2, src.Height * 2) , BitDepth.U8, 3);
            Cv.PyrUp(src, _zoomin, CvFilter.Gaussian5x5);
            return _zoomin;
        }

        public IplImage ZoomOut(IplImage src)
        {
            _zoomout = new IplImage(new CvSize(src.Width / 2, src.Height / 2), BitDepth.U8, 3);
            Cv.PyrDown(src, _zoomout, CvFilter.Gaussian5x5);
            return _zoomout;
        }

        public IplImage Resize(IplImage src, double widthRate, double heightRate)
        {
            _resize = new IplImage(new CvSize((int)(src.Width * widthRate), (int)(src.Height * heightRate)), BitDepth.U8, 3);
            Cv.Resize(src, _resize, Interpolation.Linear);
            return _resize;
        }

        public IplImage Slice(IplImage src, int x, int y, int roiWidht, int roiHeight)
        {
            //slice = new IplImage(new CvSize(roiWidht, roiHeight), BitDepth.U8, 3);

            //src.ROI = new CvRect(x, y, roiWidht, roiHeight);
            //src.SetROI(new CvRect(x, y, roiWidht, roiHeight));
            //Cv.SetImageROI(src, new CvRect(x, y, roiWidht, roiHeight));

            //Cv.Copy(src, slice);
            //Cv.Resize(src, slice);
            //slice = src.Clone(); // 속성까지 복사되어 채널 등 값이 변경됨

            //src.ResetROI();
            //Cv.ResetImageROI(src);

            CvRect roi = new CvRect(x, y, roiWidht, roiHeight);
            _slice = src.Clone(roi);

            return _slice;
        }

        public IplImage Symmetry(IplImage src, FlipMode mode)
        {
            _symm = new IplImage(src.Size, BitDepth.U8, 3);
            Cv.Flip(src, _symm, mode);
            return _symm;
        }

        public IplImage Rotate(IplImage src, double angle)
        {
            _rotate = new IplImage(src.Size, BitDepth.U8, 3);
            CvMat matrix = Cv.GetRotationMatrix2D(new CvPoint2D32f(src.Width / 2, src.Height / 2), angle, 1);
            Cv.WarpAffine(src, _rotate, matrix, Interpolation.Linear, CvScalar.ScalarAll(0));
            return _rotate;
        }

        public IplImage AffineImage(IplImage src, CvPoint2D32f[] srcPoints, CvPoint2D32f[] dstPoints)
        {
            if (srcPoints.Length != 3 || dstPoints.Length != 3)
                return null;

            _affine = new IplImage(src.Size, BitDepth.U8, 3);
            CvMat matrix = Cv.GetAffineTransform(srcPoints, dstPoints);
            Cv.WarpAffine(src, _affine, matrix, Interpolation.Linear, CvScalar.RealScalar(0));
            return _affine;
        }

        public IplImage PerspectiveImage(IplImage src, CvPoint2D32f[] srcPoints, CvPoint2D32f[] dstPoints)
        {
            if (srcPoints.Length != 4 || dstPoints.Length != 4)
                return null;

            _perspective = new IplImage(src.Size, BitDepth.U8, 3);
            CvMat matrix = Cv.GetPerspectiveTransform(srcPoints, dstPoints);
            Cv.WarpPerspective(src, _perspective, matrix, Interpolation.Linear, CvScalar.RealScalar(0));
            return _perspective;
        }

        public IplImage DrawImage()
        {
            _draw = new IplImage(new CvSize(640, 480), BitDepth.U8, 3);
            Cv.DrawLine(_draw, new CvPoint(100, 100), new CvPoint(500, 200), CvColor.Blue, 20);
            Cv.DrawCircle(_draw, new CvPoint(200, 200), 50, CvColor.Red, -1);
            Cv.DrawRect(_draw, new CvPoint(300, 150), new CvPoint(500, 300), CvColor.Green, 5);
            Cv.DrawEllipse(_draw, new CvPoint(150, 400), new CvSize(100, 50), 0, 90, 360, CvColor.Green, -1);
            Cv.PutText(_draw, "Open CV", new CvPoint(400, 400), new CvFont(FontFace.HersheyComplex, 1, 1), CvColor.White);

            return _draw;
        }

        public IplImage HSV(IplImage src)
        {
            _hsv = new IplImage(src.Size, BitDepth.U8, 3);

            IplImage h = new IplImage(src.Size, BitDepth.U8, 1);
            IplImage s = new IplImage(src.Size, BitDepth.U8, 1);
            IplImage v = new IplImage(src.Size, BitDepth.U8, 1);

            Cv.CvtColor(src, _hsv, ColorConversion.BgrToHsv);
            Cv.Split(_hsv, h, s, v, null);
            
            _hsv.SetZero();

            // Hue
            //Cv.InRangeS(h, 20, 30, h); // Yellow
            //Cv.Copy(src, hsv, h);

            // Hue - Red
            IplImage lowerRed = new IplImage(src.Size, BitDepth.U8, 1);
            IplImage upperRed = new IplImage(src.Size, BitDepth.U8, 1);
            IplImage red = new IplImage(src.Size, BitDepth.U8, 1);
            Cv.InRangeS(h, 0, 10, lowerRed);
            Cv.InRangeS(h, 170, 179, upperRed);
            Cv.AddWeighted(lowerRed, 1.0, upperRed, 1.0, 0.0, red);
            Cv.Copy(src, _hsv, red);

            // Saturation
            //Cv.InRangeS(s, 20, 30, s);
            //Cv.Copy(src, hsv, s);

            // Value
            //Cv.InRangeS(v, 20, 30, v);
            //Cv.Copy(src, hsv, v);

            return _hsv;
        }

        public IplImage HSVRed(IplImage src)
        {
            _hsvRed = new IplImage(src.Size, BitDepth.U8, 3);

            IplImage lowerRed = new IplImage(src.Size, BitDepth.U8, 1);
            IplImage upperRed = new IplImage(src.Size, BitDepth.U8, 1);
            IplImage red = new IplImage(src.Size, BitDepth.U8, 1);

            Cv.CvtColor(src, _hsvRed, ColorConversion.BgrToHsv);

            Cv.InRangeS(_hsvRed, new CvScalar(0, 100, 100), new CvScalar(10, 255, 255), lowerRed);
            Cv.InRangeS(_hsvRed, new CvScalar(170, 100, 100), new CvScalar(179, 255, 255), upperRed);
            Cv.AddWeighted(lowerRed, 1.0, upperRed, 1.0, 0.0, red);

            _hsvRed.SetZero();
            Cv.Copy(src, _hsvRed, red);

            return _hsvRed;
        }

        private void Release(params IplImage[] images)
        {
            for (int i = 0; i < images.Length; i++)
            {
                if (images[i] != null)
                    Cv.ReleaseImage(images[i]);
            }
        }

        public IplImage DilateImage(IplImage src, int threshold, int iteration)
        {
            _morp = new IplImage(src.Size, BitDepth.U8, 1);
            _bin = Binary(src, threshold);
            Cv.Dilate(_bin, _morp, _convKernel, iteration);

            return _morp;
        }

        public IplImage ErodeImage(IplImage src, int threshold, int iteration)
        {
            _morp = new IplImage(src.Size, BitDepth.U8, 1);
            _bin = Binary(src, threshold);
            Cv.Erode(_bin, _morp, _convKernel, iteration);

            return _morp;
        }

        public IplImage DEImage(IplImage src, int threshold, int iteration)
        {
            _morp = new IplImage(src.Size, BitDepth.U8, 1);
            _bin = Binary(src, threshold);
            Cv.Dilate(_bin, _morp, _convKernel, iteration);
            Cv.Erode(_morp, _morp, _convKernel, iteration);

            return _morp;
        }

        public IplImage EDImage(IplImage src, int threshold, int iteration)
        {
            _morp = new IplImage(src.Size, BitDepth.U8, 1);
            _bin = Binary(src, threshold);
            Cv.Erode(_bin, _morp, _convKernel, iteration);
            Cv.Dilate(_morp, _morp, _convKernel, iteration);

            return _morp;
        }

        public IplImage MorphologyImage(IplImage src, MorphologyOperation option, int iteration)
        {
            if (_convKernel == null)
                return null;

            _morp = new IplImage(src.Size, BitDepth.U8, 1);
            _bin = Binary(src, 50);

            Cv.MorphologyEx(_bin, _morp, _bin, _convKernel, option, iteration);
            return _morp;
        }

        public virtual void Dispose()
        {
            Release(
                _gray,
                _inversion,
                _bin,
                _blur,
                _zoomin,
                _zoomout,
                _resize,
                _slice,
                _symm,
                _rotate,
                _affine,
                _perspective,
                _draw,
                _hsv,
                _hsvRed,
                _morp,
                _canny
                );
        }
        
        public IplConvKernel ConvKernel
        {
            get { return this._convKernel; }
            set { this._convKernel = value; }
        }
    }
}
