using BoFilTest.Domain;
using BoFilTest.Utils;
using NPOI.SS.UserModel;
using Optional;
using Optional.Unsafe;
using System;
using System.Collections.Generic;

namespace BoFilTest.Export
{
    class ConfiguredExporter
    {
        private readonly ExcelTemplate template;
        private readonly ExportConfigSection config;
        private readonly string testNumber;

        public ConfiguredExporter(ExportConfigSection config, string testNumber)
        {
            this.config = config;
            this.testNumber = testNumber;
            this.template = new ExcelTemplate(config.TemplatePath(), config.TemplateSheet());
        }

        public ICell Cell(string key)
        {
            return template.GetOrCreateCell(config.CellFor(key));
        }

        public void Set(string key, FilterCakeConsistency value)
        {
            Set(key, CakeProperties.FilterCakeConsistencyNames.NameFor(value));
        }

        public void Set(string key, FilterCakeCracking value)
        {
            Set(key, CakeProperties.FilterCakeCrackingNames.NameFor(value));
        }

        public void Set(string key, EdgeDissolution value)
        {
            Set(key, CakeProperties.EdgeDissolutionNames.NameFor(value));
        }

        public void Set(string key, Option<double> value)
        {
            value.MatchSome(x => Set(key, x));
        }

        public void Set(string key, TimeSpan value)
        {
            Cell(key).SetCellValue(value.TotalSeconds);
        }

        public void Set(string key, string value)
        {
            Cell(key).SetCellValue(value);
        }

        public void Set(string key, double value)
        {
            Cell(key).SetCellValue(value);
        }

        public void Set(string key, double? value)
        {
            if (value.HasValue)
                Cell(key).SetCellValue(value.Value);
        }

        public void Set(string key, DateTime value)
        {
            ICell cell = Cell(key);
            template.SetDateStyleOn(cell);
            cell.SetCellValue(value);
        }

        public void Set(string key, bool value)
        {
            Cell(key).SetCellValue(value);
        }

        public void SetBelow<T>(string key, IEnumerable<Option<T>> data, Action<T, ICell> applier)
        {
            SetBelowInternal(key, data, (optionalValue, cell) =>
            {
                if (optionalValue.HasValue)
                    applier(optionalValue.ValueOrFailure(), cell);
            });
        }

        public void SetBelow<T>(string key, IEnumerable<T> data, Action<T, ICell> applier)
        {
            SetBelowInternal(key, data, applier);
        }

        private void SetBelowInternal<T>(string key, IEnumerable<T> data, Action<T, ICell> applier)
        {
            var startCoordinate = config.CellFor(key);
            int rowOffset = 0;
            foreach (var element in data)
            {
                var coordinate = Tuple.Create(startCoordinate.Item1, startCoordinate.Item2 + rowOffset);
                applier(element, template.GetOrCreateCell(coordinate));
                rowOffset++;
            }
        }

        public string Write(TranslationKey filenameKey)
        {
            return template.Write(config.TargetPath(), filenameKey, testNumber);
        }
    }
}
