using System.Data;
using System.Collections.Generic;
using System;
using System.Text;
using System.Threading;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using dataladder.AddressVerification;
using dataladder.Data;
using dataladder.Matching;
using dataladder.Matching.Indexing;
using dataladder.Licensing;
using dataladder.Matching.Project;
using dataladder.Data.DataTransformation;
using dataladder.XtraGridHelper;
using DataMatch.Data.Enums;
using System.Diagnostics;

namespace SampleServiceNamespace
{
    public class EngineWrapperFewDs : EngineWrapper
    {
        public EngineWrapperFewDs(string projectName, bool useLog = false, bool doTransformation = false, 
            string pathToConfig = "", string conString = "", bool useApi = false) : 
            base(projectName, useLog, doTransformation, pathToConfig, conString, useApi)
        {            
        }

        protected override MatchEngine initializeMatchingEngine()
        {
            projectInfo = new ProjectInfo();
            string proj = Path.Combine(_projectsPath, _projectName + ".dmeproj");
            projectInfo.Load(proj, loadResults:false);

            int countDatasourses = projectInfo.DataSourceCount;
            if (countDatasourses == 0)
            {
                throw new Exception("This project doesn't contain any data source.");
            }

            string[]  tables = new string[countDatasourses];
            DataSourceInfo[] originalDataSourceInfo = new DataSourceInfo[countDatasourses];

            for (int i = 0; i < countDatasourses; i++)
            {
                originalDataSourceInfo[i] = projectInfo[i];
                tables[i] = originalDataSourceInfo[i].InputTable.Name;

                originalDataSourceInfo[i].Refresh(projectInfo.DataPath, Path.Combine(projectInfo.DataPath, "uncomplete"), out string error);
                if (!string.IsNullOrEmpty(error))
                {
                    Errors += $"Error refreshing data source No{i} {tables[i]}: {error} {Environment.NewLine}";
                }
                originalDataSourceInfo[i].CreateDiagram();
                originalDataSourceInfo[i].RunTransformation();
            }


            dataSourceInfoSearchRecord = new DataSourceInfo(null, new AddressVerificationSettings());
            dataSourceInfoLog = new DataSourceInfo(null, new AddressVerificationSettings());

            if (singleRowTable != null)
            {
                singleRowTable.Dispose();
            }
            // create new table with columns same as in first data source
            singleRowTable = createSingleRowTableAndDetermineFieldNames(projectInfo);
            for (int colIndex = 0; colIndex < MatchingFieldNames.Length; colIndex++)
            {
                string fieldName = MatchingFieldNames[colIndex];
                singleRowTable.SetData("zzzzzzzzz", 0, fieldName);
            }
            if (useLog)
            {
                singleRowTable.SetData(singleRowOriginal, 0, "ACTION");
            }

            dataSourceInfoSearchRecord.InputTable = singleRowTable;
            projectInfo.Add(dataSourceInfoSearchRecord);

            if (useLog)
            {
                dataSourceInfoLog.InputTable = importedLogTable;
                projectInfo.Add(dataSourceInfoLog);
            }

            if (doTransformation)
            {
                // copying the data transformation rules to the log records
                dataSourceInfoSearchRecord.ColumnTransformationList.Copy(originalDataSourceInfo[0].ColumnTransformationList);
                dataSourceInfoSearchRecord.CreateDiagram();
                dataSourceInfoSearchRecord.RunTransformation();
                if (useLog)
                {
                    dataSourceInfoLog.ColumnTransformationList.Copy(originalDataSourceInfo[0].ColumnTransformationList);
                }
            }

            MatchDefinitionBuilder matchDefinitionBuilder = projectInfo.MatchDefinitionBuilder;

            // engine is prepared to support multiple match definitions, here we work only with one
            MultipleMatchDefinitionsManager multipleMatchDefinitionsManager = matchDefinitionBuilder.MultipleMatchDefinitionsManager;

            string firstTableInTheproject = projectInfo[0].InputTable.Name;

            for (int i = 0; i < multipleMatchDefinitionsManager.Count; i++)
            {
                MatchCriteriaList matchDefinitionsList = multipleMatchDefinitionsManager[i];
                for (int singleDefinitionIndex = 0; singleDefinitionIndex < matchDefinitionsList.Count; singleDefinitionIndex++)
                {
                    MatchCriteria matchDefinitionSingle = matchDefinitionsList[singleDefinitionIndex];
                    bool found;
                    string fieldName = matchDefinitionSingle.GetMappedFieldName(firstTableInTheproject, out found);
                    if (found) // in case of multiple definitions it could be found == false
                    {
                        matchDefinitionSingle.MapField(dataSourceNameSingleRow, fieldName);
                        if (useLog)
                        {
                            matchDefinitionSingle.MapField(dataSourceNameLog, fieldName);
                        }
                    }
                }
            }

            // add fields of new table into AvailableFields
            var lastTable = matchDefinitionBuilder.AvailableFields.TableList[projectInfo.DataSourceCount - 1];
            for (int i = 0; i < lastTable.Count; i++)
            {
                //Get from last table a field
                FieldMapInfo fieldMapInfo2 = lastTable[i];
                //And add it to the map of matching fields
                matchDefinitionBuilder.AvailableFields.MappedFieldsRowList[i].Add(fieldMapInfo2);
            }
            for (Int32 i = 0; i < matchDefinitionBuilder.AvailableFields.TableCount; ++i)
            {
                AvailableFieldsFromOneTable table = matchDefinitionBuilder.AvailableFields.TableList[i];
                table.Table = projectInfo[i].TransformedValuesTable;
            }
            
            // find all included fields
            for (int i = 0; i < matchDefinitionBuilder.AvailableFields.MappedFieldsRowList.Count; i++)
            {
                var list = matchDefinitionBuilder.AvailableFields.MappedFieldsRowList;
                if (list[i].Include)
                {
                    string fn = list[i]?.FirstNotEmptyFieldMapInfo?.FieldName;
                    if (fn != null)
                        wantedFields.Add(fn);
                }
            }

            MatchEngine matchEngine = projectInfo.CreateMatchEngine();
            matchEngine.UseApiAcceleration = UseApi;
            matchEngine.AllRecordsInGroupMustBeSimilar = false;

            matchEngine.ClearPairToMatchList(); // they are loaded with the project...
            matchEngine.DoIndex();
            dataSourceSearchIndex = projectInfo.DataSourceCount - 1;
            for (int i = 0; i < projectInfo.DataSourceCount - 1; i++)
            {
                matchEngine.AddPairToMatchList(i, dataSourceSearchIndex); // to find matches between data sources A and B
            }
            if (useLog)
            {
                matchEngine.AddPairToMatchList(1, 2); // to find matches between data sources B and C
            }
            cachedDatasetIndexes.Clear();
            //cachedDatasetIndexes.Add(0);
            for (int i = 0; i < projectInfo.DataSourceCount - 1; i++)
            {
                cachedDatasetIndexes.Add(i);
            }

            matchEngine.SetCachedDataSources(cachedDatasetIndexes);

            matchEngine.LoadAllInMemory = true;
            matchEngine.DoMatch(clearAllAfterMatching: false);
            matchEngine.ProcessFinalResults(clearAllAfterMatching: false);

            //List<string> tmp = new List<string>();
            //TableFieldNames = new string[matchDefinitionBuilder.AvailableFields.MappedFieldsRowList.Count];

            //for (int i = 0; i < matchDefinitionBuilder.AvailableFields.MappedFieldsRowList.Count; i++)
            //{
            //    MappedFieldsRow mappedFieldsRow = matchDefinitionBuilder.AvailableFields.MappedFieldsRowList[i];
            //    FieldMapInfo fieldMapInfo = mappedFieldsRow[this._tableName];
            //    if (fieldMapInfo != null)
            //    {
            //        string fieldName = fieldMapInfo.FieldName.Trim();

            //        if (!string.IsNullOrEmpty(fieldName))
            //        {
            //            tmp.Add(fieldName);
            //        }
            //    }
            //}

            //TableFieldNames = tmp.ToArray();
            //DetermineSqlFieldTypes();

            return matchEngine;
        }
    }
}