using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Windows.Forms;
using dataladder.Data;
using dataladder.XtraGridHelper;
//using DevExpress.XtraSplashScreen;
using SampleServiceNamespace;

namespace GUIusingWebService
{
    public partial class MainForm : Form
    {
        #region Fields

        SampleService sampleService = new SampleService();
        InMemoryTable inputRecordsTable;
        readonly string savedSearchDir;
        string[] activeProjectFieldNames;
        System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();

        #endregion

        #region Constructors

        public MainForm()
        {
            InitializeComponent();
            savedSearchDir = Directory.GetCurrentDirectory() + @"\saved search";
            createDirectoryIfNotExist(savedSearchDir);
            itsiButton.Visible = false;
            this.Text = "Live Search Demo Using Web Service";
        }

        #endregion

        #region Properties and Fields

        public string ProjectName
        {
            get { return projectTextBox.Text.Trim(); }
        }

        public bool DoTransformation
        {
            get { return transformationCheckBox.Checked; }
        }

        #endregion

        #region Methods

        private static bool createDirectoryIfNotExist(string dir)
        {
            bool result = Directory.Exists(dir);
            if (!result)
            {
                try
                {
                    Directory.CreateDirectory(dir);
                    result = true;
                }
                catch
                {
                    result = false;
                }
            }

            return result;
        }

        string getSavedSearchValuesTableName()
        {
            StringBuilder tmp = new StringBuilder(ProjectName);
            for (int i = 0; i < activeProjectFieldNames.Length; i++)
            {
                string fieldName = activeProjectFieldNames[i];
                tmp.Append(fieldName);
            }

            return tmp.ToString();
        }

        void saveSearchValues()
        {
            string tableName = getSavedSearchValuesTableName();
            OnDriveTable savedValuesTable = new OnDriveTable(savedSearchDir,
                tableName,
                toDeleteExisting: true);
            for (int colIndex = 0; colIndex < inputRecordsTable.ColumnCount; colIndex++)
            {
                string columnName = inputRecordsTable.GetColumnName(colIndex);
                savedValuesTable.AddField(columnName);
            }

            for (int rowIndex = 0; rowIndex < inputRecordsTable.RecordCount; rowIndex++)
            {
                for (int colIndex = 0; colIndex < inputRecordsTable.ColumnCount; colIndex++)
                {
                    object obj = inputRecordsTable.GetData(rowIndex, colIndex);
                    savedValuesTable.SetData(obj, rowIndex, colIndex);
                }
            }

            savedValuesTable.ToDeleteFilesAfterClosing = false;
            savedValuesTable.SaveHeaderFile();
            savedValuesTable.Dispose();
        }


        bool loadSearchValues()
        {
            bool result = false;
            string tableName = getSavedSearchValuesTableName();
            if (OnDriveTableHelper.Exists(savedSearchDir, tableName))
            {
                OnDriveTable savedValuesTable = new OnDriveTable(savedSearchDir,
                    tableName,
                    loadExisting: true);
                if (inputRecordsTable.ColumnCount != savedValuesTable.ColumnCount)
                {
                    savedValuesTable.Dispose();
                    MessageBox.Show("saved number of columns is different than existing number of columns!");
                    return result;
                }

                for (int rowIndex = 0; rowIndex < savedValuesTable.RecordCount; rowIndex++)
                {
                    for (int colIndex = 0; colIndex < savedValuesTable.ColumnCount; colIndex++)
                    {
                        object obj = savedValuesTable.GetData(rowIndex, colIndex);
                        inputRecordsTable.SetData(obj, rowIndex, colIndex);
                    }
                }

                savedValuesTable.Dispose();
                result = inputRecordsTable.RecordCount > 0;
            }

            return result;
        }

