using System.ComponentModel;
using Evos.Api.Repository;
using Evos.Api.Samples.Infrastructure;
using Evos.ModelsRepository.BasicData;
using Evos.ModelsRepository.Connection;
using Evos.ModelsRepository.DataProvider.TransactionDescriptions;
using Evos.ModelsRepository.Products;
using Evos.CommonValueObjects;
using Evos.Repeater.GraphicCutting;
using Nanosoft.Common.InternationalSystemOfUnits.Access;
using Nanosoft.Common.ValueObjects.Common;

namespace Evos.Api.Samples
{
    /// Низкоуровневое API: повторитель (repeater) — продукт, который тиражирует геометрию
    /// другого продукта вдоль оси с заданным шагом. Так строят воздуховоды, лотки, трубы.
    [Localizable(false)]
    public static class RepeaterSample
    {
        public static void Run()
        {
            using var sampleRepository = SampleRepository.CreateNew();

            sampleRepository.Repository.TryExecuteTransaction(
                RepeatSegmentAlongAxis,
                new TransactionInfo(TransactionDescription.Default, TransactionMode.Default));
        }

        /// Повторитель создаётся только внутри write-транзакции: он добавляет в репозиторий
        /// новый продукт-повторитель, ссылающийся на тиражируемый сегмент.
        private static void RepeatSegmentAlongAxis(RepositoryWriteAccess writeAccess)
        {
            var segment = CreateRepetitiveSegment(writeAccess);
            var step = Length.CreateOrThrow(ValueObjectsAccess.Meter.FromMeter(0.5));

            var repeaterFactory = new ApiRepeaterFactory();

            // FindOrCreateRepeater идемпотентен: для той же пары «сегмент + шаг» он не плодит
            // новый повторитель, а возвращает уже существующий. Флаг говорит, что именно произошло.
            var repeater = repeaterFactory.FindOrCreateRepeater(
                writeAccess,
                segment,
                step,
                new MeshCuttingAdditionalSettings(),
                out var created);

            SampleCheck.That(created, "первый вызов должен создать повторитель");
            Console.WriteLine($"Повторитель создан, шаг {step}");

            var sameRepeater = repeaterFactory.FindOrCreateRepeater(
                writeAccess,
                segment,
                step,
                new MeshCuttingAdditionalSettings(),
                out var createdAgain);

            SampleCheck.That(!createdAgain, "повторный вызов не должен создавать второй повторитель");
            SampleCheck.That(ReferenceEquals(sameRepeater, repeater), "должен вернуться тот же повторитель");

            var repeaters = repeaterFactory.FindRepeaters(writeAccess, segment).ToList();
            Console.WriteLine($"Повторителей у сегмента: {repeaters.Count}");
            SampleCheck.That(repeaters.Count == 1, "у сегмента должен быть ровно один повторитель");
        }

        /// Тиражируемый сегмент — обычный продукт, которому нужны два предопределённых семейства:
        /// геометрия (IStaticGraphic) и её положение в пространстве (ISpaceGraphicModel).
        /// Без них повторитель строить не из чего, и фабрика бросит исключение.
        /// Семейство добавляют пустым, значения кладут через EditableFacade — и только в транзакции.
        private static IProduct CreateRepetitiveSegment(RepositoryWriteAccess writeAccess)
        {
            var segment = writeAccess.NewProduct();

            var graphic = segment.AddFamilyOrThrow<IStaticGraphic>();
            segment.AddFamilyOrThrow<ISpaceGraphicModel>();

            // Геометрия сегмента — треугольные грани: вершины плюс индексы, ссылающиеся на них.
            graphic.EditableFacade()
                .SetProperty(family => family.Vertices, CreateBoxVertices())
                .SetProperty(family => family.Faces, CreateBoxFaces())
                .SetProperty(family => family.Color, Color.FromSRgb(160, 160, 170, 255, false));

            return segment;
        }

        /// Восемь вершин прямоугольного бруска 0.4 × 0.4 м длиной 0.5 м — заготовка воздуховода.
        private static IReadOnlyList<Position3D> CreateBoxVertices()
        {
            const double half = 0.2;
            const double length = 0.5;

            return new List<Position3D>
            {
                CreatePosition(0, -half, -half),
                CreatePosition(0, half, -half),
                CreatePosition(0, half, half),
                CreatePosition(0, -half, half),
                CreatePosition(length, -half, -half),
                CreatePosition(length, half, -half),
                CreatePosition(length, half, half),
                CreatePosition(length, -half, half)
            };
        }

        /// Двенадцать треугольников: по два на каждую из шести граней бруска.
        private static IReadOnlyList<int> CreateBoxFaces()
        {
            return new List<int>
            {
                0, 1, 2, 0, 2, 3,
                4, 6, 5, 4, 7, 6,
                0, 4, 5, 0, 5, 1,
                1, 5, 6, 1, 6, 2,
                2, 6, 7, 2, 7, 3,
                3, 7, 4, 3, 4, 0
            };
        }

        private static Position3D CreatePosition(double x, double y, double z)
        {
            return new Position3D(
                ValueObjectsAccess.Meter.FromMeter(x),
                ValueObjectsAccess.Meter.FromMeter(y),
                ValueObjectsAccess.Meter.FromMeter(z));
        }
    }
}
