using System;
using System.Drawing;

namespace OpenTK_tutorial
{
    internal class FrameProcessor
    {
        public float[] depthMap;
        public Color[] colorMap;
        int width;
        int height;

        public FrameProcessor(int width, int height, float[] depthMap, Color[] colorMap)
        {
            this.width = width;
            this.height = height;
            this.depthMap = depthMap;
            this.colorMap = colorMap;
        }

        public void FilterOcclusion(Color bgColor, float minD)
        {
            Color[] colorMap2 = new Color[colorMap.Length];
            float[] depthMap2 = new float[depthMap.Length];

            // TODO process colorMap and depthMap
            // if color pixel is further from neighbors than minD then replace with neighbor with smallest depth
            for (int i = 1; i < width - 1; i++)
            {
                for (int j = 1; j < height - 1; j++)
                {
                    Color c = colorMap[i + j * width];
                    colorMap2[i + j * width] = c;
                    depthMap2[i + j * width] = depthMap[i + j * width];

                    if (c != bgColor && !IsLegit(i, j, minD))
                    {
                        float[] v = new float[9];
                        v[0] = depthMap[(i - 1) + (j - 1) * width];
                        v[1] = depthMap[i + (j - 1) * width];
                        v[2] = depthMap[(i + 1) + (j - 1) * width];

                        v[3] = depthMap[(i - 1) + j * width];
                        v[4] = depthMap[i + j * width];
                        v[5] = depthMap[(i + 1) + j * width];

                        v[6] = depthMap[(i - 1) + (j + 1) * width];
                        v[7] = depthMap[i + (j + 1) * width];
                        v[8] = depthMap[(i + 1) + (j + 1) * width];

                        float min = 1;
                        int minIndex = -1;
                        for (int k = 0; k < 9; k++)
                        {
                            if (v[k] < min)
                            {
                                min = v[k];
                                minIndex = k;
                            }
                        }

                        int jI = minIndex / 3 - 1;
                        int iI = minIndex % 3 - 1;

                        Color c2 = colorMap[(i + iI) + (j + jI) * width];

                        colorMap2[i + j * width] = c2;
                        depthMap2[i + j * width] = min;
                    }
                }
            }
            colorMap = colorMap2;
            depthMap = depthMap2;
        }

        private bool IsLegit(int i, int j, float minD)
        {
            int[] m0 = new int[9] { 1, 0, 0, 1, 0, 0, 1, 0, 0 };
            int[] m1 = new int[9] { 0, 0, 0, 0, 0, 0, 1, 1, 1 };
            int[] m2 = new int[9] { 0, 0, 1, 0, 0, 1, 0, 0, 1 };
            int[] m3 = new int[9] { 1, 1, 1, 0, 0, 0, 0, 0, 0 };

            int[] m4 = new int[9] { 0, 0, 0, 1, 0, 0, 1, 1, 0 };
            int[] m5 = new int[9] { 0, 0, 0, 0, 0, 1, 0, 1, 1 };
            int[] m6 = new int[9] { 0, 1, 1, 0, 0, 1, 0, 0, 0 };
            int[] m7 = new int[9] { 1, 1, 0, 1, 0, 0, 0, 0, 0 };

            float[] v = new float[9];
            v[0] = depthMap[(i - 1) + (j - 1) * width];
            v[1] = depthMap[(i) + (j - 1) * width];
            v[2] = depthMap[(i + 1) + (j - 1) * width];

            v[3] = depthMap[(i - 1) + j * width];
            v[4] = depthMap[i + j * width];
            v[5] = depthMap[(i + 1) + j * width];

            v[6] = depthMap[(i - 1) + (j + 1) * width];
            v[7] = depthMap[i + (j + 1) * width];
            v[8] = depthMap[(i + 1) + (j + 1) * width];

            return (TestMask(m0, v, minD) || TestMask(m1, v, minD) ||
                    TestMask(m2, v, minD) || TestMask(m3, v, minD) ||
                    TestMask(m4, v, minD) || TestMask(m5, v, minD) ||
                    TestMask(m6, v, minD) || TestMask(m7, v, minD));
        }

        bool TestMask(int[] mask, float[] arr, float minD)
        {
            for (int i = 0; i < mask.Length; i++)
            {
                if (mask[i] == 0 && arr[i] < arr[4] - minD)
                    return false;
            }

            return true;
        }