        private InMemoryTable search(string[] values)
        {
            string hash = PortableHash.GetHash(ProjectName);
            string[] searchResult = sampleService.GetMatches(hash, ProjectName, values, 100, true);
            InMemoryTable searchResultsTable = new InMemoryTable("Search Results Table");
            int fieldsCount;
            int.TryParse(searchResult[0], out fieldsCount);
            if (fieldsCount > 0)
            {
                for (int colIndex = 0; colIndex < fieldsCount; colIndex++)
                {
                    searchResultsTable.AddField(searchResult[colIndex + 1], OnDriveTable.StorageDataType.Other);
                }
            }

            int rowCount = (searchResult.Length - fieldsCount - 1) / fieldsCount;
            int index = fieldsCount + 1;
            for (int rowIndex = 0; rowIndex < rowCount; rowIndex++)
            {
                for (int colIndex = 0; colIndex < fieldsCount; colIndex++)
                {
                    string value = searchResult[index++];
                    searchResultsTable.SetData(value, rowIndex, colIndex);
                }
            }

            return searchResultsTable;
        }

        private string[] getSearchValues()
        {
            string[] result = new string[inputRecordsTable.RecordCount * inputRecordsTable.ColumnCount];
            int index = 0;
            for (int rowindex = 0; rowindex < inputRecordsTable.RecordCount; rowindex++)
            {
                for (int colIndex = 0; colIndex < inputRecordsTable.ColumnCount; colIndex++)
                {
                    object obj = inputRecordsTable.GetData(rowindex, colIndex);
                    string s = (obj == null) ? "" : obj.ToString();
                    result[index++] = s;
                }
            }

            return result;
        }

