using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
using dataladder.Data;
using dataladder.Matching;
using dataladder.XtraGridHelper;
using LiveSearchDemo.Contracts;
using LiveSearchDemo.Presenters;
using LiveSearchDemo.Entities;
using SampleServiceNamespace;

namespace UsingTheExistingProjectWithGUI
{
    internal partial class MainForm : Form, IView
    {
        #region Fields

        string projectName = "";
        string[] previousSearchValues = new string[0];
        bool doTimerSearch = false;

        private bool AutoSearchAndLoad => autoSaveCheckBox.Checked;

        private readonly IPresenter _presenter;

        #endregion

        #region Constructors

        public MainForm()
        {
            InitializeComponent();

            _presenter = new MainFormPresenter(this);
            Project = new Project();
            Text += $" {SampleServiceNamespace.ProductInfo.VersionApi} ({SampleServiceNamespace.ProductInfo.Version})";
        }

        #endregion

        #region Properties and Fields

        //EngineWrapper.AddWeightToFirstLetterActions addWeightToFirstLetterAction
        //{
        //  get
        //  {
        //    EngineWrapper.AddWeightToFirstLetterActions result;
        //    if (projectRadioButton.Checked)
        //    {
        //      result = EngineWrapper.AddWeightToFirstLetterActions.Project;
        //    }
        //    else if (onRadioButton.Checked)
        //    {
        //      result = EngineWrapper.AddWeightToFirstLetterActions.On;
        //    }
        //    else
        //    {
        //      result = EngineWrapper.AddWeightToFirstLetterActions.Off;
        //    }
        //    return result;
        //  }
        //}

        #endregion

        #region IView implementation

        public String ProjectsPath { get; set; }

        public Project Project { get; set; }

        public Int32 BestMatchesCapacity { get; set; }

        public InMemoryTable LiveSearchResultsTable { get; set; }

        public InMemoryTable DuplicatesTable { get; set; }

        public InMemoryTable InsertedTable { get; set; }

        public InMemoryTable CleansedTable { get; set; }

        public String[] SearchValues { get; set; }

        public String[] InputValues { get; set; }

        public Func<String, Boolean> IsInputDataColumn { get; set; }


        public void BindProjectInfo()
        {
            this.Text += $" - {Project.Name}";
            projectNameTextBox.Text = Project.Path;

            searchButton.Enabled = true;
            submitButton.Enabled = true;

            PopulateInputPanels();

            this.dbDataGrid.DataSource = new VirtualListDynamic(CleansedTable);
            dbDataGrid.Controls[0].Enabled = dbDataGrid.Controls[1].Enabled = true; // workaround for the disabled vertical scroll bar
        }

        public void BindSavedSearchValues(Dictionary<String, String> savedValues)
        {
            foreach (var ctrl in searchCriteriaPanel.Controls)
            {
                TextBox txtBox = ctrl as TextBox;
                if (txtBox != null)
                {
                    txtBox.Text = savedValues[txtBox.Name];
                }
            }
        }

        public void BindSearchResults()
        {
            if (InvokeRequired)
            {
                Invoke(new MethodInvoker(() => {
                    searchResultsDataGridView.DataSource = null;
                    searchResultsDataGridView.DataSource = new dataladder.XtraGridHelper.VirtualListDynamic(LiveSearchResultsTable);

                    foreach (DataGridViewColumn column in searchResultsDataGridView.Columns)
                    {
                        String colName = column.Name;
                        if (!IsInputDataColumn(colName))
                        {
                            searchResultsDataGridView.Columns[colName].DisplayIndex = searchResultsDataGridView.ColumnCount - 1;
                        }
                    }
                }));
            }
            else
            {
                searchResultsDataGridView.DataSource = null;
                searchResultsDataGridView.DataSource = new dataladder.XtraGridHelper.VirtualListDynamic(LiveSearchResultsTable);

                foreach (DataGridViewColumn column in searchResultsDataGridView.Columns)
                {
                    String colName = column.Name;
                    if (!IsInputDataColumn(colName))
                    {
                        searchResultsDataGridView.Columns[colName].DisplayIndex = searchResultsDataGridView.ColumnCount - 1;
                    }
                }
            }
            

            if (AutoSearchAndLoad)
            {
                _presenter.SaveSearchValues();
            }
        }

