using System;
using System.Collections.Generic;
using System.Linq;
using Quadarax.Foundation.Common.Console;
using Quadarax.Foundation.Common.Value;
using Quadarax.Foundation.QConsole.Argument;

namespace Quadarax.Foundation.QConsole.Command.Base
{
    public abstract class AbstractSelectionCommand : AbstractListCommand
    {
        #region *** Private fields ***
        private int _maxSelections;
        private int _minSelections;
        private IList<ISelectionEntry> _selectedItems;
        
        #endregion

        #region *** Properties ***
        protected abstract bool IsStoredToGlobal { get; } 
        protected int MaxSelections => _maxSelections;
        protected int MinSelections => _minSelections;
        public ISelectionEntry[] SelectedItems => _selectedItems.ToArray();
        #endregion

        #region *** Constructors ***
        protected AbstractSelectionCommand(Engine engine,int minSelection, int maxSelection) : base(engine)
        {
            if (maxSelection==0)
                throw new ArgumentOutOfRangeException(nameof(maxSelection),$"Max selections value must be bigger than zero in command '{Name}'.");
            if (minSelection==0)
                throw new ArgumentOutOfRangeException(nameof(minSelection),$"Min selections value must be bigger than zero in command '{Name}'.");
            if (minSelection > maxSelection)
                throw new ArgumentOutOfRangeException(nameof(maxSelection),$"Max selections value must be bigger than Min selection value in command '{Name}'.");

            _maxSelections = maxSelection;
            _minSelections = minSelection;
            _selectedItems = new List<ISelectionEntry>();
        }
        #endregion

        #region *** Public Operations ***
        

        public void ClearSelected()
        {
            _selectedItems.Clear();
            WriteDebugInfo($"Command '{Name}' clears selected items.");
        }
        #endregion

        #region *** Protected Overrides ***
        protected override IEnumerable<AbstractArgument> OnSetupArguments()
        {
            var arguments = base.OnSetupArguments().ToList();
            arguments.AddRange(
            new []
            {
                new FlagArgument(Constants.Arguments.Selection.Code,
                    Constants.Arguments.Selection.Description,
                    Constants.Arguments.Selection.Hint, false)
            });
            return arguments;
        }

        protected override Result EndExecute(Result incommingResult)
        {
            var result = base.EndExecute(incommingResult);
            if (!result.IsSuccess)
                return result;

            ClearSelected();
            var nCnt = Items.Count();
            var selectionEnabled = GetArgument(Constants.Arguments.Selection.Code);
            if ((bool) selectionEnabled.Value.Get() == false)
                return result;
            using (var writer = new ConsoleWriter(ConsoleColor.White))
            {
                using (var writerCyan = new ConsoleWriter(ConsoleColor.Cyan))
                {
                    writer.WriteLine($"Select ordinal values separated by '{Engine.Configuration.CharacterSelectionInputValueSeparator}'...");
                    writer.WriteLine($"Must be selected at least {MinSelections} ordinals and maximal {MaxSelections} ordinals.");
                    writerCyan.Write($"[1-{nCnt}]>");
                    var ordinalString = writerCyan.ReadLine();
                    var ordinals = ordinalString.Split(new[] {Engine.Configuration.CharacterSelectionInputValueSeparator}, StringSplitOptions.RemoveEmptyEntries);
                    
                    if (!(MinSelections<=ordinals.Length && ordinals.Length <= MaxSelections))
                            throw new Exception($"Number of ordinals must be in interval <{MinSelections};{MaxSelections}>. Current number of ordinals is {ordinals.Length}.");

                    foreach (var ordinal in ordinals.Select(x=>int.Parse(x)))
                    {
                        if (ordinal<0 || ordinal > nCnt)
                            throw new Exception($"Ordinal {ordinal} is out of scope interval <1;{nCnt}>.");
                        _selectedItems.Add(GetItem(ordinal-1));
                    }

                    writer.WriteLine($"Selected {_selectedItems.Count} ordinals.");

                    if (IsStoredToGlobal)
                        Engine.AppendSelection(_selectedItems.ToArray());
                }
            }

            return result;
        }

        protected void EnableDefaultSelectionMode()
        {
            var argSelectionMode = GetArgument(Constants.Arguments.Selection.Code);
            argSelectionMode.DefaultValue.Set(true);
            argSelectionMode.SetValueAsDefault();
        }
        protected void DisableDefaultSelectionMode()
        {
            var argSelectionMode = GetArgument(Constants.Arguments.Selection.Code);
            argSelectionMode.DefaultValue.Set(false);
            argSelectionMode.SetValueAsDefault();
        }

        #endregion

        #region *** Private Operations ***

        #endregion

    }
}
