using FastReport.Controls;
using FastReport.Utils;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Drawing;
using System;
using System.Threading.Tasks;
using System.Threading;
#if !MONO
using FastReport.DevComponents.DotNetBar;
#endif

namespace FastReport.Design.ToolWindows
{
    /// <summary>
    /// Represents the "Validator" window.
    /// </summary>
    public class ValidatorWindow : ToolWindowBase
    {
        #region Fields
        private DataGridView gridView;
        private Splitter splitter;
        private TextBox tbUpdating;
        private CancellationTokenSource updateErrCancelToken;
        System.Windows.Forms.Timer timer; 
        private bool skipSelectChanged;
#endregion

        #region Private Methods
        private Image GetImage(ValidationError.ErrorLevel level)
        {
            switch (level)
            {
                case ValidationError.ErrorLevel.Warning:
                    return Res.GetImage(211, 96);
                default:
                    return Res.GetImage(80, 96);
            }
        }

        private void Timer_Tick(object sender, EventArgs e)
        {
            if (tbUpdating.Text.Contains("..."))
                tbUpdating.Text = tbUpdating.Text.Replace("...", "");
            else
                tbUpdating.Text += ".";
        }


#if !MONO
        private void ParentControl_VisibleChanged(object sender, EventArgs e)
        {
            if (ParentControl.Visible)
                UpdateErrors(!PageDesigners.Page.ReportWorkspace.EnableBacklightIntersectingObjects);
        }
#else
        private void ValidatorWindow_VisibleChanged(object sender, EventArgs e)
        {
            if (this.Visible)
                UpdateErrors(!PageDesigners.Page.ReportWorkspace.EnableBacklightIntersectingObjects);
        }
#endif

        private void GridView_SelectionChanged(object sender, EventArgs e)
        {
            if (skipSelectChanged || gridView.SelectedRows.Count == 0)
                return;

            ValidationError validationError = (ValidationError)gridView.SelectedRows[0].Tag;

            if (validationError.Object.Page != Designer.ActiveReportTab.ActivePage)
                Designer.ActiveReportTab.ActivePage = validationError.Object.Page;
            Designer.SelectedObjects.Clear();

            Designer.SelectedObjects.Add(validationError.Object);
            Designer.SelectionChanged(this);
        }

        private void UpdateUI(List<ValidationError> errors, CancellationTokenSource token)
        {
            if (errors == null)
                return;

            skipSelectChanged = true;
            gridView.Rows.Clear();

            if (!token.IsCancellationRequested)
            {
                timer.Enabled = false;
                tbUpdating.Hide();
                gridView.Show();
            }


            foreach (ValidationError error in errors)
            {
                gridView.Rows.Add(GetImage(error.Level), error.Name, error.Message);
                gridView.Rows[gridView.RowCount - 1].Tag = error;
            }
            skipSelectChanged = false;
        }

        #endregion

        #region Public Methods
        /// <summary>
        /// Initializes a new instance of the <see cref="ValidatorWindow"/> class with default settings.
        /// </summary>
        /// <param name="designer">The report designer.</param>
        public ValidatorWindow(Designer designer) : base(designer)
        {
#if !MONO
            ParentControl.VisibleChanged += ParentControl_VisibleChanged;
#else
            this.VisibleChanged += ValidatorWindow_VisibleChanged; 
#endif
            updateErrCancelToken = new CancellationTokenSource();
            Name = "ValidatorWindow";
            skipSelectChanged = false;

            timer = new System.Windows.Forms.Timer();
            timer.Interval = 1500;
            timer.Tick += Timer_Tick;

            tbUpdating = new TextBox();
            tbUpdating.Dock = DockStyle.Top;
            tbUpdating.TextAlign = HorizontalAlignment.Center;
            tbUpdating.Visible = false;
            tbUpdating.Text = "Updating";
            tbUpdating.ReadOnly = true;
            tbUpdating.BackColor = Color.White;

            gridView = new DataGridView();
            gridView.Dock = DockStyle.Fill;
            gridView.ColumnHeadersHeight = 25;
            gridView.BackgroundColor = Color.White;
            gridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            gridView.AllowUserToAddRows = false;
            gridView.AllowUserToDeleteRows = false;
            gridView.RowHeadersVisible = false;
            gridView.SelectionChanged += GridView_SelectionChanged;
            gridView.CellClick += GridView_SelectionChanged;

            gridView.Columns.Add(new DataGridViewColumn(new DataGridViewImageCell()));
            gridView.Columns[0].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
            gridView.Columns[0].MinimumWidth = 24;
            gridView.Columns[0].ReadOnly = true;
            gridView.Columns[0].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;

            gridView.Columns.Add(new DataGridViewColumn(new DataGridViewTextBoxCell()));
            gridView.Columns[1].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            gridView.Columns[1].ReadOnly = true;

            gridView.Columns.Add(new DataGridViewColumn(new DataGridViewTextBoxCell()));
            gridView.Columns[2].AutoSizeMode = DataGridViewAutoSizeColumnMode.Fill;
            gridView.Columns[2].ReadOnly = true;
#if !MONO

            splitter = new Splitter();
            splitter.Dock = DockStyle.Bottom;
            splitter.Visible = true;

            ParentControl.Controls.AddRange(new Control[] { gridView, splitter, tbUpdating });
#else
            splitter = new Splitter();
            splitter.Dock = DockStyle.Bottom;
            splitter.Visible = true;

            Controls.AddRange(new Control[] { gridView, splitter, tbUpdating });
#endif
            timer.Enabled = true;

            tbUpdating.Show();
            gridView.Hide();

            UpdateDpiDependencies();
            Localize();
        }

