using AsbCloudApp.Data;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudDb.Model;
using AsbCloudDb.Model.WITS;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudInfrastructure.Services.Trajectory
{

    public class TrajectoryVisualizationService : ITrajectoryVisualizationService
    {
        private readonly IPlannedTrajectoryRepository plannedRepository;
        private readonly IFactualTrajectoryRepository factualRepository;

        public TrajectoryVisualizationService(IPlannedTrajectoryRepository plannedRepository, IFactualTrajectoryRepository factualRepository)
        {
            this.plannedRepository = plannedRepository;
            this.factualRepository = factualRepository;
        }

        /// <summary>
        /// Получение плановой и фактической траектории по скважине
        /// </summary>
        /// <param name="idWell">ключ скважины</param>
        /// <param name="token"></param>
        /// <returns></returns>
        public async Task<PlanFactBase<IEnumerable<TrajectoryVisualizationDto>>> GetTrajectoryAsync(int idWell, CancellationToken token)
        {
            var result = new PlanFactBase<IEnumerable<TrajectoryVisualizationDto>>();

            var geoPlanCoordinates = (await plannedRepository.GetAsync(idWell, token))
               .Select(coord => new TrajectoryDto(coord.WellboreDepth, coord.ZenithAngle, coord.AzimuthGeo))
               .ToArray();
            var geoFactCoordinates = (await factualRepository.GetAsync(idWell, token))
              .Select(coord => new TrajectoryDto(coord.Deptsvym, coord.Svyinc, coord.Svyazc))
              .ToArray();

            result.Plan = GetTrajectoryVisualisation(geoPlanCoordinates);
            result.Fact = GetTrajectoryVisualisation(geoFactCoordinates);

            return result;
        }

        /// <summary>
        /// Формирует список координат для визуализации трактории 3D
        /// </summary>
        /// <param name="geoCoordinates"></param>
        /// <returns></returns>
        private List<TrajectoryVisualizationDto> GetTrajectoryVisualisation(TrajectoryDto[] geoCoordinates)
        {
            var cartesianCoordinates = new List<TrajectoryVisualizationDto>(geoCoordinates.Length) {
                new (),
            };

            for (var i = 1; i < geoCoordinates.Length; i++)
            {
                var intervalGeoParams = geoCoordinates[i - 1];
                var deltaWellLength = geoCoordinates[i].WellboreDepth - intervalGeoParams.WellboreDepth;

                if (intervalGeoParams.ZenithAngle != null && intervalGeoParams.AzimuthGeo != null && deltaWellLength != null) {
                    
                    var projectionLengthToXYSurface = deltaWellLength.Value * Math.Sin(intervalGeoParams.ZenithAngle.Value * Math.PI / 180);

                    var dz = deltaWellLength.Value * Math.Cos(intervalGeoParams.ZenithAngle.Value * Math.PI / 180);
                    var dx = projectionLengthToXYSurface * Math.Sin(intervalGeoParams.AzimuthGeo.Value * Math.PI / 180);
                    var dy = projectionLengthToXYSurface * Math.Cos(intervalGeoParams.AzimuthGeo.Value * Math.PI / 180);

                    var preCoordinates = cartesianCoordinates[i - 1];
                    var coordinates = new TrajectoryVisualizationDto
                    {
                        Z = preCoordinates.Z + dz,
                        X = preCoordinates.X + dx,
                        Y = preCoordinates.Y + dy,
                    };

                    cartesianCoordinates.Add(coordinates);
                }               
            }

            return cartesianCoordinates;
        }
    }

    /// <summary>
    /// Класс для формирования визуализации траектории 3D
    /// </summary>
    internal class TrajectoryDto
    {
        /// <summary>
        /// Глубина ствола
        /// </summary>
        public double? WellboreDepth { get; }

        /// <summary>
        /// Зенитный угол
        /// </summary>
        public double? ZenithAngle { get; }

        /// <summary>
        /// Азимут географиеский
        /// </summary>
        public double? AzimuthGeo { get; }

        public TrajectoryDto(double? wellboreDepth, double? zenithAngle, double? azimuthGeo)
        {
            WellboreDepth = wellboreDepth;
            ZenithAngle = zenithAngle;
            AzimuthGeo = azimuthGeo;
        }
    }
}