        public void Filter(Color bgColor)
        {
            Color[] colorMap2 = new Color[colorMap.Length];
            float[] depthMap2 = new float[depthMap.Length];

            // TODO process colorMap and depthMap
            // if background pixel is not an edge vertex, then replace by neighbor with smallest depth
            for (int i = 1; i < width - 1; i++)
            {
                for (int j = 1; j < height - 1; j++)
                {
                    Color c = colorMap[i + j * width];
                    colorMap2[i + j * width] = c;
                    depthMap2[i + j * width] = depthMap[i + j * width];

                    if (c == bgColor && !IsSilouhette(i, j, bgColor))
                    {
                        float[] v = new float[9];
                        v[0] = depthMap[(i-1) + (j-1) * width];
                        v[1] = depthMap[i + (j-1) * width];
                        v[2] = depthMap[(i+1) + (j -1) * width];

                        v[3] = depthMap[(i-1) + j * width];
                        v[4] = depthMap[i + j * width];
                        v[5] = depthMap[(i+1) + j * width];
                     
                        v[6] = depthMap[(i-1) + (j+1) * width];
                        v[7] = depthMap[i + (j+1) * width];
                        v[8] = depthMap[(i+1) + (j +1) * width];

                        float min = 1;
                        int minIndex = -1;
                        for (int k = 0; k < 9; k++)
                        {
                            if (v[k] < min)
                            {
                                min = v[k];
                                minIndex = k;
                            }
                        }

                        int jI = minIndex / 3 - 1;
                        int iI = minIndex % 3 - 1;

                        Color c2 = colorMap[(i + iI) + (j + jI) * width];

                        colorMap2[i + j * width] = c2;
                        depthMap2[i + j * width] = min;
                    }

                }
            }

            colorMap = colorMap2;
            depthMap = depthMap2;
        }

        private bool IsSilouhette(int i, int j, Color bgColor)
        {
            int[] m0 = new int[9] {1, 0, 0, 1, 0, 0, 1, 0, 0};
            int[] m1 = new int[9] {0, 0, 0, 0, 0, 0, 1, 1, 1};
            int[] m2 = new int[9] {0, 0, 1, 0, 0, 1, 0, 0, 1};
            int[] m3 = new int[9] {1, 1, 1, 0, 0, 0, 0, 0, 0 };

            int[] m4 = new int[9] { 0, 0, 0, 1, 0, 0, 1, 1, 0 };
            int[] m5 = new int[9] { 0, 0, 0, 0, 0, 1, 0, 1, 1 };
            int[] m6 = new int[9] { 0, 1, 1, 0, 0, 1, 0, 0, 0 };
            int[] m7 = new int[9] { 1, 1, 0, 1, 0, 0, 0, 0, 0 };

            int[] v = new int[9];
            v[0] = colorMap[(i-1) + (j-1) * width] == bgColor ? 0 : 1;
            v[1] = colorMap[i + (j-1) * width] == bgColor ? 0 : 1;
            v[2] = colorMap[(i+1) + (j-1) * width] == bgColor ? 0 : 1;

            v[3] = colorMap[(i-1) + j * width] == bgColor ? 0 : 1;
            v[4] = colorMap[i + j * width] == bgColor ? 0 : 1;
            v[5] = colorMap[(i+1) + j * width] == bgColor ? 0 : 1;

            v[6] = colorMap[(i-1) + (j+1) * width] == bgColor ? 0 : 1;
            v[7] = colorMap[i + (j+1) * width] == bgColor ? 0 : 1;
            v[8] = colorMap[(i+1) + (j+1) * width] == bgColor ? 0 : 1;

            return (TestMask(m0, v) || TestMask(m1, v) ||
                    TestMask(m2, v) || TestMask(m3, v) ||
                    TestMask(m4, v) || TestMask(m5, v) ||
                    TestMask(m6, v) || TestMask(m7, v));
        }

        bool TestMask(int[] mask, int[] arr)
        {
            for (int i = 0; i < mask.Length; i++)
            {
                if (mask[i] == 0 && mask[i] != arr[i])
                    return false;
            }

            return true;
        }

        public void SaveToFilie(string path)
        {
            // Save to file
            Bitmap bmp = new Bitmap(width, height);
            for (int i = 0; i < width; i++)
                for (int j = 0; j < height; j++)
                {
                    bmp.SetPixel(i, j, colorMap[i + j * width]);
                }

            bmp.RotateFlip(RotateFlipType.RotateNoneFlipY);
            bmp.Save(path + ".png", System.Drawing.Imaging.ImageFormat.Png); Bitmap bmp2 = new Bitmap(width, height);

            for (int i = 0; i < width; i++)
                for (int j = 0; j < height; j++)
                {
                    bmp2.SetPixel(i, j, Color.FromArgb((int)(255 * depthMap[i + j * width]), (int)(255 * depthMap[i + j * width]), (int)(255 * depthMap[i + j * width])));
                }
            bmp2.RotateFlip(RotateFlipType.RotateNoneFlipY);
            bmp2.Save(path + "_depth.png", System.Drawing.Imaging.ImageFormat.Png);
        }
    }
}
