﻿using System;
using System.Collections.Generic;
using TuiForms.DotNet;
using TuiForms.Graphics;
using TuiForms.Themes;

namespace TuiForms.Controls
{
    internal partial class AnimatedLabel : ILabel
    {
        public ConsoleColor? BackgroundColor => Label.BackgroundColor;

        public IForm Form => Label.Form;

        public ConsoleColor? ForegroundColor => Label.ForegroundColor;

        public bool Invalid { get => Label.Invalid; set => Label.Invalid = value; }

        public int? LabelWidth => Label.LabelWidth;

        public ILayoutData LayoutData { get => Label.LayoutData; set => Label.LayoutData = value; }
        public string[] Lines { set => Label.Lines = value; }

        public IContainer ParentContainer { get => Label.ParentContainer; set => Label.ParentContainer = value; }

        public ConsolePosition Position { get => Label.Position; set => Label.Position = value; }
        public ConsoleSize PreferredSize { get => Label.PreferredSize; set => Label.PreferredSize = value; }
        //public IComponentRenderer Renderer { get; }
        public IComponentRenderer Renderer { get => Label.Renderer; }
        public ConsoleSize Size { get => Label.Size; set => Label.Size = value; }
        public string Text { get => Label.Text; set => Label.Text = value; }

        public TuiDesktop TuiDeskTop => Label.TuiDeskTop;

        public ThemeDefinition ThemeDefinition { get => Label.ThemeDefinition; }

        public ConsoleSize GetBounds(string[] lines, ConsoleSize currentBounds)
        {
            return Label.GetBounds(lines, currentBounds);
        }

        public bool HasParent(IContainer parent)
        {
            return Label.HasParent(parent);
        }

        public void Invalidate()
        {
            Label.Invalidate();
        }

        public bool IsInside(IContainer container)
        {
            return Label.IsInside(container);
        }

        public void OnRemoved(IContainer container)
        {
            Label.ParentContainer = null;
        }

        public Label SetBackgroundColor(ConsoleColor? backgroundColor)
        {
            return Label.SetBackgroundColor(backgroundColor);
        }

        public Label SetForegroundColor(ConsoleColor? foregroundColor)
        {
            return Label.SetForegroundColor(foregroundColor);
        }

        public Label SetLabelWidth(int? labelWidth)
        {
            return Label.SetLabelWidth(labelWidth);
        }

        public string[] SplitIntoMultipleLines(string text)
        {
            return Label.SplitIntoMultipleLines(text);
        }

        public ConsolePosition GetPositionOnForm(ConsolePosition position)
        {
            return Label.GetPositionOnForm(position);
        }

        public ConsolePosition GetPositionOnConsole(ConsolePosition position)
        {
            return Label.GetPositionOnConsole(position);
        }
    }
    /// <summary>
    /// This is a special label that contains not just a single text to display but a number of frames that are cycled
    /// through. The class will manage a timer on its own and ensure the label is updated and redrawn. There is a static
    /// helper method available to create the classic "spinning bar": {@code createClassicSpinningLine()}
    /// </summary>
    internal partial class AnimatedLabel //: Label
    {
        public Label Label { get; }
        private IEventProvider EventProvider { get; }

        private readonly IList<string[]> _frames;
        private ConsoleSize _combinedMaximumPreferredSize;
        private int _currentFrame;

        /// <summary>
        /// Creates a new animated label, initially set to one frame. You will need to add more frames and call
        /// {@code startAnimation()} for this to start moving.
        /// </summary>
        /// <param name="firstFrameText"> The content of the label at the first frame </param>
        public AnimatedLabel(string[] frames, IEventProvider eventProvider)
        {
            Label = new Label(frames[0]);
            _currentFrame = 0;
            _combinedMaximumPreferredSize = new ConsoleSize(0, 0);

            _frames = new List<string[]>();
            foreach(string frame in frames)
            {
                AddFrame(frame);
            }
            
            string[] lines = SplitIntoMultipleLines(frames[0]);
            _frames.Add(lines);
            EnsurePreferredSize(lines);

            EventProvider = eventProvider;
            EventProvider.OnAnimateTiming += Timer_OnAnimateTiming;
        }

        public ConsoleSize CalculatePreferredSize()
        {
            return Label.Renderer.GetPreferredSize(this).Max(_combinedMaximumPreferredSize);
        }

        /// <summary>
        /// Adds one more frame at the end of the list of frames </summary>
        /// <param name="text"> Text to use for the label at this frame </param>
        /// <returns> Itself </returns>
        public AnimatedLabel AddFrame(string text)
        {
            string[] lines = SplitIntoMultipleLines(text);
            _frames.Add(lines);
            EnsurePreferredSize(lines);
            return this;
        }

        private void EnsurePreferredSize(string[] lines)
        {
            _combinedMaximumPreferredSize = _combinedMaximumPreferredSize.Max(GetBounds(lines, _combinedMaximumPreferredSize));
        }

        /// <summary>
        /// Advances the animated label to the next frame. You normally don't need to call this manually as it will be done
        /// by the animation.
        /// </summary>
        private void Timer_OnAnimateTiming(object sender, EventArgs e)
        {
            _currentFrame++;
            if (_currentFrame >= _frames.Count)
            {
                _currentFrame = 0;
            }
            Label.Lines = _frames[_currentFrame];
            Invalidate();
        }
    }
}
