using SFML.Graphics;
using SFML.System;
using Spine;
using System;
using System.Collections.Generic;
using System.CommandLine;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace SpineViewerCLI
{
    public static class Extension
    {
        /// <summary>
        /// 获取一个对象副本, 继承所有状态
        /// </summary>
        public static SpineObject Copy(this SpineObject self, bool keepTrackTime = false)
        {
            var spineObject = new SpineObject(self, true);

            // 拷贝轨道动画, 但是仅拷贝第一个条目
            foreach (var tr in self.AnimationState.IterTracks().Where(t => t is not null))
            {
                var t = spineObject.AnimationState.SetAnimation(tr!.TrackIndex, tr.Animation, tr.Loop);
                t.TimeScale = tr.TimeScale;
                t.Alpha = tr.Alpha;
                if (keepTrackTime)
                    t.TrackTime = tr.TrackTime;
            }

            // XXX(#105): 部分 3.4.02 版本模型在设置动画后出现附件残留, 因此强制进行一次 Setup
            if (spineObject.Version == SpineVersion.V34)
            {
                spineObject.Skeleton.SetSlotsToSetupPose();
            }

            spineObject.Update(0);
            return spineObject;
        }

        /// <summary>
        /// 获取当前状态包围盒
        /// </summary>
        public static FloatRect GetCurrentBounds(this SpineObject self)
        {
            self.Skeleton.GetBounds(out var x, out var y, out var w, out var h);
            return new(x, y, Math.Max(w, 1e-6f), Math.Max(h, 1e-6f));
        }

        /// <summary>
        /// 计算所有轨道第一个条目的动画时长最大值
        /// </summary>
        /// <param name="self"></param>
        /// <returns></returns>
        public static float GetAnimationMaxDuration(this SpineObject self)
        {
            return self.AnimationState.IterTracks().Select(t => t?.Animation.Duration ?? 0).DefaultIfEmpty(0).Max();
        }

        /// <summary>
        /// 合并另一个矩形
        /// </summary>
        public static FloatRect Union(this FloatRect self, FloatRect rect)
        {
            float left = Math.Min(self.Left, rect.Left);
            float top = Math.Min(self.Top, rect.Top);
            float right = Math.Max(self.Left + self.Width, rect.Left + rect.Width);
            float bottom = Math.Max(self.Top + self.Height, rect.Top + rect.Height);
            return new(left, top, right - left, bottom - top);
        }

        /// <summary>
        /// 按给定的帧率获取所有轨道第一个条目动画全时长包围盒大小, 是一个耗时操作, 如果可能的话最好缓存结果
        /// </summary>
        public static FloatRect GetAnimationBounds(this SpineObject self, float fps = 30)
        {
            using var copy = self.Copy();
            var bounds = copy.GetCurrentBounds();
            var maxDuration = copy.GetAnimationMaxDuration();
            for (float tick = 0, delta = 1 / fps; tick < maxDuration; tick += delta)
            {
                bounds = bounds.Union(copy.GetCurrentBounds());
                copy.Update(delta);
            }
            return bounds;
        }

        /// <summary>
        /// 自动添加所有能找到的类型是 <see cref="Argument"/> 或者 <see cref="Option"/> 的公开属性
        /// </summary>
        /// <param name="self"></param>
        public static void AddArgsAndOpts(this Command self)
        {
            // 用反射查找自己所有的公开属性是 Argument 或者 Option 的
            foreach (var prop in self.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
            {
                var value = prop.GetValue(self);
                if (value is Argument arg) self.Add(arg);
                else if (value is Option opt) self.Add(opt);
            }
        }
    }
}
