using pavesys_iRAP.Business;
using pavesys_iRAP.DTO;
using pavesys_iRAP.ViewModels;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.IO;
using System.Threading.Tasks;
using Avalonia.Controls;
using Serilog;
using Microsoft.EntityFrameworkCore;
using NetTopologySuite.Geometries;
using Mapsui.Projections;
using System.Globalization;
using System.Text;


namespace pavesys_iRAP.Helper
{
    public static class CrudHelper
    {

        private static Dictionary<string, Dictionary<int, int>> fieldMappings;
        public static PersistenceContext dbCtx;

        static CrudHelper()
        {
            fieldMappings = new Dictionary<string, Dictionary<int, int>>();
            dbCtx = new PersistenceContext();
            dbCtx.Database.SetCommandTimeout(TimeSpan.FromSeconds(55));
            // Initialize mappings for all fields
            InitializeMappings();

        }

        public static void RefreshContext()
        {
            dbCtx = new PersistenceContext();
            dbCtx.Database.SetCommandTimeout(TimeSpan.FromSeconds(55));
        }

        // Method to initialize mappings for 'UpgradeCost' field
        private static void InitializeMappings()
        {
            Dictionary<int, int> upgradeCostMap = new Dictionary<int, int>
            {
                { 1, 0 },
                { 2, 1 },
                { 3, 2 }
            };

            // Add 'UpgradeCost' mappings to the field mappings dictionary
            fieldMappings.Add("UpgradeCost", upgradeCostMap);

            Dictionary<int, int> roadworksMap = new Dictionary<int, int>
            {
                { 3, 2 },
                { 2, 1 },
                { 1, 0 }
            };

            // Add 'Roadworks' mappings to the field mappings dictionary
            fieldMappings.Add("RoadWorks", roadworksMap);

            Dictionary<int, int> areaTypeMap = new Dictionary<int, int>
            {
                { 2, 0 },
                { 1, 1 }
            };

            // Add 'AreaType' mappings to the field mappings dictionary
            fieldMappings.Add("AreaType", areaTypeMap);

            Dictionary<int, int> soilUseMap = new Dictionary<int, int>
            {
                { 6, 4 },
                { 4, 3 },
                { 7, 5 },
                { 3, 2 },
                { 2, 1 },
                { 1, 0 },
                { 5, 6 }
            };
            fieldMappings.Add("SoilUse", soilUseMap);

            Dictionary<int, int> speedLimitMap = new Dictionary<int, int>
            {
                { 25, 0 },
                { 24, 1 },
                { 23, 2 },
                { 22, 3 },
                { 21, 4 },
                { 20, 5 },
                { 19, 6 },
                { 18, 7 },
                { 17, 8 },
                { 16, 9 },
                { 15, 10 },
                { 14, 11 },
                { 13, 12 },
                { 12, 13 },
                { 11, 14 },
                { 10, 15 },
                { 9, 16 },
                { 8, 17 },
                { 7, 18 },
                { 6, 19 },
                { 5, 20 },
                { 4, 21 },
                { 3, 22 },
                { 2, 23 },
                { 1, 24 },
            };
            // Add 'SpeedLimit' mappings to the field mappings dictionary
            fieldMappings.Add("SpeedLimit", speedLimitMap);
            Dictionary<int, int> speedLimitMapMPH = new Dictionary<int, int>
            {
                { 45, 0 },
                { 44, 1 },
                { 43, 2 },
                { 42, 3 },
                { 41, 4 },
                { 40, 5 },
                { 39, 6 },
                { 38, 7 },
                { 37, 8 },
                { 36, 9 },
                { 35, 10 },
                { 34, 11 },
                { 33, 12 },
                { 32, 13 },
                { 31, 14 },
                { 30, 15 }
            };
            // Add 'SpeedLimitMph' mappings to the field mappings dictionary
            fieldMappings.Add("SpeedLimitMPH", speedLimitMapMPH);

            Dictionary<int, int> observedFlowMap = new Dictionary<int, int>
            {
                { 1, 0 },
                { 2, 1 },
                { 3, 2 },
                { 4, 3 },
                { 5, 4 },
                { 6, 5 }
            };
            // Add 'ObservedFlow' mappings to the field mappings dictionary
            fieldMappings.Add("ObservedFlow", observedFlowMap);
            Dictionary<int, int> propertyAccessPointsMap = new Dictionary<int, int>
            {
                { 4, 3 },
                { 1, 0 },
                { 2, 1 },
                { 3, 2 }
            };

            // Add 'PropertyAccessPoints' mappings to the field mappings dictionary
            fieldMappings.Add("PropertyAccessPoints", propertyAccessPointsMap);

            Dictionary<int, int> carriageWayMap = new Dictionary<int, int>
            {
                { 1, 0 },
                { 2, 1 },
                { 3, 2 },
                { 4, 3 },
                { 5, 4 }
            };
            // Add 'CarriageWay' mappings to the field mappings dictionary
            fieldMappings.Add("CarriageWay", carriageWayMap);

            Dictionary<int, int> numberLanesMap = new Dictionary<int, int>
            {
                { 4, 3 },
                { 3, 2 },
                { 6, 5 },
                { 2, 1 },
                { 5, 4 },
                { 1, 0 }
            };

            // Add 'NumberLanes' mappings to the field mappings dictionary
            fieldMappings.Add("NumberLanes", numberLanesMap);
            Dictionary<int, int> medianTypeMap = new Dictionary<int, int>
            {
                { 11, 0 },
                { 14, 1 },
                { 10, 2 },
                { 8, 3 },
                { 9, 4 },
                { 7, 5 },
                { 6, 6 },
                { 5, 7 },
                { 2, 10 },
                { 1, 11 },
                { 12, 12 },
                { 15, 13 },
                { 4, 8 },
                { 3, 9 },
                { 13, 14 }
            };
            // Add 'MedianType' mappings to the field mappings dictionary
            fieldMappings.Add("MedianType", medianTypeMap);
            Dictionary<int, int> laneWidthMap = new Dictionary<int, int>
            {
                { 3, 2 },
                { 2, 1 },
                { 1, 0 }
            };
            // Add 'LaneWidth' mappings to the field mappings dictionary
            fieldMappings.Add("LaneWidth", laneWidthMap);
            Dictionary<int, int> pavedShoulderMap = new Dictionary<int, int>
            {
                { 4, 3 },
                { 3, 2 },
                { 2, 1 },
                { 1, 0 }
            };
            // Add 'PavedShoulder' mappings to the field mappings dictionary
            fieldMappings.Add("PavedShoulder", pavedShoulderMap);
            Dictionary<int, int> roadsideSeverityObjectMap = new Dictionary<int, int>
            {
                { 10, 0 },
                { 11, 1 },
                { 12, 2 },
                { 15, 4 },
                { 5, 6 },
                { 6, 8 },
                { 8, 7 },
                { 9, 9 },
                { 16, 5 },
                { 13, 3 },
                { 14, 11 },
                { 2, 12 },
                { 1, 13 },
                { 4, 15 },
                { 3, 14 },
                { 7, 10 },
                { 17, 16 }
            };

            // Add 'RoadsideSeverityObject' mappings to the field mappings dictionary
            fieldMappings.Add("RoadsideSeverityObject", roadsideSeverityObjectMap);

            Dictionary<int, int> skidResistanceGripMap = new Dictionary<int, int>
            {
                { 5, 0 },
                { 4, 1 },
                { 3, 2 },
                { 2, 3 },
                { 1, 4 }
            };

            // Add 'SkidResistanceGrip' mappings to the field mappings dictionary
            fieldMappings.Add("SkidResistanceGrip", skidResistanceGripMap);

            Dictionary<int, int> schoolZoneWarningMap = new Dictionary<int, int>
            {
                { 3, 1 },
                { 2, 2 },
                { 1, 3 },
                { 4, 0 }
            };

            // Add 'SchoolZoneWarning' mappings to the field mappings dictionary
            fieldMappings.Add("SchoolZoneWarning", schoolZoneWarningMap);


            Dictionary<int, int> schoolZoneSupervisorMap = new Dictionary<int, int>
            {
                { 3, 0 },
                { 2, 1 },
                { 1, 2 },

            };

            // Add 'SchoolZoneWarning' mappings to the field mappings dictionary
            fieldMappings.Add("SchoolZoneSupervisor", schoolZoneSupervisorMap);


            Dictionary<int, int> sidewalkMap = new Dictionary<int, int>
            {
                { 5, 0 },
                { 4, 3 },
                { 3, 4 },
                { 2, 5 },
                { 1, 6 },
                { 7, 1 },
                { 6, 2 }
            };

            // Add 'Sidewalk' mappings to the field mappings dictionary
            fieldMappings.Add("Sidewalk", sidewalkMap);
            Dictionary<int, int> pedestrianCrossingMap = new Dictionary<int, int>
            {
                { 1, 10 },
                { 2, 9 },
                { 3, 8 },
                { 4, 4 },
                { 5, 2 },
                { 6, 1 },
                { 14, 7 },
                { 15, 6 },
                { 16, 5 },
                { 17, 3 },
                { 7, 0 }
            };

            // Add 'PedestrianCrossingInspectedRoad' mappings to the field mappings dictionary
            fieldMappings.Add("PedestrianCrossing", pedestrianCrossingMap);
            Dictionary<int, int> facilitiesMotorisedTwoWheelersMap = new Dictionary<int, int>
            {
                { 6, 0 },
                { 5, 1 },
                { 2, 2 },
                { 1, 5 },
                { 4, 3 },
                { 3, 4 }
            };

            // Add 'FacilitiesMotorisedTwoWheelers' mappings to the field mappings dictionary
            fieldMappings.Add("FacilitiesMotorisedTwoWheelers", facilitiesMotorisedTwoWheelersMap);

            Dictionary<int, int> bicycleFacilityMap = new Dictionary<int, int>
            {
                { 4, 0 },
                { 5, 2 },
                { 3, 3 },
                { 7, 4 },
                { 2, 5 },
                { 1, 6 },
                { 6, 1 }
            };
            // Add 'BicycleFacility' mappings to the field mappings dictionary
            fieldMappings.Add("BicycleFacility", bicycleFacilityMap);

            Dictionary<int, int> gradeMap = new Dictionary<int, int>
            {
                { 5, 0 },
                { 4, 1 },
                { 1, 2 }
            };

            // Add 'Grade' mappings to the field mappings dictionary
            fieldMappings.Add("Grade", gradeMap);
            Dictionary<int, int> intersectionTypeMap = new Dictionary<int, int>
            {
                { 8, 0 },
                { 17, 5 },
                { 7, 1 },
                { 10, 2 },
                { 13, 10 },
                { 4, 3 },
                { 9, 7 },
                { 6, 6 },
                { 3, 4 },
                { 5, 8 },
                { 14, 12 },
                { 2, 9 },
                { 1, 11 },
                { 15, 13 },
                { 16, 14 },
                { 12, 15 }
            };

            // Add 'IntersectionType' mappings to the field mappings dictionary
            fieldMappings.Add("IntersectionType", intersectionTypeMap);



        }