        private void InitProject()
        {
            stopwatch.Restart();
            string hash = PortableHash.GetHash(ProjectName);
            bool initResult;
            bool initResultSpecified;

            UpdateLoadStatus($"Initializing project '{ProjectName}...'");

            Boolean initError = false;

            try
            {
                sampleService.Init(hash, ProjectName, useLog: false, useLogSpecified: false,
                    doTransformation: DoTransformation, doTransformationSpecified: DoTransformation,
                    InitResult: out initResult, InitResultSpecified: out initResultSpecified);

                if (initResult)
                {
                    statusLabel.Text = ProjectName + " started";
                }
            }
            catch (System.Net.WebException ex)
            {
                initError = true;
                MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            if (initError)
            {
                return;
            }

            //sampleService.InitAsync(hash, ProjectName, useLog: false, useLogSpecified: false, doTransformation: DoTransformation, doTransformationSpecified: DoTransformation);

            UpdateLoadStatus($"Getting matched fields...'");

            activeProjectFieldNames = sampleService.GetMatchingFields(hash, ProjectName);

            inputRecordsTable =
                new InMemoryTable("Input Records Table", /* activeProjectFieldNames, */ toCheckMemory: false);
            inputRecordsTable.AddFields(activeProjectFieldNames);
            if (!loadSearchValues())
            {
                inputRecordsTable.SetData("", 0, 0); // ensuring one row
            }

            inputRecordsDataGridView.DataSource = new VirtualListDynamic(inputRecordsTable);
            showResponseTime();
            this.Text = "Live Search Demo Using Web Service - " + ProjectName;
        }

        #endregion

        #region Events

        private void initButton_Click(object sender, EventArgs e)
        {
            try
            {
                // TODO
                //SplashScreenManager.ShowDefaultWaitForm(this, true, true, false, 0, "Please wait.", "Loading project...");
                this.Enabled = false;
                InitProject();
            }
            finally
            {
                this.Enabled = true;
                // TODO
                // SplashScreenManager.CloseDefaultWaitForm();
            }
        }

        private void showResponseTime()
        {
            stopwatch.Stop();
            timerLabel.Text = "Response time: " + PerfectFormatTime(stopwatch.Elapsed);
        }

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

        private void UpdateLoadStatus(String status)
        {
            // TODO
            // SplashScreenManager.Default?.SetWaitFormDescription(status);
        }

        private void AddNewRecord()
        {
            inputRecordsTable.SetData("", inputRecordsTable.RecordCount, 0);
            inputRecordsDataGridView.DataSource = new VirtualListDynamic(inputRecordsTable);
        }

        private void RemoveLastRecord()
        {
            if (inputRecordsTable != null)
            {
                HashSet<Int32> rowsToSkip = new HashSet<Int32>
                {
                    inputRecordsTable.RecordCount - 1
                };
                InMemoryTable newTable = CloneTable(inputRecordsTable, rowsToSkip);
                if (newTable != null)
                {
                    inputRecordsTable.Dispose();
                    inputRecordsTable = null;
                    inputRecordsTable = newTable;
                    inputRecordsDataGridView.DataSource = new VirtualListDynamic(inputRecordsTable);
                }
            }
        }

        private InMemoryTable CloneTable(InMemoryTable sourceTable, HashSet<Int32> rowsToSkip)
        {
            String tableName = "InMemoryTable";
            InMemoryTable newTable;

            if (sourceTable == null)
            {
                newTable = new InMemoryTable(tableName);
                return newTable;

            }

            newTable = new InMemoryTable(sourceTable.Name, sourceTable.ToCheckMemory, sourceTable.CompactMode, sourceTable.RecordCount);

            for (Int32 colIndex = 0; colIndex < sourceTable.ColumnCount; colIndex++)
            {
                OnDriveTable.StorageDataType colType = sourceTable.GetColumnType(colIndex);
                String colName = sourceTable.GetColumnName(colIndex);
                //Object tag = sourceTable.GetTag(colIndex);
                newTable.AddField(colName, colType);
                //if (tag != null)
                //{
                //    newTable.SetTag(colIndex, tag);
                //}
            }

            for (Int32 rowIndex = 0; rowIndex < sourceTable.RecordCount; rowIndex++)
            {
                if (rowsToSkip.Contains(rowIndex))
                {
                    continue;
                }
                for (Int32 colIndex = 0; colIndex < sourceTable.ColumnCount; colIndex++)
                {
                    Object cellValue = sourceTable.GetData(rowIndex, colIndex);
                    newTable.SetData(cellValue, rowIndex, colIndex);
                }
            }

            return newTable;
        }

        #endregion

        private void newRecordButton_Click(object sender, EventArgs e)
        {
            AddNewRecord();
        }

        private void searchButton_Click(object sender, EventArgs e)
        {
            stopwatch.Restart();
            saveSearchValues();
            string[] values = getSearchValues();
            InMemoryTable searchResultsTable = search(values);
            resultsDataGridView.DataSource = new VirtualListDynamic(searchResultsTable);
            showResponseTime();
            timerLabel.Text += " " + searchResultsTable.RecordCount.ToString() + " records found";
        }

        private void stopButton_Click(object sender, EventArgs e)
        {
            stopwatch.Restart();
            string hash = PortableHash.GetHash(ProjectName);
            bool unloadResult;
            bool unloadResultSpecified;
            sampleService.Unload(hash, ProjectName, out unloadResult, out unloadResultSpecified);
            if (unloadResult)
            {
                statusLabel.Text = ProjectName + " stopped";
            }

            showResponseTime();
        }

        #region ITSI specific...

        string getRowValue(DataRow dataRow, string fieldName)
        {
            object obj = dataRow[fieldName];
            string result = (obj != DBNull.Value) ? obj.ToString() : "";
            return result;
        }

        private void itsiButton_Click(object sender, EventArgs e)
        {
            //compareApiAndGuiResultsGroups();
            compareApiAndGuiResultsPairs();
        }

        private void compareApiAndGuiResultsGroups()
        {
            DataTable groupsTable;
            using (SqlConnection conn =
                new SqlConnection(@"Server=WNET-A15053\SQLEXPRESS;Database=API;Trusted_Connection=True;"))
            {
                conn.Open();
                string cmdText =
                    "select [Group ID], [person], [First Name], [Middle Name], [Last Name], [Common Name] from person_names_groups order by [Group ID], [Score] desc";
                groupsTable = executeCmd(cmdText, conn);
            }

            int rowIndex = 0;
            StringBuilder sbDifferences = new StringBuilder();
            while (true)
            {
                DataRow dataRow = groupsTable.Rows[rowIndex];
                string[] values = new string[4];
                int groupId = (int) dataRow["Group ID"];
                values[0] = getRowValue(dataRow, "First Name");
                values[1] = getRowValue(dataRow, "Middle Name");
                values[2] = getRowValue(dataRow, "Last Name");
                values[3] = getRowValue(dataRow, "Common Name");
                testApiLabel.Text = values[0] + " " + values[1] + " " + values[2] + " " + values[3];
                Application.DoEvents();
                InMemoryTable searchResults = search(values);
                int recordInGroupCount = searchResults.RecordCount;
                for (int i = 0; i < recordInGroupCount - 1; i++)
                {
                    rowIndex++;
                    if (rowIndex > groupsTable.Rows.Count)
                    {
                        testApiLabel.Text = "testing finished";
                        break;
                    }

                    dataRow = groupsTable.Rows[rowIndex];
                    if (groupId != (int) dataRow["Group ID"])
                    {
                        sbDifferences.Append(groupId.ToString());
                        sbDifferences.Append(" ");
                        for (int ii = 0; ii < 3; ii++)
                        {
                            sbDifferences.Append(values[ii]);
                            sbDifferences.Append(" ");
                        }

                        sbDifferences.AppendLine("");
                        Application.DoEvents();
                        rowIndex--;
                        break;
                    }
                }

                rowIndex++;
            }

            for (rowIndex = 0; rowIndex < groupsTable.Rows.Count; rowIndex++)
            {
                DataRow dataRow = groupsTable.Rows[rowIndex];
            }
        }

        void updatePairsDictionary(Dictionary<int, List<int>> allPairsDict,
            int recordA,
            int recordB)
        {
            List<int> matchingRecordsList;
            if (!allPairsDict.TryGetValue(recordA, out matchingRecordsList))
            {
                matchingRecordsList = new List<int>();
                allPairsDict.Add(recordA, matchingRecordsList);
            }

            if (!matchingRecordsList.Contains(recordB))
            {
                matchingRecordsList.Add(recordB);
            }
        }

        private void compareApiAndGuiResultsPairs()
        {
            DataTable pairsTable;
            OnDriveTable compareResultsTable = new OnDriveTable(@".\", "ApiVsGuiDiffs", toDeleteExisting: true);
            compareResultsTable.ToDeleteFilesAfterClosing = false;
            compareResultsTable.AddField("side");
            compareResultsTable.AddField("Record");
            compareResultsTable.AddField("Matching Record");
            compareResultsTable.SaveHeaderFile();
            using (SqlConnection conn =
                new SqlConnection(@"Server=WNET-A15053\SQLEXPRESS;Database=API;Trusted_Connection=True;"))
            {
                conn.Open();
                //string cmdText = "select [Pair ID], Record, [Matching Record], [person], [First Name], [Middle Name], [Last Name], [Common Name] from person_names_pairs_opt_off";
                //string cmdText = "select [Pair ID], Record, [Matching Record], [fullname], [company], [address1], [zip] from person_names_pairs_opt_off";
                string cmdText =
                    "select [Pair ID], Record, [Matching Record], [fullname], [company], [address1], [zip] from dbo.[ApiTest1ds results]";
                pairsTable = executeCmd(cmdText, conn);
            }

            Dictionary<int, string[]> valuesByRecordDict = new Dictionary<int, string[]>();
            Dictionary<int, List<int>> allPairsDict = new Dictionary<int, List<int>>();
            for (int rowIndex = 0; rowIndex < pairsTable.Rows.Count; rowIndex++)
            {
                DataRow dataRow = pairsTable.Rows[rowIndex];
                int record = (int) dataRow["Record"];
                int matchingRecord = (int) dataRow["Matching Record"];
                updatePairsDictionary(allPairsDict, record, matchingRecord);
                updatePairsDictionary(allPairsDict, matchingRecord, record);
                string[] values;
                if (!valuesByRecordDict.TryGetValue(record, out values))
                {
                    values = new string[4];
                    //values[0] = getRowValue(dataRow, "First Name");
                    //values[1] = getRowValue(dataRow, "Middle Name");
                    //values[2] = getRowValue(dataRow, "Last Name");
                    //values[3] = getRowValue(dataRow, "Common Name");
                    values[0] = getRowValue(dataRow, "fullname");
                    values[1] = getRowValue(dataRow, "company");
                    values[2] = getRowValue(dataRow, "address1");
                    values[3] = getRowValue(dataRow, "zip");
                    valuesByRecordDict.Add(record, values);
                }
            }

            int recordsProcessed = 0;
            foreach (KeyValuePair<int, string[]> keyValuePair in valuesByRecordDict)
            {
                int record = keyValuePair.Key;
                string[] values = keyValuePair.Value;
                List<int> expectedRecords;
                bool problemFound = false;
                recordsProcessed++;
                if (allPairsDict.TryGetValue(record, out expectedRecords))
                {
                    testApiLabel.Text = " #" + recordsProcessed.ToString() + " " + values[0] + " " + values[1] + " " +
                                        values[2] + " " + values[3];
                    Application.DoEvents();
                    InMemoryTable searchResults = search(values);
                    List<int> foundRecords = new List<int>();
                    for (int i = 0; i < searchResults.RecordCount; i++)
                    {
                        int matchingRecord = Convert.ToInt32(searchResults.GetData(i, "Record"));
                        if (matchingRecord == record)
                        {
                            continue;
                        }

                        foundRecords.Add(matchingRecord);
                    }

                    problemFound = logDiffs(compareResultsTable, record, expectedRecords, foundRecords);
                }
                else
                {
                    problemFound = true;
                }

                if (problemFound)
                {
                    break;
                }
            }

            testApiLabel.Text = " #" + recordsProcessed.ToString() + " finished!";
            resultsDataGridView.DataSource = new VirtualListDynamic(compareResultsTable);
        }

        private static bool logDiffs(OnDriveTable compareResultsTable, int record, List<int> expectedRecords,
            List<int> foundRecords)
        {
            bool result = logDiffsFromOneSide(compareResultsTable, "API", record, expectedRecords, foundRecords);
            result |= logDiffsFromOneSide(compareResultsTable, "GUI", record, foundRecords, expectedRecords);
            return result;
        }

        private static bool logDiffsFromOneSide(OnDriveTable compareResultsTable, string side, int record,
            List<int> recordsA, List<int> recordsB)
        {
            bool problemFound = false;
            for (int i = 0; i < recordsA.Count; i++)
            {
                int matchingRecord = recordsA[i];
                if (!recordsB.Contains(matchingRecord))
                {
                    problemFound = true;
                    int newRowIndex = compareResultsTable.RecordCount;
                    compareResultsTable.SetData(side, newRowIndex, 0);
                    compareResultsTable.SetData(record, newRowIndex, 1);
                    compareResultsTable.SetData(matchingRecord, newRowIndex, 2);
                    compareResultsTable.Flush();
                }
            }

            return problemFound;
        }

        private DataTable executeCmd(string cmdText, SqlConnection conn)
        {
            DataTable result = null;
            SqlCommand sqlCommand = new SqlCommand(cmdText, conn);

            using (SqlCommand cmdSelect = new SqlCommand(cmdText, conn))
            {
                using (SqlDataAdapter adapter = new SqlDataAdapter())
                {
                    adapter.SelectCommand = cmdSelect;
                    result = new DataTable();
                    adapter.Fill(result);
                }
            }

            return result;
        }

        private void LoadProject()
        {
            String appPath = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
            String engineConfigurationIniFileName = "configuration.ini";
            string fullIniName = System.IO.Path.Combine(appPath, engineConfigurationIniFileName);
            IniParser parser = new IniParser(fullIniName);
            string projectsPath = parser.GetSetting("AppSettings", "projectsPath");
            using (OpenFileDialog projectOpenFileDialog = new OpenFileDialog())
            {

                projectOpenFileDialog.InitialDirectory = projectsPath;
                if (projectOpenFileDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    string projectFileName = projectOpenFileDialog.FileName;
                    String projectName = System.IO.Path.GetFileNameWithoutExtension(projectFileName);
                    projectTextBox.Text = projectName;
                }
            }
        }

        #endregion

        private void loadButton_Click(Object sender, EventArgs e)
        {
            LoadProject();
        }

        private void removeRecordButton_Click(Object sender, EventArgs e)
        {
            RemoveLastRecord();
        }
    }
}
