using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Runtime.Serialization;

using CategoryTheory;
using DiagramUI;
using GeneralLinearMethod;
using Regression;
using DataPerformer;
using BitmapConsumer;


namespace ImageNavigation
{
    /// <summary>
    /// Selection associated with bitmap
    /// </summary>
    [Serializable()]
    public class BitmapSelection : ICategoryObject, ISerializable, IMeasurements, IStructuredSelection,
        IUpdatableSelection, IBitmapConsumer, IStructuredSelectionCollection
    {


        #region Fields

        /// <summary>
        /// Shifts
        /// </summary>
        private static readonly int[,] sh = new int[,]{{1, 1}, {0, 1}, {-1, 1}, {-1, 0}, 
											{-1, -1}, {0, -1}, {1, -1}, {1, 0}};

        /// <summary>
        /// Border shifts
        /// </summary>
        private static readonly int[,] bsh = new int[,] { { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 0 } };

        /// <summary>
        /// Bitmap
        /// </summary>
        private Bitmap bitmap;


        /// <summary>
        /// Camera
        /// </summary>
        private IBitmapProvider bmpProvider;

        /// <summary>
        /// Associated object
        /// </summary>
        private object obj;

        /// <summary>
        /// Color of fon
        /// </summary>
        private Color fonColor;

        /// <summary>
        /// Left border
        /// </summary>
        private Point left;

        /// <summary>
        /// Right border
        /// </summary>
        private Point right;

        /// <summary>
        /// Edge of bitmap
        /// </summary>
        private List<Point>[] bitmapEdge;

        /// <summary>
        /// Edge of camera
        /// </summary>
        private List<Point>[] cameraEdge;

        /// <summary>
        /// Measurements data
        /// </summary>
        private object[] data;

        /// <summary>
        /// Fixed data
        /// </summary>
        private object[] fixedData;

        /// <summary>
        /// Measure
        /// </summary>
        private IMeasure m;

        /// <summary>
        /// Edge delta
        /// </summary>
        private int delta = 10;

        /// <summary>
        /// Bitmap provider
        /// </summary>
        private IBitmapProvider provider;

        private static readonly Double a = 0;

        private static readonly double? b = (double?)0;

        #endregion

        #region Constructors

        public BitmapSelection()
        {

            m = new Measure(new object[] { a, a }, new MeasureParameter(getData), "Selection");
        }

        public BitmapSelection(SerializationInfo info, StreamingContext context)
        {
            Bitmap = (Bitmap)info.GetValue("Bitmap", typeof(Bitmap));
            m = new Measure(new object[] { a, a }, new MeasureParameter(getData), "Selection");
        }


        #endregion

        #region ISerializable Members

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Bitmap", bitmap);
        }

        #endregion

        #region ICategoryObject Members

        public ICategory Category
        {
            get
            {
                // TODO:  Add BitmapSelection.Category getter implementation
                return null;
            }
        }

        public ICategoryArrow Id
        {
            get
            {
                // TODO:  Add BitmapSelection.Id getter implementation
                return null;
            }
        }

        #endregion

        #region IAssociatedObject Members

        public object Object
        {
            get
            {
                return obj;
            }
            set
            {
                obj = value;
            }
        }

        #endregion

        #region IMeasurements Members

        public int Count
        {
            get
            {
                return 1;
            }
        }

        public IMeasure this[int n]
        {
            get
            {
                return m;
            }
        }

        public void UpdateMeasurements()
        {
            Bitmap bmp = bmpProvider.Bitmap;
            cameraEdge = createFigureEdge(bmp);
            int n = bitmapEdge[0].Count / delta + bitmapEdge[1].Count / delta;
            if (data == null)
            {
                data = new object[n];
            }
            if (data.Length != n)
            {
                data = new object[n];
            }
            int i = 0;
            for (int j = 0; j < 2; j++)
            {
                List<Point> be = bitmapEdge[j];
                List<Point> ce = cameraEdge[j];
                Point l = ce[0];
                Point r = ce[ce.Count - 1];
                for (int k = 0; k < be.Count; k += delta)
                {
                    Point pb = be[k];
                    double m = 10000000000;

                    foreach (Point pc in ce)
                    {
                        double x = (double)(pb.X - pc.X);
                        double y = (double)(pb.Y - pc.Y);
                        double a = x * x + y * y;
                        if (m > a)
                        {
                            m = a;
                        }
                    }
                    data[i] = Math.Sqrt(m);
                    ++i;
                    if (i == n)
                    {
                        return;
                    }
                }
            }
        }


        public string SourceName
        {
            get
            {
                IObjectLabel l = obj as IObjectLabel;
                return l.Name;
            }
        }

        public bool IsUpdated
        {
            get
            {
                return true;
            }
            set
            {
            }
        }

        #endregion

        #region IStructuredSelection Members

        public int DataDimension
        {
            get
            {
                try
                {
                    return fixedData.Length;
                }
                catch (Exception)
                {
                }
                return 0;
            }
        }

        double? DataPerformer.IStructuredSelection.this[int n]
        {
            get
            {
                return (double?)fixedData[n];
            }
        }

        public double GetWeight(int n)
        {
            return 1;
        }

        public double GetApriorWeight(int n)
        {
            return 0;
        }

        public int GetTolerance(int n)
        {
            return 0;
        }

        public void SetTolerance(int n, int tolerance)
        {
        }

        public bool HasFixedAmount
        {
            get
            {
                return false;
            }
        }

        public string Name
        {
            get
            {
                return "Bitmap";
            }
        }

        #endregion

        #region IUpdatableSelection Members