        //Dado um campo, mapeia um indice da especificação iRAP para o indice visual da aplicação
        public static int? MapCoding(string fieldName, int? input)
        {
            if (input is not null)
            {
                if (fieldMappings.ContainsKey(fieldName))
                {
                    Dictionary<int, int> mapping = fieldMappings[fieldName];
                    if (mapping.ContainsKey((int)input))
                    {
                        return mapping[(int)input];
                    }
                }
            }
            Debug.Write("Error Map ->" + fieldName);
            //We should never get here cause I'm only calling this with fieldNames i've defined
            return null;
        }

        // Dado um campo, mapeia de indice visual para um indice da especificação iRAP
        public static int? UnMapCoding(string fieldName, int? mappedValue)
        {
            if (fieldMappings.ContainsKey(fieldName))
            {
                Dictionary<int, int> mappings = fieldMappings[fieldName];
                foreach (var kvp in mappings)
                {
                    if (kvp.Value == mappedValue)
                    {
                        return kvp.Key;
                    }
                }
            }

            // If no mapping found, return the mapped value itself
            Debug.Write("Error UnMap ->" + fieldName);
            return mappedValue;
        }

        /// <summary>
        /// Salva a instância de codificação atual no banco de dados.
        /// 
        /// </summary>
        /// <param name="origin">Codificação Original</param>
        /// <param name="updateCode">Se deve atualizar o campo updateDate ou não</param>
        /// 
        public static Coding SaveRemappedCoding(CodingPageViewModel origin, bool updateCode)
        {
            Coding updatedCoding = dbCtx.Codings.Find(origin.codingId);

            if (updatedCoding.CodingDate == null)
                updatedCoding.CodingDate = DateTime.Now;

            updatedCoding.CarriageWay = UnMapCoding("CarriageWay", origin.CarriageWay);
            updatedCoding.UpgradeCost = UnMapCoding("UpgradeCost", origin.UpgradeCost);
            updatedCoding.MotorcycleObserveredFlow = UnMapCoding("ObservedFlow", origin.MotorcycleFlow);
            updatedCoding.BicycleObservedFlow = UnMapCoding("ObservedFlow", origin.BicycleFlow);
            updatedCoding.PedestrianObservedFlow = UnMapCoding("ObservedFlow", origin.PedestrianRoadFlow);
            updatedCoding.PedestrianObservedAlongRoadDriver = UnMapCoding("ObservedFlow", origin.PedestrianDriverFlow);
            updatedCoding.PedestrianObservedAlongRoadPassenger = UnMapCoding("ObservedFlow", origin.PedestrianPassengerFlow);
            updatedCoding.LandUseDrivers = UnMapCoding("SoilUse", origin.SoilDriver);
            updatedCoding.LandUsePassenger = UnMapCoding("SoilUse", origin.SoilPassenger);
            updatedCoding.AreaType = UnMapCoding("AreaType", origin.AreaType);
            if (origin.currentProject.IsMph == null || origin.currentProject.IsMph == false)
            {
                updatedCoding.SpeedLimit = UnMapCoding("SpeedLimit", origin.CarSpeedLimit);
                updatedCoding.MotorcycleSpeedLimit = UnMapCoding("SpeedLimit", origin.MotorcycleSpeedLimit);
                updatedCoding.TruckSpeedLimit = UnMapCoding("SpeedLimit", origin.TruckSpeedLimit);
            }
            else
            {
                updatedCoding.SpeedLimit = UnMapCoding("SpeedLimitMPH", origin.CarSpeedLimit);
                updatedCoding.MotorcycleSpeedLimit = UnMapCoding("SpeedLimitMPH", origin.MotorcycleSpeedLimit);
                updatedCoding.TruckSpeedLimit = UnMapCoding("SpeedLimitMPH", origin.TruckSpeedLimit);
            }
            updatedCoding.DifferentialSpeeds = origin.DifferentialSpeed == false ? 1 : 2;
            updatedCoding.MedianType = UnMapCoding("MedianType", origin.MedianType);
            updatedCoding.CentrelineRumbleStrips = origin.SoundMiddle == false ? 1 : 2;
            updatedCoding.IntersectionQuality = origin.IntersectionQuality + 1;
            updatedCoding.RoadsideSeverityDriversSideDistance = origin.SeverityDistanceDriver + 1;
            updatedCoding.RoadsideSeverityDriversSideObject = UnMapCoding("RoadsideSeverityObject", origin.SeverityObjectDriver);
            updatedCoding.RoadsideSeverityPassengerSideDistance = origin.SeverityDistancePassenger + 1;
            updatedCoding.RoadsideSeverityPassengerSideObject = UnMapCoding("RoadsideSeverityObject", origin.SeverityObjectPassenger);
            updatedCoding.ShoulderRumbleStrips = origin.SoundLandUse == false ? 1 : 2;
            updatedCoding.PavedShoulderDriversSide = UnMapCoding("PavedShoulder", origin.LandUseWidthDriver);
            updatedCoding.PavedShoulderPassengerSide = UnMapCoding("PavedShoulder", origin.LandUseWidthPassenger);
            updatedCoding.IntersectionType = UnMapCoding("IntersectionType", origin.IntersectionType);
            updatedCoding.IntersectionChannelisation = origin.IntersectionChanneling == false ? 1 : 2;
            updatedCoding.IntersectingRoadVolume = origin.TrafficVolumeTransversal + 1;
            updatedCoding.IntersectionQuality = origin.IntersectionQuality + 1;
            updatedCoding.PropertyAccessPoints = UnMapCoding("PropertyAccessPoints", origin.PropertyAccess);
            updatedCoding.NumberLanes = UnMapCoding("NumberLanes", origin.LaneNumber);
            updatedCoding.LaneWidth = UnMapCoding("LaneWidth", origin.LaneWidth);
            updatedCoding.Curvature = origin.Curvature + 1;
            updatedCoding.QualityCurve = origin.CurvatureQuality + 1;
            updatedCoding.Grade = UnMapCoding("Grade", origin.Incline);
            updatedCoding.RoadCondition = origin.RoadQuality + 1;
            updatedCoding.SkidResistanceGrip = UnMapCoding("SkidResistanceGrip", origin.SkidResistance);
            updatedCoding.Delineation = origin.Delineation == false ? 2 : 1;
            updatedCoding.StreetLighting = origin.PublicLighthing == false ? 1 : 2;
            updatedCoding.PedestrianCrossingInspectedRoad = UnMapCoding("PedestrianCrossing", origin.PedCrossInspected);
            updatedCoding.PedestrianCrossingQuality = origin.CrossingQuality + 1;
            updatedCoding.PedestrianCrossingFacilitiesSideRoad = UnMapCoding("PedestrianCrossing", origin.PedCrossTransversal);
            updatedCoding.PedestrianFencing = origin.PedChanneling == false ? 1 : 2;
            updatedCoding.SpeedManagementTrafficCalming = origin.SpeedControl == false ? 1 : 2;
            updatedCoding.VehicleParking = origin.VehicleParking + 1;
            updatedCoding.SidewalkDriversSide = UnMapCoding("Sidewalk", origin.SideWalkDriver);
            updatedCoding.SidewalkPassengerSide = UnMapCoding("Sidewalk", origin.SideWalkPassenger);
            updatedCoding.ServiceRoad = origin.MarginRoad == false ? 1 : 2;
            updatedCoding.FacilitiesMotorisedTwoWheelers = UnMapCoding("FacilitiesMotorisedTwoWheelers", origin.InfraMotorcycle);
            updatedCoding.BicycleFacility = UnMapCoding("BicycleFacility", origin.InfraBicycle);
            updatedCoding.Roadworks = UnMapCoding("RoadWorks", origin.RoadWork);
            updatedCoding.SightDistance = origin.VisibilityDistance + 1;
            updatedCoding.SchoolZoneWarning = UnMapCoding("SchoolZoneWarning", origin.SchoolWarn);
            updatedCoding.SchoolZoneCrossingSupervisor = UnMapCoding("SchoolZoneSupervisor", origin.SchoolSupervisor);
            updatedCoding.Comments = origin.Comments;
            updatedCoding.Landmark = origin.Landmark;

            if (updateCode)
                updatedCoding.UpdateDate = DateTime.Now;

            dbCtx.SaveChanges();
            return updatedCoding;
        }

