using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using ModelData.BusinessModel.RDF;
using ModelData.BusinessModel.MainEntities;
using ModelData.BusinessModel.ExtraEntities;
using ModelData.BusinessModel.Tools;

namespace ModelData.BusinessModel.UnitOfWork
{
    public class Context
    {
        public List<MapPoint> MapPoints { set; get; }
        public List<Region> Regions { set; get; }
        public List<SettlementType> SettlementTypes { set; get; }

        public Context ()
        {
            MapPoints = new List<MapPoint>();
            Regions = new List<Region>();
            SettlementTypes = new List<SettlementType>();
        }

        public void GetAllSettlements(string YearMin, string YearMax)
        {
            //url title lat long settlementType typeName region regionName maxPop
            var lst_query = RDFQuery.GetData(RDFQuery.QueryType.AllSettlements, new string[] { YearMin, YearMax });

            MapPoint mapPoint;
            string str;
            Region region;
            SettlementType settlementType;

            foreach (var elem in lst_query)
            {
                str = elem["url"].ToString();

                mapPoint = MapPoints.Find(x => x.URL == str);

                if (mapPoint == null)
                {
                    mapPoint = new MapPoint()
                    {
                        URL = str,
                        Title = Convert(elem["title"])/*elem["title"].ToString()*/,
                        Position = Convert(elem["lat"]) + " " + Convert(elem["long"]),                        
                        Population = Int32.Parse(Convert(elem["maxPop"]))
                    };
                    MapPoints.Add(mapPoint);
                }

                if (elem["region"] != null)
                {
                    str = Convert(elem["region"]);

                    region = Regions.Find(x => x.URL == str);

                    if (region == null)
                    {
                        region = new Region()
                        {
                            URL = str,
                            Title = Convert(elem["regionName"])//elem["regionName"].ToString()
                        };
                        Regions.Add(region);
                    }

                    if (!mapPoint.Region.Contains(region.Title))
                        mapPoint.Region.Add(region.Title);
                }

                if (elem["settlementType"] != null)
                {
                    str = Convert(elem["settlementType"]);

                    settlementType = SettlementTypes.Find(x => x.URL == str);

                    if (settlementType == null)
                    {
                        settlementType = new SettlementType()
                        {
                            URL = str,
                            Title = Convert(elem["typeName"])//elem["typeName"].ToString()
                        };
                        SettlementTypes.Add(settlementType);
                    }

                    if (!mapPoint.Type.Contains(settlementType.Title))
                        mapPoint.Type.Add(settlementType.Title);
                }               
            }
        }

        public Settlement LoadInfoAboutSettlement(string URL)
        {
            Settlement settlement = null;
            EditedSettlement edited;
            List<EditedSettlement> listEdited = new List<EditedSettlement>();

            var lst_query = RDFQuery.GetData(RDFQuery.QueryType.SettlementInfo, new string[] { URL });

            foreach (var elem in lst_query)
            {
                // title lat long wasFoundYear legend founder person foundersRegion editedSettlement sourceURL sourceTitle sourceCreator year settlementType typeName region regionName editedName alternateName population populationFemales populationMales

                if (elem["editedSettlement"] == null)
                {
                    settlement = new Settlement(URL, elem["title"].ToString(), new Coordinate(elem["lat"], elem["long"]));

                    if (elem["wasFoundYear"] != null)
                        settlement.hasBeginning = new InstantTime(Convert(elem["wasFoundYear"]));

                    if (elem["legend"] != null)
                        settlement.Legend = Convert(elem["legend"]);

                    if (elem["founder"] != null)
                    {
                        settlement.Founder = new Founder() { URL = Convert(elem["founder"]) };

                        if (elem["person"] != null)
                            settlement.Founder.Name = Convert(elem["person"]);

                        if (elem["foundersRegion"] != null)
                            settlement.Founder.FounderRegion = Convert(elem["foundersRegion"]);
                    }
                    
                    continue;
                }


                edited = new EditedSettlement
                    (
                        elem["editedSettlement"].ToString(),
                        settlement.URL,
                        new InstantTime(Convert(elem["year"])),
                        new Source
                        {
                            Title = elem["sourceTitle"].ToString(),
                            URLs = new List<string> { elem["sourceURL"].ToString() },
                            Auther = (elem["sourceCreator"] != null) ? Convert(elem["sourceCreator"]) : ""
                        }
                    );

                if (elem["editedName"] != null)
                    edited.Title = Convert(elem["editedName"]);

                if (elem["population"] != null)
                    edited.PopulationAll = int.Parse(Convert(elem["population"]));

                if (elem["populationMales"] != null)
                    edited.PopulationMales = int.Parse(Convert(elem["populationMales"]));

                if (elem["populationFemales"] != null)
                    edited.PopulationFemales = int.Parse(Convert(elem["populationFemales"]));

                if (elem["alternateName"] != null)
                    edited.AlternativeName.Add(Convert(elem["alternateName"]));

                if (elem["region"] != null)               
                    edited.Region = new Region()
                    {
                        URL = Convert(elem["region"]),
                        Title = Convert(elem["regionName"])
                    };

                if (elem["settlementType"] != null)
                    edited.Type = new SettlementType()
                    {
                        URL = Convert(elem["settlementType"]),
                        Title = Convert(elem["typeName"])
                    };

                //  Если в результате существования нескольких URL у Source происходит дублирование EditedSettlements
                //  То сначала будет добавлен новый экземпляр EditedSettlement'а, а потом наткнемся на его копию, у которой URL источника другой
                //  Тогда просто впишем URL в существующий источник
                var wasAdded = listEdited.Find(e => e.URL == edited.URL);
                if (wasAdded == null)
                    listEdited.Add(edited);
                else
                    wasAdded.Source.URLs.AddRange(edited.Source.URLs);                
            }
            settlement.EditedSettlements = listEdited;

            return settlement;
        }

        public List<Region> LoadAllRegions()
        {
            var query_result = RDFQuery.GetData(RDFQuery.QueryType.AllRegions, new string[] { });
            foreach (var elem in query_result)
            {
                Regions.Add(new Region()
                {
                    URL = Convert(elem["region"]),
                    Title = Convert(elem["regionName"])
                });
            }
            return Regions;
        }

        public List<SettlementType> LoadAllTypes()
        {
            var query_result = RDFQuery.GetData(RDFQuery.QueryType.AllSettlementTypes, new string[] { });
            foreach (var elem in query_result)
            {
                SettlementTypes.Add(new SettlementType()
                {
                    URL = Convert(elem["settlementType"]),
                    Title = Convert(elem["typeName"])
                });
            }
            return SettlementTypes;
        }

        #region Tools

        private string GetValue(VDS.RDF.Query.SparqlResult elem, string value)
        {
            return elem[value]?.ToString();
        }

        private string Convert(VDS.RDF.INode node)
        {
            return node.ToString().Split('^')[0];
        }

        #endregion
    }
}