        /// <summary>
        /// Updating list of report objects errors in other thread.
        /// </summary>
        /// <param name="checkIntersectObj"></param>
        public void UpdateErrors(bool checkIntersectObj)
        {
            tbUpdating.Show();
            timer.Enabled = true;

            updateErrCancelToken.Cancel();
            updateErrCancelToken = new CancellationTokenSource();
            Action<object> action = (object token) =>
            {
                CancellationTokenSource tokenSource = token as CancellationTokenSource;
                List<ValidationError> errors = Validator.ValidateReport(Designer.ActiveReport, checkIntersectObj, tokenSource.Token);

                if(!tokenSource.IsCancellationRequested)
                    gridView.Invoke((Action)(() => UpdateUI(errors, tokenSource)));
            };

            Task.Factory.StartNew(action, updateErrCancelToken, updateErrCancelToken.Token);
        }

        /// <summary>
        /// Stoping thread with UpdateErrors task.
        /// </summary>
        public void CancelUpdateErrors()
        {
            updateErrCancelToken.Cancel();
        }

        /// <summary>
        /// Hiding table with errors and showing loading progress.
        /// </summary>
        public void ShowLoadingProgress()
        {
            tbUpdating.Show();
            gridView.Hide();
            timer.Enabled = true;
        }

        /// <inheritdoc/>
        public override void UpdateContent()
        {
            base.UpdateContent();

#if !MONO
            if (ParentControl.Visible)
#else
            if (Visible)
#endif
                UpdateErrors(!PageDesigners.Page.ReportWorkspace.EnableBacklightIntersectingObjects);
        }

        /// <inheritdoc/>
        public override void UpdateUIStyle()
        {
            base.UpdateUIStyle();
#if !MONO
            splitter.BackColor = UIStyleUtils.GetControlColor(Designer.UIStyle);
#else
            splitter.BackColor = UIStyleUtils.GetColorTable(Designer.UIStyle).ControlBackColor;
#endif
        }

        /// <inheritdoc/>
        public override void Localize()
        {
            base.Localize();
            MyRes res = new MyRes("Designer,ToolWindow,Validator");

            Text = res.Get("");
            gridView.Columns[1].HeaderText = res.Get("Name");
            gridView.Columns[2].HeaderText = res.Get("Message");
            tbUpdating.Text = res.Get("Updating");

            UpdateErrors(!Design.PageDesigners.Page.ReportWorkspace.EnableBacklightIntersectingObjects);
        }

        /// <inheritdoc/>
        public override void UpdateDpiDependencies()
        {
            base.UpdateDpiDependencies();
            Image = Designer.GetImage(70);
        }

        /// <inheritdoc/>
        public override void SaveState()
        {
            XmlItem xi = Config.Root.FindItem("Designer").FindItem(Name);
            xi.SetProp("GridViewColumn1Width", gridView.Columns[1].Width.ToString());
            xi.SetProp("GridViewColumn2Width", gridView.Columns[2].Width.ToString());
            xi.SetProp("GridViewHeadersHeight", gridView.ColumnHeadersHeight.ToString());
        }

        /// <inheritdoc/>
        public override void RestoreState()
        {
            XmlItem xi = Config.Root.FindItem("Designer").FindItem(Name);

            string s = xi.GetProp("GridViewColumn1Width");
            if (s != "")
                gridView.Columns[1].Width = int.Parse(s);

            s = xi.GetProp("GridViewColumn2Width");
            if (s != "")
                gridView.Columns[2].Width = int.Parse(s);

            s = xi.GetProp("GridViewHeadersHeight");
            if (s != "")
                gridView.ColumnHeadersHeight = int.Parse(s);
        }
#endregion
    }
}