        public static CodingImage GenerateCodingImage(String pathCamera, int imageFileName, int cameraType, Coding coding)
        {

            String baseFileName = String.Format("{0:0000000000}{1}",
                imageFileName, ".jpg");


            String camFile = String.Format("{0}{1}{2}",
                pathCamera, Path.DirectorySeparatorChar,
                baseFileName);

            if (camFile != null && File.Exists(camFile))
            {
                CodingImage image = new CodingImage();
                if (coding.CodingId != null)
                    image.CodingId = coding.CodingId.Value;
                image.FileDate = File.GetCreationTime(camFile);
                image.ImageName = baseFileName;
                image.Type = cameraType;

                return image;
            }

            baseFileName = String.Format("{0:00000000}{1}",
                imageFileName, ".jpg");

            camFile = String.Format("{0}{1}{2}",
                pathCamera, Path.DirectorySeparatorChar,
                baseFileName);

            if (camFile != null && File.Exists(camFile))
            {
                CodingImage image = new CodingImage();
                if (coding.CodingId != null)
                    image.CodingId = coding.CodingId.Value;
                image.FileDate = File.GetCreationTime(camFile);
                image.ImageName = baseFileName;
                image.Type = cameraType;

                return image;
            }


            return null;
        }
        public static int RoundToNearestMultipleOfFive(int number)
        {
            return (int)(Math.Round(number / 5.0)) * 5;
        }

