using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Emgu.CV;
using Emgu.CV.Structure;
using bbiwarg.Output;
using bbiwarg.Recognition.FingerRecognition;
using bbiwarg.Recognition.HandRecognition;
using bbiwarg.Utility;

namespace bbiwarg.Images
{
    public class DepthImage
    {
        public Image<Gray, byte> Image { get; private set; }
        public Image<Gray, byte> BackgroundMask { get; private set; }
        public Int16 MinDepth { get; private set; }
        public Int16 MaxDepth { get; private set; }

        public DepthImage(IntPtr rawDepthData, int width, int height, ConfidenceImage confidenceImage)
        {
            Image<Gray, Int16> rawDepthImage = new Image<Gray, Int16>(width, height, width * 2, rawDepthData);

            // filter with confidenceImage mask
            rawDepthImage = rawDepthImage.Or((1 - confidenceImage.Mask).Convert<Gray, Int16>().Mul(Int16.MaxValue));

            // smooth with median filter
            rawDepthImage = rawDepthImage.SmoothMedian(Parameters.DepthImageMedianSize);
            
            // threshold min&maxDepth
            MinDepth = findMinDepth(rawDepthImage);
            MaxDepth = (Int16)(MinDepth + Parameters.DepthImageDepthRange);

            // threshold (dst = (src > (MaxDepth - MinDepth)) ? MaxDepth - MinDepth : src)
            Image = (rawDepthImage - MinDepth).ThresholdTrunc(new Gray(MaxDepth - MinDepth)).Convert<Gray, byte>();
            
            // smooth with median filter
            Image = Image.SmoothMedian(Parameters.DepthImageMedianSize);
        }

        public Int16 getDepthAt(Point point)
        {
            return getDepthAt(point.X, point.Y);
        }

        public Int16 getDepthAt(int x, int y)
        {
            return (Int16)(MinDepth + Image.Data[y, x, 0]);
        }

        public void setDepthAt(Point point, Int16 depth)
        {
            setDepthAt(point.X, point.Y, depth);
        }

        public void setDepthAt(int x, int y, Int16 depth)
        {
            Image.Data[y, x, 0] = (byte)(depth - MinDepth);
        }

        private Int16 findMinDepth(Image<Gray, Int16> image)
        {
            // min and max values
            double[] min, max;
            // min and max locations
            Point[] minLoc, maxLoc;

            image.MinMax(out min, out max, out minLoc, out maxLoc);

            return (Int16)min[0];
        }
    }
}