        public void BindDuplicateCheckResults()
        {
            Color duplicateColor = Color.Tomato;
            Color potentialDuplicateColor = Color.Orange;
            Color uniqueColor = Color.ForestGreen;

            Boolean insertNewRecord = false;

            if (DuplicatesTable == null)
            {
                this.statusLbl.ForeColor = Color.SteelBlue;
                this.statusLbl.Text = $"EMPTY SEARCH VALUES";
            }
            else if (DuplicatesTable.RecordCount > 0)
            {
                double autoMatchThreshold = (double)autoMatchUpDown.Value / 100;
                double manualReviewThreshold = (double)manualReviewUpDown.Value / 100;

                this.duplicatesDataGridView.DataSource = new VirtualListDynamic(DuplicatesTable);

                var colNames = DuplicatesTable.GetColumnNames();
                var scoreColName = colNames.FirstOrDefault(s => s.ToUpper().Equals("SCORE"));

                if (scoreColName == null) return;

                foreach (DataGridViewColumn column in duplicatesDataGridView.Columns)
                {
                    String colName = column.Name;
                    if (!IsInputDataColumn(colName))
                    {
                        duplicatesDataGridView.Columns[colName].DisplayIndex = duplicatesDataGridView.ColumnCount - 1;
                    }
                }

                duplicatesDataGridView.Columns[scoreColName].DisplayIndex = 0;



                Double maxScore = 0;

                for (int i = 0; i < DuplicatesTable.RecordCount; i++)
                {
                    double scoreValue = (double)DuplicatesTable.GetData(i, scoreColName) / 100;
                    if (scoreValue > maxScore)
                    {
                        maxScore = scoreValue;
                    }

                    duplicatesDataGridView.Rows[i].DefaultCellStyle.ForeColor = scoreValue >= autoMatchThreshold ? duplicateColor
                                                                                                                          : scoreValue >= manualReviewThreshold ? potentialDuplicateColor
                                                                                                                                                                : uniqueColor;
                }

                if (maxScore >= autoMatchThreshold)
                {
                    this.statusLbl.ForeColor = duplicateColor;
                    this.statusLbl.Text = $"DUPLICATE";
                }
                else if (maxScore >= manualReviewThreshold)
                {
                    this.statusLbl.ForeColor = potentialDuplicateColor;
                    this.statusLbl.Text = $"POTENTIAL DUPLICATE";

                    insertNewRecord = MessageBox.Show($"Do you want to add the new record\nto the data source?\n\nNumber of similar records: {DuplicatesTable.RecordCount}",
                                                       "Manual Record Review",
                                                       MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes;
                }
                else
                {
                    insertNewRecord = true;
                    this.statusLbl.ForeColor = uniqueColor;
                    this.statusLbl.Text = $"UNIQUE";
                }
            }
            else
            {
                insertNewRecord = true;
                this.statusLbl.ForeColor = uniqueColor;
                this.statusLbl.Text = $"UNIQUE";
            }

            if (insertNewRecord)
            {
                _presenter.InsertRecord();
            }
        }

        public void BindInsertedTable()
        {
            duplicatesDataGridView.DataSource = new VirtualListDynamic(InsertedTable);
        }

        public void BindCleansedTable()
        {
            this.dbDataGrid.DataSource = new VirtualListDynamic(CleansedTable);
        }

        public void DisplayError(String errorMessage)
        {
            MessageBox.Show(errorMessage,
                            "Error",
                            MessageBoxButtons.OK,
                            MessageBoxIcon.Error);
        }

        #endregion

        #region Methods

        private void PopulateInputPanels()
        {
            searchCriteriaPanel.Controls.Clear();
            inputRecordPanel.Controls.Clear();
            fieldThresholdPanel.Controls.Clear();

            int verticalDist = 30;
            int verticalMargin = 5;
            int horizontalMargin = 5;
            int maxLeft = 0;
            int height = 10;

            for (int i = 0; i < Project.Criteria.Count; i++)
            {
                string fieldName = Project.Criteria[i].FieldName;
                Label label = new Label
                {
                    AutoSize = true,
                    Name = fieldName,
                    Text = fieldName + ":",
                    Top = i * verticalDist + verticalMargin,
                    Left = horizontalMargin
                };
                //label.Width = 20;
                searchCriteriaPanel.Controls.Add(label);
                maxLeft = Math.Max(label.Left + label.Width, maxLeft);
            }

            maxLeft += horizontalMargin;

            for (int i = 0; i < Project.Criteria.Count; i++)
            {
                string fieldName = Project.Criteria[i].FieldName;

                TextBox textBox = new TextBox
                {
                    Name = fieldName,
                    Top = i * verticalDist + verticalMargin,
                    Left = maxLeft,
                    Width = searchCriteriaPanel.Width - maxLeft - horizontalMargin - 20
                };

                searchCriteriaPanel.Controls.Add(textBox);
            }

            maxLeft = 0;

            for (int i = 0; i < Project.Criteria.Count; i++)
            {
                string fieldName = Project.Criteria[i].FieldName;

                Label label = new Label
                {
                    AutoSize = true,
                    Name = fieldName,
                    Text = fieldName + ":",
                    Top = i * verticalDist + verticalMargin,
                    Left = horizontalMargin
                };
                //label.Width = 20;
                inputRecordPanel.Controls.Add(label);
                maxLeft = Math.Max(label.Left + label.Width, maxLeft);
            }

            maxLeft += horizontalMargin;

            for (int i = 0; i < Project.Criteria.Count; i++)
            {
                string fieldName = Project.Criteria[i].FieldName;

                TextBox textBox = new TextBox
                {
                    Name = fieldName,
                    Top = i * verticalDist + verticalMargin,
                    Left = maxLeft,
                    Width = inputRecordPanel.Width - maxLeft - horizontalMargin - 20
                };

                inputRecordPanel.Controls.Add(textBox);
            }

            for (int i = 0; i < Project.Criteria.Count; i++)
            {
                string fieldName = Project.Criteria[i].FieldName;
                Label label = new Label
                {
                    AutoSize = true,
                    Name = fieldName,
                    // todo: add actual threshold info
                    Text = $"{fieldName} ({Project.Criteria[i].Threshold}%)",
                    Top = i * verticalDist + verticalMargin,
                    Left = horizontalMargin
                };
                label.Width = 20;
                fieldThresholdPanel.Controls.Add(label);
            }

            fieldThresholdPanel.AutoScroll = true;
            inputRecordPanel.AutoScroll = true;
            searchCriteriaPanel.AutoScroll = true;
        }

        string[] GetSearchValues()
        {
            List<string> result = new List<string>();
            for (int i = 0; i < searchCriteriaPanel.Controls.Count; i++)
            {
                Control control = searchCriteriaPanel.Controls[i];
                if (control is TextBox)
                {
                    TextBox textBox = control as TextBox;
                    result.Add(textBox.Text.Trim());
                }
            }
            return result.ToArray();
        }

        private void SaveSearchValues()
        {
        }

        private async void Search()
        {
            timeInfoLabel.Text = "...";

            Application.DoEvents();

            System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
            
            SearchValues = GetSearchValues();

            int bestMatchesCapacity;
            if (!int.TryParse(maxCapacityTextBox.Text, out bestMatchesCapacity))
            {
                MessageBox.Show("Invalid max capacity!");
                return;
            }

            BestMatchesCapacity = bestMatchesCapacity;

            searchResultsDataGridView.Enabled = false;
            //searchProgressBar.Style = ProgressBarStyle.Marquee;
            //searchProgressBar.Show();

            stopwatch.Start();
            ////var val = await Task.Run(() => _presenter.PerformLiveSearch());
            var val = _presenter.PerformLiveSearch();
            stopwatch.Stop();

            //searchProgressBar.Style = ProgressBarStyle.Blocks;
            //searchProgressBar.Value = searchProgressBar.Minimum;
            //searchProgressBar.Hide();
            searchResultsDataGridView.Enabled = true;
            //TimeSpan span = val < stopwatch.Elapsed ? val : stopwatch.Elapsed;
            
            timeInfoLabel.Text = "Response time: " + PerfectFormatTime(val);
        }

        private String PerfectFormatTime(TimeSpan timeSpan)
        {
            return String.Format("{0} sec {1} ms", (Int32)timeSpan.TotalSeconds, timeSpan.Milliseconds);
        }

        #endregion

        #region Events and Handlers

        private void openProjectButton_Click(object sender, EventArgs e)
        {
            try
            {
                //TODO Splash on
                this.Enabled = false;

                _presenter.LoadProjectsPath();

                projectOpenFileDialog.InitialDirectory = ProjectsPath;

                if (projectOpenFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    this.Project.Path = projectOpenFileDialog.FileName;
                    this.Project.Name = System.IO.Path.GetFileNameWithoutExtension(this.Project.Path);
                    _presenter.LoadProject();

                    if (AutoSearchAndLoad)
                    {
                        _presenter.LoadSavedSearchValues();
                    }
                }
            }
            finally
            {
                this.Enabled = true;
                //TODO Splash off
            }
        }

        private void searchButton_Click(object sender, EventArgs e)
        {
            Search();
        }

        private void searchTimer_Tick(object sender, EventArgs e)
        {
            string[] searchValues = GetSearchValues();

            if (searchValues.Length == previousSearchValues.Length)
            {
                for (int i = 0; i < searchValues.Length; i++)
                {
                    if (searchValues[i] != previousSearchValues[i])
                    {
                        doTimerSearch = true;
                    }
                    previousSearchValues[i] = searchValues[i];
                }
            }
            else
            {
                previousSearchValues = GetSearchValues();
            }
            if (doTimerSearch)
            {
                //if (!engineWrapper.SearchInProgress)
                {
                    Search();
                    doTimerSearch = false;
                }
            }
        }

        private void liveSearchCheckBox_CheckedChanged(object sender, EventArgs e)
        {
            searchTimer.Enabled = liveSearchCheckBox.Checked;
        }

        private void resetButton_Click(object sender, EventArgs e)
        {
            foreach (Control control in inputRecordPanel.Controls)
            {
                (control as TextBox)?.ResetText();
            }
        }

        private void submitButton_Click(object sender, EventArgs e)
        {
            duplicatesDataGridView.DataSource = null; // clearing the duplicates grid

            List<string> result = new List<string>();
            for (int i = 0; i < inputRecordPanel.Controls.Count; i++)
            {
                Control control = inputRecordPanel.Controls[i];
                if (control is TextBox)
                {
                    TextBox textBox = control as TextBox;
                    result.Add(textBox.Text.Trim());
                }
            }

            InputValues = result.ToArray();

            double autoMatchThreshold = (double)autoMatchUpDown.Value / 100;
            double manualReviewThreshold = (double)manualReviewUpDown.Value / 100;

            _presenter.CheckForDuplicates();
        }

        private void setTresholdsButton_Click(object sender, EventArgs e)
        {
            double high = (double)(autoMatchUpDown.Value) / 100.0;
            double low = (double)(manualReviewUpDown.Value) / 100.0;
            _presenter?.SetThresholds(high, low);
        }

        private void MainForm_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.F1)
            {
                LiveSearchDemo.SettingsForm settings = new LiveSearchDemo.SettingsForm();
                settings.Init(this);
                settings.ShowDialog();
            }
        }

        private void checkBoxUseFloatingThresholds_CheckedChanged(object sender, EventArgs e)
        {
            generalThresholdGroupBox.Enabled = checkBoxUseFloatingThresholds.Checked;
            ChangePresenterMode();
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            recordInfoPanel.Enabled = checkBoxUseCacheTable.Checked;
            ChangePresenterMode();
        }

        private void ChangePresenterMode()
        {
            _presenter?.ChangeMode(checkBoxUseFloatingThresholds.Checked, checkBoxUseCacheTable.Checked);
        }
        #endregion
    }
}