        /// <summary>
        /// Function that receives an InputXml and Create the corresponding project, it's codings and codingImages
        /// </summary>
        /// <param name="XmlLog"></param>
        /// <param name="CreatedProject"></param>
        /// <param name="logStartTrechoId"></param>
        /// <param name="logEndTrechoId"></param>
        /// <param name="pathCam1"></param>
        /// <param name="pathCam2"></param>
        /// <param name="pulse"></param>
        /// 
        public static void CreateProjectXml(InputXML XmlLog, Project CreatedProject, int logStartTrechoId, int logEndTrechoId, string pathCam1, string pathCam2, decimal pulse)
        {
            //Log.Debug("Entering CreateProjectXml method");


            List<Coding> codings = new List<Coding>();
            decimal counterSegment = 0; // 0 to 100 
            int counterStation = 0; // increments by 20 whenever counterStationMeters hits 20m
            decimal counterStationMeters = 0;
            int counterSequence = 1;
            decimal counterKm = CreatedProject.KmStart;
            //Descolamento da primeira foto em relação ao 0. Dado pelo odometro trecho
            var DisplacementStartPhoto = XmlLog.logs.Where(l => l.Id == logStartTrechoId).First().Odometro * pulse;
            int counterCorrectStartSegment = 0; // usado para corrigir a posição do inicio do segmento.
            int DisplacementCorrection = 0;
            Coding readCoding = new Coding();

            //Log.Debug("Initialized counters. KmStart: {0}, logStartTrechoId: {1}, logEndTrechoId: {2}, pulse: {3}", counterKm, logStartTrechoId, logEndTrechoId, pulse);
            for (int i = logStartTrechoId; i < logEndTrechoId - 1; i++)
            {



                //Log.Debug("Processing log index: {0}, OdometroTrecho: {1}", i, XmlLog.logs[i].OdometroTrecho);

                // Every 20 meters create a photo until 80m. 0 included
                if ((counterStationMeters >= 20 && counterStation <= 80) || counterStationMeters == 0)
                {
                    //Log.Debug("Attempting to generate image for Camera 1 at OdometroTrecho: {0}", XmlLog.logs[i].OdometroTrecho);
                    //var image = GenerateCodingImage(pathCam1, RoundToNearestMultipleOfFive((int)Math.Round(XmlLog.logs[i].OdometroTrecho + logStartTrechoId)), 1, readCoding);
                    var image = GenerateCodingImage(pathCam1, RoundToNearestMultipleOfFive((int)(XmlLog.logs[i].Odometro)), 1, readCoding);

                    if (image != null)
                    {
                        //Log.Debug("Image generated for Camera 1, Sequence: {0}", counterSequence);
                        image.Sequence = counterSequence;
                        readCoding.ImagesCam.Add(image);
                    }
                    else
                    {
                        Log.Debug("Image not found for Camera 1 at Odometro: {0}", RoundToNearestMultipleOfFive((int)(XmlLog.logs[i].Odometro)));

                    }

                    //Log.Debug("Attempting to generate image for Camera 2 at OdometroTrecho: {0}", XmlLog.logs[i].OdometroTrecho);
                    //image = GenerateCodingImage(pathCam2, RoundToNearestMultipleOfFive((int)Math.Round(XmlLog.logs[i].OdometroTrecho + logStartTrechoId)), 2, readCoding);
                    image = GenerateCodingImage(pathCam2, RoundToNearestMultipleOfFive((int)(XmlLog.logs[i].Odometro)), 2, readCoding);


                    if (image != null)
                    {
                        Log.Debug("Image generated for Camera 2, Sequence: {0}", counterSequence + 6);
                        image.Sequence = counterSequence + 6;
                        readCoding.ImagesCam.Add(image);
                    }

                    counterStation += 20;
                    counterStationMeters = 0;
                    counterSequence++;
                }

                // Create a coding every 100 meters
                if (counterSegment >= 100)
                {
                    Log.Debug("Creating Coding segment at counterSegment: {0}, counterKm: {1}", counterSegment, counterKm);

                    readCoding.ProjectId = CreatedProject.ProjectId.Value;
                    readCoding.CoderName = CreatedProject.CoderName;
                    readCoding.RoadName = CreatedProject.Road;
                    readCoding.Section = CreatedProject.Section;
                    readCoding.RoadSurveyDate = XmlLog.logs[i].DataHora;
                    readCoding.InitialKm = counterKm;
                    readCoding.FinalKm = counterKm + 0.1M;
                    readCoding.Distance = (decimal)Math.Truncate(counterKm);
                    //if (i - ((int)DisplacementStartPhoto - logStartTrechoId) > 0 && DisplacementCorrection == 0)
                    //{
                    //    DisplacementCorrection = (int)DisplacementStartPhoto - logStartTrechoId;
                    //}
                    ////Not the correct way of doing this but since i'm not sure it will not give this exception better do this than crashing.
                    //else
                    //{
                    //    DisplacementCorrection = 0;
                    //}
                    ////Debug.WriteLine($"Ué ->>i:{i - ((int)DisplacementStartPhoto - i)} i - correção{i - ((int)DisplacementStartPhoto - logStartTrechoId)} ddispl-start{(int)DisplacementStartPhoto - logStartTrechoId}");
                    readCoding.Longitude = XmlLog.logs[i - counterCorrectStartSegment].LonLatGPGGA.Item1;
                    readCoding.Latitude = XmlLog.logs[i - counterCorrectStartSegment].LonLatGPGGA.Item2;
                    readCoding.CodingDate = DateTime.Now;
                    readCoding.Comments = "";
                    readCoding.Landmark = "";
                    codings.Add(readCoding);

                    Log.Debug("Added coding segment to list. New list count: {0}", codings.Count);

                    readCoding = new Coding();
                    if (CreatedProject.KmStart > CreatedProject.KmEnd)
                    {
                        counterKm -= 0.1M;
                    }
                    else
                    {
                        counterKm += 0.1M;
                    }
                    counterSegment -= 100;

                    counterStation = 0;
                    counterStationMeters = 0;
                    counterSequence = 1;
                    counterSegment += 1 * pulse;
                    //counterSegment += (decimal)dist;
                    counterCorrectStartSegment = 0;
                    continue;
                }

                counterStationMeters += 1 * pulse;
                counterSegment += 1 * pulse;
                //counterStationMeters += (decimal)dist;
                //counterSegment += (decimal)dist;
                counterCorrectStartSegment++;
            }
            //Tentativa de correção para quando não aparece a foto final.
            if (counterSegment > 5)
            {
                readCoding.ProjectId = CreatedProject.ProjectId.Value;
                readCoding.CoderName = CreatedProject.CoderName;
                readCoding.RoadName = CreatedProject.Road;
                readCoding.Section = CreatedProject.Section;
                readCoding.RoadSurveyDate = XmlLog.logs[logEndTrechoId - counterCorrectStartSegment - 2].DataHora;
                readCoding.InitialKm = counterKm;
                readCoding.FinalKm = counterKm + counterSegment;
                readCoding.Distance = (decimal)Math.Truncate(counterKm);
                readCoding.Longitude = XmlLog.logs[logEndTrechoId - counterCorrectStartSegment - 2].LonLatGPGGA.Item1;
                readCoding.Latitude = XmlLog.logs[logEndTrechoId - counterCorrectStartSegment - 2].LonLatGPGGA.Item2;
                readCoding.CodingDate = DateTime.Now;
                readCoding.Comments = "";
                readCoding.Landmark = "";
                codings.Add(readCoding);

            }
            Log.Debug("Finished processing loop. Total codings to add: {0}", codings.Count);

            try
            {
                Log.Debug("Attempting to add range of codings to database.");
                dbCtx.Codings.AddRange(codings);
            }
            catch (Exception ex)
            {
                Log.Error("Error in CreateProjectXml while adding codings. Exception: {0}", ex.Message);
                Log.Fatal(ex, "AddRange failed");
            }

            try
            {
                Log.Debug("Attempting to save changes to database.");
                dbCtx.SaveChanges();
            }
            catch (Exception ex)
            {
                Log.Error("Error in CreateProjectXml while saving changes. Exception: {0}", ex.Message);
                Log.Fatal(ex, "SaveChanges failed");
            }

            Log.Debug("Exiting CreateProjectXml method");
        }