        public void UpdateSelection()
        {
            UpdateMeasurements();
            if (fixedData == null)
            {
                fixedData = new object[data.Length];
            }
            if (fixedData.Length != data.Length)
            {
                fixedData = new object[data.Length];
            }
            for (int i = 0; i < data.Length; i++)
            {
                fixedData[i] = (double?)b;//data[i];
            }
        }

        #endregion

        #region Specific Members

        public Bitmap Bitmap
        {
            set
            {
                bitmap = value;
                fonColor = bitmap.GetPixel(0, 0);
                bitmapEdge = createFigureEdge(bitmap);
            }
        }

        public Bitmap EdgeBitmap
        {
            get
            {
                if (bitmap == null)
                {
                    return null;
                }
                Bitmap bmp = new Bitmap(bitmap.Width, bitmap.Height);
                Graphics g = Graphics.FromImage(bmp);
                Brush brush = new SolidBrush(Color.White);
                g.FillRectangle(brush, 0, 0, bmp.Width, bmp.Height);
                Color bC = Color.Red;
                foreach (List<Point> l in bitmapEdge)
                {
                    foreach (Point p in l)
                    {
                        bmp.SetPixel(p.X, p.Y, bC);
                    }
                }
                if (bmpProvider != null)
                {
                    try
                    {
                        cameraEdge = createFigureEdge(bmpProvider.Bitmap);
                        Color cC = Color.Blue;
                        foreach (List<Point> l in cameraEdge)
                        {
                            foreach (Point p in l)
                            {
                                bmp.SetPixel(p.X, p.Y, cC);
                            }
                        }
                    }
                    catch (Exception)
                    {
                    }
                }
                return bmp;
            }
        }

        public IBitmapProvider BitmapProvider
        {
            set
            {
                if (value == null)
                {
                    bmpProvider = null;
                    return;
                }
                if (bmpProvider != null)
                {
                    throw new Exception("Camera already exists");
                }
                bmpProvider = value;
            }
        }


        private List<Point>[] createFigureEdge(Bitmap bmp)
        {
            for (int i = 0; i < bmp.Width; i++)
            {
                for (int j = 0; j < bmp.Height; j++)
                {
                    if (!bmp.GetPixel(i, j).Equals(fonColor))
                    {
                        left = new Point(i - 1, j);
                        goto endLeft;
                    }
                }
            }
        endLeft:
            for (int i = bmp.Width - 1; i >= 0; i--)
            {
                for (int j = 0; j < bmp.Height; j++)
                {
                    if (!bmp.GetPixel(i, j).Equals(fonColor))
                    {
                        right = new Point(i + 1, j);
                        goto endRight;
                    }
                }
            }
        endRight:
            List<Point> l = new List<Point>();
            l.Add(left);
            int n = 0;
            Point p1 = addPoint(left, bmp, l, ref n);
            n = 0;
            Point p2 = addPoint(left, bmp, l, ref n);
            Point pt = (p1.Y < p2.Y) ? p1 : p2;
            Point pb = (p1.Y < p2.Y) ? p2 : p1;
            List<Point> lt = new List<Point>();
            lt.Add(left);
            lt.Add(pt);
            createFigureEdge(bmp, pt, lt);
            List<Point> lb = new List<Point>();
            lb.Add(left);
            lb.Add(pb);
            createFigureEdge(bmp, pb, lb);
            return new List<Point>[] { lt, lb };
        }

        private object getData()
        {
            return data;
        }

        private void createFigureEdge(Bitmap bmp, Point p, List<Point> list)
        {
            int n = 0;
            Point point = new Point(p.X, p.Y);
            do
            {
                point = addPoint(point, bmp, list, ref n);
            }
            while (point.X > 0);
        }

        private Point addPoint(Point point, Bitmap bmp, List<Point> list, ref int n)
        {
            Point p = new Point(point.X, point.Y);
            for (int i = 0; i < sh.GetLength(0); i++)
            {
                p.X = point.X + sh[i, 0];
                p.Y = point.Y + sh[i, 1];
                Color c = bmp.GetPixel(p.X, p.Y);
                if ((!c.Equals(fonColor)) | (!layOnBoundary(p, bmp)) | list.Contains(p))
                {
                    continue;
                }
                if (p.Equals(right))
                {
                    return new Point(-1, 0);
                }
                list.Add(p);
                n = list.Count - 1;
                return p;
            }
            Point po = (Point)list[n - 1];
            n = n - 1;
            return addPoint(po, bmp, list, ref n);
        }

        private bool layOnBoundary(Point p, Bitmap bmp)
        {
            for (int i = 0; i < bsh.GetLength(0); i++)
            {
                Color c = bmp.GetPixel(p.X + bsh[i, 0], p.Y + bsh[i, 1]);
                if (!c.Equals(fonColor))
                {
                    return true;
                }
            }
            return false;
        }

        #endregion

        #region IBitmapConsumer Members

        public void Process()
        {
            Bitmap = provider.Bitmap.Clone() as Bitmap;
        }

        public IBitmapProvider Provider
        {
            get
            {
                return provider;
            }
            set
            {
                if (provider == null)
                {
                    provider = value;
                    if (value != null)
                    {
                        Bitmap = provider.Bitmap.Clone() as Bitmap;
                    }
                    return;
                }
                if (value != null)
                {
                    throw new Exception("Provider already exists");
                }
            }
        }

        #endregion

        #region IStructuredSelectionCollection Members

        int IStructuredSelectionCollection.Count
        {
            get
            {
                return 1;
            }
        }

        IStructuredSelection IStructuredSelectionCollection.this[int i]
        {
            get
            {
                return this;
            }
        }

        #endregion
    }
}
