using NLog;
using SFML.System;
using SkiaSharp;
using System;
using System.Collections.Frozen;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Spine.Exporters
{
    /// <summary>
    /// 多帧画面导出基类, 可以获取连续的帧序列
    /// </summary>
    public abstract class VideoExporter : BaseExporter
    {
        public VideoExporter(uint width, uint height) : base(width, height) { }
        public VideoExporter(Vector2u resolution) : base(resolution) { }

        /// <summary>
        /// 导出时长
        /// </summary>
        public float Duration 
        { 
            get => _duration; 
            set
            {
                if (value < 0)
                {
                    _logger.Warn("Omit invalid duration: {0}", value);
                    return;
                }
                _duration = value;
            }
        }
        protected float _duration = 0;

        /// <summary>
        /// 帧率
        /// </summary>
        public float Fps
        {
            get => _fps;
            set
            {
                if (value <= 0)
                {
                    _logger.Warn("Omit invalid fps: {0}", value);
                    return;
                }
                _fps = value;
            }
        }
        protected float _fps = 24;

        public float Speed
        {
            get => _speed;
            set
            {
                if (_speed <= 0)
                {
                    _logger.Warn("Omit invalid speed: {0}", value);
                    return;
                }
                _speed = value;
            }
        }
        protected float _speed = 1f;

        /// <summary>
        /// 是否保留最后一帧
        /// </summary>
        public bool KeepLast { get => _keepLast; set => _keepLast = value; }
        protected bool _keepLast = true;

        /// <summary>
        /// 获取总帧数
        /// </summary>
        public int GetFrameCount()
        {
            var delta = 1f / _fps;
            var total = (int)(_duration * _fps); // 完整帧的数量

            var deltaFinal = _duration - delta * total; // 最后一帧时长
            var final = _keepLast && deltaFinal > 1e-3 ? 1 : 0;

            var frameCount = 1 + total + final; // 所有帧的数量 = 起始帧 + 完整帧 + 最后一帧
            return frameCount;
        }

        /// <summary>
        /// 生成帧序列, 用于导出帧序列
        /// </summary>
        protected IEnumerable<SFMLImageVideoFrame> GetFrames(SpineObject[] spines)
        {
            float delta = 1f / _fps;
            int total = (int)(_duration * _fps); // 完整帧的数量
            bool hasFinal = _keepLast && (_duration - delta * total) > 1e-3;

            // 导出首帧
            var firstFrame = GetFrame(spines);
            yield return firstFrame;

            // 导出完整帧
            for (int i = 0; i < total; i++)
            {
                foreach (var spine in spines) spine.Update(delta * _speed);
                yield return GetFrame(spines);
            }

            // 导出最后一帧
            if (hasFinal)
            {
                // XXX: 此处还是按照完整的一帧时长进行更新, 也许可以只更新准确的最后一帧时长
                foreach (var spine in spines) spine.Update(delta * _speed); 
                yield return GetFrame(spines);
            }
        }

        /// <summary>
        /// 生成帧序列, 支持中途取消和进度输出, 用于动图视频等单个文件输出
        /// </summary>
        protected IEnumerable<SFMLImageVideoFrame> GetFrames(SpineObject[] spines, string output, CancellationToken ct)
        {
            int frameCount = GetFrameCount();
            int frameIdx = 0;

            _progressReporter?.Invoke(frameCount, 0, $"[0/{frameCount}] {output}");
            foreach (var frame in GetFrames(spines))
            {
                if (ct.IsCancellationRequested)
                {
                    _logger.Info("Export cancelled");
                    frame.Dispose();
                    break;
                }

                _progressReporter?.Invoke(frameCount, frameIdx + 1, $"[{frameIdx + 1}/{frameCount}] {output}");
                yield return frame;
                frameIdx++;
            }
        }

        public sealed override void Export(string output, params SpineObject[] spines) => Export(output, default, spines);

        /// <summary>
        /// 导出给定的模型, 从前往后对应从上往下的渲染顺序
        /// </summary>
        /// <param name="output">输出路径, 一般而言都是文件路径, 少数情况指定的是文件夹</param>
        /// <param name="ct">取消令牌</param>
        /// <param name="spines">要导出的模型, 从前往后对应从上往下的渲染顺序</param>
        public abstract void Export(string output, CancellationToken ct, params SpineObject[] spines);
    }
}