        //public static void CreateProjectXml(InputXML XmlLog, Project CreatedProject, int logStartTrechoId, int logEndTrechoId, string pathCam1, string pathCam2, decimal pulse)
        //{
        //    List<Coding> codings = new List<Coding>();
        //    decimal counterSegment = 0; // 0 a 100 
        //    int counterStation = 0; // vai acumular de 20 em 20 sempre que counterStationMeters bater 20m
        //    decimal counterStationMeters = 0;
        //    int counterSequence = 1;
        //    decimal counterKm = CreatedProject.KmStart;
        //    Coding readCoding = new Coding();
        //    for (int i = logStartTrechoId; i < logEndTrechoId; i++)
        //    {
        //        // A cada 20 metros cria uma foto. até 80m. 0 incluso
        //        if ((counterStationMeters >= 20 && counterStation <= 80) || counterStationMeters == 0)
        //        {
        //            var image = GenerateCodingImage(pathCam1, RoundToNearestMultipleOfFive((int)Math.Round(XmlLog.logs[i].OdometroTrecho)), 1, readCoding);
        //            if (image != null)
        //            {
        //                image.Sequence = counterSequence;
        //                readCoding.ImagesCam.Add(image);
        //            }
        //            else
        //            {
        //                Debug.WriteLine("Foto não encontrada:" + RoundToNearestMultipleOfFive((int)Math.Round(XmlLog.logs[i].OdometroTrecho)));
        //            }
        //            image = GenerateCodingImage(pathCam2, RoundToNearestMultipleOfFive((int)Math.Round(XmlLog.logs[i].OdometroTrecho)), 2, readCoding);
        //            if (image != null)
        //            {
        //                image.Sequence = counterSequence + 6;
        //                readCoding.ImagesCam.Add(image);
        //            }

        //            counterStation += 20;
        //            counterStationMeters = 0;
        //            counterSequence++;
        //        }
        //        //Cria um coding a cada 100 metros
        //        if (counterSegment >= 100)
        //        {
        //            readCoding.ProjectId = CreatedProject.ProjectId.Value;
        //            readCoding.CoderName = CreatedProject.CoderName;
        //            readCoding.RoadName = CreatedProject.Road;
        //            readCoding.Section = CreatedProject.Section;
        //            readCoding.RoadSurveyDate = XmlLog.logs[i].DataHora;
        //            readCoding.InitialKm = counterKm;
        //            readCoding.FinalKm = counterKm + 0.1M;
        //            readCoding.Distance = (decimal)Math.Truncate(counterKm);
        //            readCoding.Longitude = XmlLog.logs[i].LonLatGPGGA.Item1;
        //            readCoding.Latitude = XmlLog.logs[i].LonLatGPGGA.Item2;
        //            readCoding.CodingDate = DateTime.Now;
        //            codings.Add(readCoding);
        //            readCoding = new Coding();
        //            if (CreatedProject.KmStart > CreatedProject.KmEnd)
        //            {
        //                counterKm -= 0.1M;
        //            }
        //            else
        //            {
        //                counterKm += 0.1M;
        //            }
        //            counterSegment -= 100;

        //            counterStation = 0;
        //            counterStationMeters = 0;
        //            counterSequence = 1;
        //            counterSegment += 1 * pulse;

        //            continue;
        //        }
        //        counterStationMeters += 1 * pulse;
        //        counterSegment += 1 * pulse;
        //    }
        //    //foreach (var coding in codings)
        //    //{
        //    //    //Debug.WriteLine(px++ + " Coding:" + coding.ImagesCam.Count);
        //    //}
        //    try { dbCtx.Codings.AddRange(codings); } catch (Exception ex) { Log.Error("Error CreateProjectXml Catch AddRange(codings)"); Log.Fatal(ex, "Add"); }
        //    try { dbCtx.SaveChanges(); } catch (Exception ex) { Log.Error("Error CreateProjectXml Catch SaveChanges"); Log.Fatal(ex, "Save Error"); }

