using System;
using System.Linq;
using System.Windows.Forms;
using DbModel.DbGui;
using DbModel.DB.Model;
using log4net;
using OpenCvSharp;
using OpenCvSharp.CPlusPlus;
using YSoft.Rqa.Points;
using Button = DbModel.DB.Model.Button;
using Screen = DbModel.DB.Model.Screen;
using Size = OpenCvSharp.CPlusPlus.Size;

namespace YSoft.Rqamfp.Camera
{
    public partial class DbFiller : Form
    {
        public Mat Image;
        private Point2f[] _element;
        private MFP _mfp;
        private Mat _markedImage;
        private Button _button;
        private Screen _screen;
        private Window _win = new Window("Db Filler");
        private Scalar _rectangleColor = Scalar.Red;
        private static readonly ILog Logger = LogManager.GetLogger(typeof(DbFiller));

        public DbFiller() {
            InitializeComponent();
            new RqaContext().Database.Initialize(true);
        }

        private void OnLoad(object sender, EventArgs e) {
            _element = new Point2f[2];
            _markedImage = new Mat(new Size(Image.Width, Image.Height), MatType.CV_8UC3);
            Image.CopyTo(_markedImage);
            _win.OnMouseCallback += OnMarking;
            UpdatePic();
        }

        private void UpdatePic() {
            _win.ShowImage(_markedImage);
        }

        private void OnMarking(MouseEvent eventType, int x, int y, MouseEvent flags) {
            if (eventType == MouseEvent.LButtonDown) {
                if (_mfp == null) {
                    MessageBox.Show("First choose MFD");
                    return;
                }
                _markedImage.Dispose();
                _markedImage = new Mat(new Size(Image.Width, Image.Height), MatType.CV_8UC3);
                Image.CopyTo(_markedImage);
                _element[0].X = x;
                _element[0].Y = y;
            }
            else if (eventType == MouseEvent.LButtonUp) {
                if (Math.Abs(_element[0].X - x) < 10)
                    return;
                _element[1].X = x;
                _element[1].Y = y;
                _markedImage.Rectangle(_element[0], _element[1], _rectangleColor, 2);
                _element[0] = ToMils(_element[0]);
                _element[1] = ToMils(_element[1]);
                UpdatePic();
            }
        }

        private async void AddElementOnClick(object sender, EventArgs e) {
            // ReSharper disable CompareOfFloatsByEqualityOperator
            if ((_button == null && _screen == null) || (_element[0].X == 0 && _element[1].X == 0) || _mfp == null) {
            // ReSharper restore CompareOfFloatsByEqualityOperator
                MessageBox.Show("First mark the element or choose button/screen");
                return;
            }
            var context = new RqaContext();
            _mfp = context.MFPs.Find(_mfp.Id);
            var size = new DbModel.DB.Model.Size(_element[1].X - _element[0].X, _element[0].Y - _element[1].Y);
            var position = new Point3(_element[0].X, _element[0].Y - size.Height);
            if (elementDecisionBP.Checked) {
                _button = context.Buttons.Find(_button.Id);
                var bp = new ButtonPlacement {
                    Button = _button,
                    Position = position,
                    Printer = _mfp,
                    TimesTapped = 0,
                    Size = size
                };
                if (context.ButtonPlacements.Any(x => (x.Button.Id == _button.Id && x.Printer.Id == _mfp.Id))) {
                    MessageBox.Show("Button placement with this tag already exists for this printer");
                    return;
                }
                context.ButtonPlacements.Add(bp);
            }
            else if (elementDecisionFR.Checked) {
                _screen = await context.Screens.FindAsync(_screen.Id);
                var region = new FeatureRegion {
                    DateAdded = DateTime.Now,
                    Device = _mfp,
                    Text = textChooseText.Text,
                    Tag = tagChooseText.Text,
                    Position = position,
                    Screen = _screen,
                    Size = size,
                    IsWhiteText = whiteText.Checked
                };
                if (context.FeatureRegions.Any(x => (x.Tag == region.Tag && x.Screen.Id == _screen.Id && x.Device.Id == _mfp.Id))) {
                    MessageBox.Show("Feature region with this tag already exists for this screen");
                    return;
                }
                context.FeatureRegions.Add(region);
            }
            try {
                context.SaveChanges();
                MessageBox.Show("Element successfully added");
                Logger.Info("Element successfully added");
                tagChooseText.Text = string.Empty;
                textChooseText.Text = string.Empty;
            } catch (Exception ex) {
                Logger.Warn("Element was not added successfuly", ex);
            }
        }

        private void ChooseButtonOnClick(object sender, EventArgs e) {
            var db = new DbViewer(typeof(Button), true);
            try {
                db.ShowDialog();
                UpdatePic();
                _button = (Button) db.Result;
                buttonChooseText.Text = _button.ToString();
            } catch (Exception) {
                Logger.Debug("DB Viewer was closed");
            }
        }

        private void CloseOnClick(object sender, EventArgs e) {
            Cv2.DestroyAllWindows();
            Close();
        }

        private void MfpChoosementOnClick(object sender, EventArgs e) {
            var db = new DbViewer(typeof(MFP), true);
            try {
                db.ShowDialog();
                UpdatePic();
                _mfp = (MFP) db.Result;
                mfpChooseText.Text = _mfp.ToString();
            } catch (Exception) {
                Logger.Debug("DB Viewer was closed");
            }
        }

        private Point2f ToMils(Point2f element) {
            var screenPixels = new DbModel.DB.Model.Size(Image.Width, Image.Height);
            var screenMilis = new DbModel.DB.Model.Size(_mfp.Model.Screen.Width, _mfp.Model.Screen.Height);
            element.Y = (float) Math.Round(screenMilis.Height - element.Y*(screenMilis.Height/screenPixels.Height));
            element.X = (float) Math.Round(element.X*(screenMilis.Width/screenPixels.Width));
            return element;
        }

        private void ReshowOnClick(object sender, EventArgs e) => UpdatePic();

        private void ElementTypeOnChange(object sender, EventArgs e) {
            if (elementDecisionFR.Checked) {
                screenChoose.Enabled = true;
                textChooseText.Enabled = true;
                tagChooseText.Enabled = true;
                buttonChoose.Enabled = false;
                whiteText.Enabled = true;
            }
            else {
                screenChoose.Enabled = false;
                textChooseText.Enabled = false;
                tagChooseText.Enabled = false;
                buttonChoose.Enabled = true;
                whiteText.Enabled = false;
            }
        }

        private void ScreenChoosementOnClick(object sender, EventArgs e) {
            var db = new DbViewer(typeof(Screen), true);
            try {
                db.ShowDialog();
                UpdatePic();
                _screen = (Screen) db.Result;
                screenChooseText.Text = _screen.ToString();
            } catch (Exception) {
                Logger.Debug("DB Viewer was closed");
            }
        }

        private void TextRegionOnChange(object sender, EventArgs e) {
            tagChooseText.Text = textChooseText.Text;
        }

        private void SetColorOnClick(object sender, EventArgs e) {
            var result = colorDialog1.ShowDialog();
            if (result != DialogResult.OK)
                return;
            _rectangleColor.Val0 = colorDialog1.Color.B;
            _rectangleColor.Val1 = colorDialog1.Color.G;
            _rectangleColor.Val2 = colorDialog1.Color.R;
            _rectangleColor.Val3 = 255;
        }
    }
}