        //}

        public static void CreateProjectKml(LineString densifiedLineString, Project CreatedProject, int logStartTrechoId, int logEndTrechoId, string pathCam1, string pathCam2, DateTime surveyDate)
        {

            //Total de metros na linestring
            decimal sumDensified = 0;
            for (int i = 0; i < densifiedLineString.Coordinates.Count() - 1; i++)
            {

                var p1 = SphericalMercator.ToLonLat(densifiedLineString.Coordinates[i].X, densifiedLineString.Coordinates[i].Y);
                var p2 = SphericalMercator.ToLonLat(densifiedLineString.Coordinates[i + 1].X, densifiedLineString.Coordinates[i + 1].Y);

                if (!double.IsNaN(p1.lon) && !double.IsNaN(p1.lat) && !double.IsNaN(p2.lon) && !double.IsNaN(p2.lat))
                    sumDensified += (decimal)InputXML.GetVincentyDistance(p1.lat, p1.lon, p2.lat, p2.lon);
                else
                    sumDensified += (decimal)0.888;

            }


            decimal linePulse = sumDensified / densifiedLineString.Coordinates.Count();
            int startLine = (int)Math.Round(logStartTrechoId / linePulse);
            int endLine = (int)Math.Round(logEndTrechoId / linePulse);
            decimal sumMeters = logStartTrechoId; // decide initial value
            List<Coding> codings = new List<Coding>();
            decimal counterSegment = 0; // 0 a 100 4
            int counterStation = 0; // vai acumular de 20 em 20 sempre que counterStationMeters bater 20m
            decimal counterStationMeters = 0;
            int counterSequence = 1;
            decimal counterKm = CreatedProject.KmStart;
            Coding readCoding = new Coding();
            var extensao = Math.Abs(CreatedProject.KmEnd - CreatedProject.KmStart);

            while (sumMeters <= extensao * 1000 + 15 + logStartTrechoId && sumMeters <= densifiedLineString.Coordinates.Count() - 3)
            {
                var p1 = SphericalMercator.ToLonLat(densifiedLineString.Coordinates[(int)sumMeters].X, densifiedLineString.Coordinates[(int)sumMeters].Y);
                var p2 = SphericalMercator.ToLonLat(densifiedLineString.Coordinates[(int)sumMeters + 1].X, densifiedLineString.Coordinates[(int)sumMeters + 1].Y);
                var dist = (decimal)InputXML.GetVincentyDistance(p1.lat, p1.lon, p2.lat, p2.lon);

                // A cada 20 metros cria uma foto. até 80m. 0 incluso
                if ((counterStationMeters >= 20 && counterStation <= 80) || counterStationMeters == 0)
                {
                    var image = GenerateCodingImage(pathCam1, RoundToNearestMultipleOfFive((int)Math.Round(sumMeters)), 1, readCoding);
                    if (image != null)
                    {
                        image.Sequence = counterSequence;
                        readCoding.ImagesCam.Add(image);
                    }
                    else
                    {
                        Debug.WriteLine("Foto não encontrada:" + RoundToNearestMultipleOfFive((int)Math.Round(sumMeters)));
                    }
                    image = GenerateCodingImage(pathCam2, RoundToNearestMultipleOfFive((int)Math.Round(sumMeters)), 2, readCoding);
                    if (image != null)
                    {
                        image.Sequence = counterSequence + 6;
                        readCoding.ImagesCam.Add(image);
                    }

                    counterStation += 20;
                    counterStationMeters = 0;
                    counterSequence++;
                }
                //Cria um coding a cada 100 metros
                if (counterSegment >= 100)
                {
                    readCoding.ProjectId = CreatedProject.ProjectId.Value;
                    readCoding.CoderName = CreatedProject.CoderName;
                    readCoding.RoadName = CreatedProject.Road;
                    readCoding.Section = CreatedProject.Section;
                    readCoding.RoadSurveyDate = surveyDate;
                    readCoding.InitialKm = counterKm;
                    readCoding.FinalKm = counterKm + 0.1M;
                    readCoding.Distance = (decimal)Math.Truncate(counterKm);
                    var LonLat = SphericalMercator.ToLonLat(densifiedLineString.Coordinates[(int)sumMeters - 99].X, densifiedLineString.Coordinates[(int)sumMeters - 99].Y);
                    if (!double.IsNaN(LonLat.lon) && !double.IsNaN(LonLat.lat))
                    {
                        readCoding.Longitude = (decimal)LonLat.lon;
                        readCoding.Latitude = (decimal)LonLat.lat;
                    }

                    readCoding.CodingDate = DateTime.Now;
                    readCoding.Comments = "";
                    readCoding.Landmark = "";
                    codings.Add(readCoding);
                    readCoding = new Coding();
                    if (CreatedProject.KmStart > CreatedProject.KmEnd)
                    {
                        counterKm -= 0.1M;
                    }
                    else
                    {
                        counterKm += 0.1M;
                    }

                    counterSegment -= 100;
                    counterStation = 0;
                    counterStationMeters = 0;
                    counterSequence = 1;

                    //counterSegment += 1 * linePulse;
                    //sumMeters += 1 * linePulse;

                    counterSegment += dist;
                    sumMeters += dist;

                    continue;
                }

                //counterStationMeters += 1 * linePulse;
                //counterSegment += 1 * linePulse;
                //sumMeters += 1 * linePulse;

                counterStationMeters += dist;
                counterSegment += dist;
                sumMeters += dist;
            }


            try { dbCtx.Codings.AddRange(codings); }
            catch (Exception ex)
            { Log.Fatal(ex, "Add"); }
            try { dbCtx.SaveChanges(); }
            catch (Exception ex)
            { Log.Fatal(ex, "Save Error"); }

        }

        public static void CreateProjectPreviousOutput(InputXML xmlLog, string csvPath, Project createdProject, string pathCam1, string pathCam2)
        {
            try
            {
                List<Coding> codings = new List<Coding>();
                var lines = File.ReadAllLines(csvPath);

                List<string> photosAlreadyUsedCam1 = new();
                List<string> photosAlreadyUsedCam2 = new();


                lines = lines.Skip(1).ToArray();
                int i = 0;
                int foundLogStartIndex = 0;

                //for (int k = 0; k < lines.Count() - 1; k++) 
                //{
                //    var fields1 = lines[k].Split(';');
                //    var fields2 = lines[k+1].Split(';');

                //    (decimal, decimal) LonLatActual= (Math.Truncate(Decimal.Parse(fields1[9]) * 100000) / 100000, Math.Truncate(Decimal.Parse(fields1[8]) * 100000) / 100000);
                //    (decimal, decimal) LonLatNext= (Math.Truncate(Decimal.Parse(fields2[9]) * 100000) / 100000, Math.Truncate(Decimal.Parse(fields2[8]) * 100000) / 100000);
                //    var dist = (decimal)InputXML.GetVincentyDistance((double)LonLatActual.Item1, (double)LonLatActual.Item2, (double)LonLatNext.Item1, (double)LonLatNext.Item2);

                //    if (dist < 100)
                //        Debug.WriteLine($" --------------------->{dist}");
                //    else
                //        Debug.WriteLine(dist);


                //}

                //Variaveis usadas para o aviso de erro 
                int ctSegmentsNotIncluded = 0;
                List<string> segmentsWithError = new();
                foreach (var line in lines)
                {
                   

                    var fields = line.Split(';');
                    Coding coding = new Coding();
                    bool foundLogStart = false;
                    (decimal, decimal) LonLatStartSegment = (Math.Truncate(Decimal.Parse(fields[9]) * 100000) / 100000, Math.Truncate(Decimal.Parse(fields[8]) * 100000) / 100000);
                    LogXML logStart = new();
                    decimal errorMargin = 0.00011M; // mais ou menos 8 metros
                    while (!foundLogStart)
                    {
                        if ((xmlLog.logs[i].LonLatGPGGA.Item1 >= LonLatStartSegment.Item1 - errorMargin && xmlLog.logs[i].LonLatGPGGA.Item1 <= LonLatStartSegment.Item1 + errorMargin)
                            && (xmlLog.logs[i].LonLatGPGGA.Item2 >= LonLatStartSegment.Item2 - errorMargin && xmlLog.logs[i].LonLatGPGGA.Item2 <= LonLatStartSegment.Item2 + errorMargin))
                        {
                            logStart = xmlLog.logs[i];
                            foundLogStart = true;
                            foundLogStartIndex = i;
                        }
                        i++;
                        if (i >= xmlLog.logs.Count)
                            break;
                    }


                    if (!foundLogStart)
                    {
                        segmentsWithError.Add(fields[6]);
                        ctSegmentsNotIncluded++;
                        Log.Error($"KM: {fields[6]} from output was unable to match with the current xml coordinates");
                        i = foundLogStartIndex; // Retorna ao ultimo indice achado, talvez não seja necessário, mas aumenta a chance de encontrar outro match de coordenada nova com antiga.
                        continue;
                    }


                    if (foundLogStart)
                    {
                        int segmentLength = 20;

                        for (int j = 0; j < 5; j++)
                        {

                            var image = GenerateCodingImage(pathCam1, RoundToNearestMultipleOfFive(logStart.Odometro + (j * segmentLength)), 1, coding);

                            if (image != null)
                            {
                                if (!photosAlreadyUsedCam1.Contains(image.ImageName))
                                {
                                    image.Sequence = j + 1;
                                    coding.ImagesCam.Add(image);
                                    photosAlreadyUsedCam1.Add(image.ImageName);
                                }
                                else
                                    Log.Error($"Photo {image.ImageName}  was not added because it has already been used in another segment");
                            }
                            else
                            {
                                Debug.WriteLine("Foto não encontrada:" + RoundToNearestMultipleOfFive(logStart.Odometro + j * segmentLength));
                            }
                            image = GenerateCodingImage(pathCam2, RoundToNearestMultipleOfFive(logStart.Odometro + (j * segmentLength)), 2, coding);

                            if (image != null)
                            {
                                if (!photosAlreadyUsedCam2.Contains(image.ImageName))
                                {
                                    image.Sequence = j + 7;
                                    coding.ImagesCam.Add(image);
                                    photosAlreadyUsedCam2.Add(image.ImageName);
                                }
                                else
                                    Log.Error($"Photo {image.ImageName}  was not added because it has already been used in another segment");
                            }

                        }

                        coding.InitialKm = Decimal.Parse(fields[6]);
                        coding.FinalKm = coding.InitialKm + 0.1M;
                        coding.ProjectId = createdProject.ProjectId!.Value;
                        coding.CoderName = createdProject.CoderName;
                        coding.CodingDate = DateTime.Now;
                        coding.RoadSurveyDate = xmlLog.logs[5].DataHora;
                        coding.RoadName = createdProject.Road;
                        coding.Section = createdProject.Section;
                        coding.Distance = Decimal.Parse(fields[6]);
                        coding.Length = Decimal.Parse(fields[7]);
                        coding.Latitude = Decimal.Parse(fields[8]);
                        coding.Longitude = Decimal.Parse(fields[9]);
                        coding.Landmark = fields[10];
                        coding.Comments = fields[11];
                        coding.CarriageWay = int.Parse(fields[12]);
                        coding.UpgradeCost = int.Parse(fields[13]);
                        coding.MotorcycleObserveredFlow = int.Parse(fields[14]);
                        coding.BicycleObservedFlow = int.Parse(fields[15]);
                        coding.PedestrianObservedFlow = int.Parse(fields[16]);
                        coding.PedestrianObservedAlongRoadDriver = int.Parse(fields[17]);
                        coding.PedestrianObservedAlongRoadPassenger = int.Parse(fields[18]);
                        coding.LandUseDrivers = int.Parse(fields[19]);
                        coding.LandUsePassenger = int.Parse(fields[20]);
                        coding.AreaType = int.Parse(fields[21]);
                        coding.SpeedLimit = int.Parse(fields[22]);
                        coding.MotorcycleSpeedLimit = int.Parse(fields[23]);
                        coding.TruckSpeedLimit = int.Parse(fields[24]);
                        coding.DifferentialSpeeds = int.Parse(fields[25]);
                        coding.MedianType = int.Parse(fields[26]);
                        coding.CentrelineRumbleStrips = int.Parse(fields[27]);
                        coding.RoadsideSeverityDriversSideDistance = int.Parse(fields[28]);
                        coding.RoadsideSeverityDriversSideObject = int.Parse(fields[29]);
                        coding.RoadsideSeverityPassengerSideDistance = int.Parse(fields[30]);
                        coding.RoadsideSeverityPassengerSideObject = int.Parse(fields[31]);
                        coding.ShoulderRumbleStrips = int.Parse(fields[32]);
                        coding.PavedShoulderDriversSide = int.Parse(fields[33]);
                        coding.PavedShoulderPassengerSide = int.Parse(fields[34]);
                        coding.IntersectionType = int.Parse(fields[35]);
                        coding.IntersectionChannelisation = int.Parse(fields[36]);
                        coding.IntersectingRoadVolume = int.Parse(fields[37]);
                        coding.IntersectionQuality = int.Parse(fields[38]);
                        coding.PropertyAccessPoints = int.Parse(fields[39]);
                        coding.NumberLanes = int.Parse(fields[40]);
                        coding.LaneWidth = int.Parse(fields[41]);
                        coding.Curvature = int.Parse(fields[42]);
                        coding.QualityCurve = int.Parse(fields[43]);
                        coding.Grade = int.Parse(fields[44]);
                        coding.RoadCondition = int.Parse(fields[45]);
                        coding.SkidResistanceGrip = int.Parse(fields[46]);
                        coding.Delineation = int.Parse(fields[47]);
                        coding.StreetLighting = int.Parse(fields[48]);
                        coding.PedestrianCrossingInspectedRoad = int.Parse(fields[49]);
                        coding.PedestrianCrossingQuality = int.Parse(fields[50]);
                        coding.PedestrianCrossingFacilitiesSideRoad = int.Parse(fields[51]);
                        coding.PedestrianFencing = int.Parse(fields[52]);
                        coding.SpeedManagementTrafficCalming = int.Parse(fields[53]);
                        coding.VehicleParking = int.Parse(fields[54]);
                        coding.SidewalkDriversSide = int.Parse(fields[55]);
                        coding.SidewalkPassengerSide = int.Parse(fields[56]);
                        coding.ServiceRoad = int.Parse(fields[57]);
                        coding.FacilitiesMotorisedTwoWheelers = int.Parse(fields[58]);
                        coding.BicycleFacility = int.Parse(fields[59]);
                        coding.Roadworks = int.Parse(fields[60]);
                        coding.SightDistance = int.Parse(fields[61]);
                        if (!string.IsNullOrWhiteSpace(fields[62]))
                            coding.VehicleFlow = int.Parse(fields[62]);
                        if (!string.IsNullOrWhiteSpace(fields[63]))
                            coding.MotorcyclePercentual = int.Parse(fields[63]);
                        if (!string.IsNullOrWhiteSpace(fields[64]))
                            coding.PedestrianPeakHourFlowAcrossRoad = int.Parse(fields[64]);
                        if (!string.IsNullOrWhiteSpace(fields[65]))
                            coding.PedestrianPeakHourFlowAlongRoadDriver = int.Parse(fields[65]);
                        if (!string.IsNullOrWhiteSpace(fields[66]))
                            coding.PedestrianPeakHourFlowAlongRoadPassenger = int.Parse(fields[66]);
                        if (!string.IsNullOrWhiteSpace(fields[67]))
                            coding.BicyclePeakHourlyFlow = int.Parse(fields[67]);
                        if (!string.IsNullOrWhiteSpace(fields[68]))
                            coding.OperatingSpeed85thPercentile = int.Parse(fields[68]);
                        if (!string.IsNullOrWhiteSpace(fields[69]))
                            coding.OperatingSpeedMean = int.Parse(fields[69]);
                        if (!string.IsNullOrWhiteSpace(fields[70]))
                            coding.RoadsCarsRead = int.Parse(fields[70]);
                        if (!string.IsNullOrWhiteSpace(fields[71]))
                            coding.CarStarRatingPolicyTarget = int.Parse(fields[71]);
                        if (!string.IsNullOrWhiteSpace(fields[72]))
                            coding.MotorcycleStarRatingPolicyTarget = int.Parse(fields[72]);
                        if (!string.IsNullOrWhiteSpace(fields[73]))
                            coding.PedestrianStarRatingPolicyTarget = int.Parse(fields[73]);
                        if (!string.IsNullOrWhiteSpace(fields[74]))
                            coding.BicycleStarRatingPolicyTarget = int.Parse(fields[74]);
                        coding.AnnualFatalityGrowthMultiplier = int.Parse(fields[75]);
                        coding.SchoolZoneWarning = int.Parse(fields[76]);
                        coding.SchoolZoneCrossingSupervisor = int.Parse(fields[77]);
                        codings.Add(coding);
                    }
                }
                if (codings.Count > 0)
                {
                    if (segmentsWithError.Count > 0)
                    {
                        DialogHelper.ShowOkDialog("Error", "", nonLocalizedContent:
                            $" {Localizer.Localizer.Instance["ErrorPreviousOutput"]}: \n" +
                            $" {Localizer.Localizer.Instance["AffectedKM"]}: {segmentsWithError.First()} -- {segmentsWithError.Last()} \n" +
                            $" {Localizer.Localizer.Instance["NumAffected"]}: {ctSegmentsNotIncluded}");
                    }
                    try { dbCtx.Codings.AddRange(codings); }
                    catch (Exception ex)
                    { Log.Fatal(ex, "Add"); }
                    try { dbCtx.SaveChanges(); }
                    catch (Exception ex)
                    { Log.Fatal(ex, "Save Error"); }
                }
                else
                {
                    DialogHelper.ShowOkDialog("Error", "NoProjectCreated");
                    dbCtx.Remove(createdProject);
                    dbCtx.SaveChanges();
                }
            }
            catch (Exception ex)
            {
                Log.Debug(ex, "Erro");
            }
        }


        public static async Task<List<Coding>> LoadProjectCodings(int projId)
        {
            return await dbCtx.Codings.Where(p => p.ProjectId == projId).ToListAsync();
            //return await (from coding in dbCtx.Codings
            //        where projId == coding.ProjectId
            //        select coding).ToListAsync();
        }

        public static Project GetProjectById(int projectId)
        {
            return dbCtx.Projects.Find(projectId);
        }

        /// <summary>
        /// Finds codings by project ID, with an option to filter only codified entries.
        /// </summary>
        /// <param name="projectId">The ID of the project.</param>
        /// <param name="onlyCodified">If true, only include codings that have been updated.</param>
        /// <returns>A list of codings matching the criteria.</returns>
        public static List<Coding> FindCodingsByProject(int projectId, bool onlyCodified)
        {
            var query = from coding in dbCtx.Codings
                        where coding.ProjectId == projectId
                        select coding;

            if (onlyCodified)
            {
                query = query.Where(coding => coding.UpdateDate != null);
            }

            return query.ToList();
        }

        public static async Task<List<CodingImage>> FindCodingImagesByProjectId(int projID)
        {
            var codingList = await LoadProjectCodings(projID);
            List<int?> codingIDList = codingList.Select(c => c.CodingId).ToList();

            return await (from codingImage in CrudHelper.dbCtx.CodingImages
                          where codingIDList.Any(id => id == codingImage.CodingId)
                          select codingImage).ToListAsync();
        }

        public static bool CanConnectDB()
        {
            return dbCtx.Database.CanConnect();
        }
    }
}
