using System.Reflection;

namespace DownKyi.Core.Danmaku2Ass;

/// <summary>
/// 显示方式
/// </summary>
public class Display
{
    public Config Config;
    public Danmaku Danmaku;
    public int LineIndex;

    public int FontSize;
    public bool IsScaled;
    public int MaxLength;
    public int Width;
    public int Height;

    public Tuple<int, int> Horizontal;
    public Tuple<int, int> Vertical;

    public int Duration;
    public int Leave;

    protected Display()
    {
    }

    public Display(Config config, Danmaku danmaku)
    {
        Config = config;
        Danmaku = danmaku;
        LineIndex = 0;

        IsScaled = SetIsScaled();
        FontSize = SetFontSize();
        MaxLength = SetMaxLength();
        Width = SetWidth();
        Height = SetHeight();

        Horizontal = SetHorizontal();
        Vertical = SetVertical();

        Duration = SetDuration();
        Leave = SetLeave();
    }

    /// <summary>
    /// 根据弹幕样式自动创建对应的 Display 类
    /// </summary>
    /// <returns></returns>
    public static Display Factory(Config config, Danmaku danmaku)
    {
        var dict = new Dictionary<string, Display>
        {
            { "scroll", new ScrollDisplay(config, danmaku) },
            { "top", new TopDisplay(config, danmaku) },
            { "bottom", new BottomDisplay(config, danmaku) }
        };
        return dict[danmaku.Style];
    }

    /// <summary>
    /// 字体大小
    /// 按用户自定义的字体大小来缩放
    /// </summary>
    /// <returns></returns>
    protected int SetFontSize()
    {
        if (IsScaled)
        {
            Console.WriteLine($"{Danmaku.SizeRatio}");
        }

        return Utils.IntCeiling(Config.BaseFontSize * Danmaku.SizeRatio);
    }

    /// <summary>
    /// 字体是否被缩放过
    /// </summary>
    /// <returns></returns>
    protected bool SetIsScaled()
    {
        return !Math.Round(Danmaku.SizeRatio, 2).Equals(1.0);
        //return Danmaku.SizeRatio.Equals(1.0f);
    }

    /// <summary>
    /// 最长的行字符数
    /// </summary>
    /// <returns></returns>
    protected int SetMaxLength()
    {
        var lines = Danmaku.Content.Split('\n');

        return lines.Select(Utils.DisplayLength).Prepend(0).Max();
    }

    /// <summary>
    /// 整条字幕宽度
    /// </summary>
    /// <returns></returns>
    protected int SetWidth()
    {
        float charCount = MaxLength; // / 2;
        return Utils.IntCeiling(FontSize * charCount);
    }

    /// <summary>
    /// 整条字幕高度
    /// </summary>
    /// <returns></returns>
    protected int SetHeight()
    {
        var lineCount = Danmaku.Content.Split('\n').Length;
        return lineCount * FontSize;
    }

    /// <summary>
    /// 出现和消失的水平坐标位置
    /// 默认在屏幕中间
    /// </summary>
    /// <returns></returns>
    protected virtual Tuple<int, int> SetHorizontal()
    {
        var x = (int)Math.Floor(Config.ScreenWidth / 2.0);
        return Tuple.Create(x, x);
    }

    /// <summary>
    /// 出现和消失的垂直坐标位置
    /// 默认在屏幕中间
    /// </summary>
    /// <returns></returns>
    protected virtual Tuple<int, int> SetVertical()
    {
        var y = (int)Math.Floor(Config.ScreenHeight / 2.0);
        return Tuple.Create(y, y);
    }

    /// <summary>
    /// 整条字幕的显示时间
    /// </summary>
    /// <returns></returns>
    protected virtual int SetDuration()
    {
        var baseDuration = 3 + Config.TuneDuration;
        if (baseDuration <= 0)
        {
            baseDuration = 0;
        }

        float charCount = MaxLength / 2;

        int value;
        if (charCount < 6)
        {
            value = baseDuration + 1;
        }
        else if (charCount < 12)
        {
            value = baseDuration + 2;
        }
        else
        {
            value = baseDuration + 3;
        }

        return value;
    }

    /// <summary>
    /// 离开碰撞时间
    /// </summary>
    /// <returns></returns>
    protected virtual int SetLeave()
    {
        return (int)(Danmaku.Start + Duration);
    }

    /// <summary>
    /// 按照新的行号重新布局
    /// </summary>
    /// <param name="lineIndex"></param>
    public void Relayout(int lineIndex)
    {
        LineIndex = lineIndex;
        Horizontal = SetHorizontal();
        Vertical = SetVertical();
    }
}

/// <summary>
/// 顶部
/// </summary>
public class TopDisplay : Display
{
    public TopDisplay(Config config, Danmaku danmaku) : base(config, danmaku)
    {
        //Console.WriteLine("TopDisplay constructor.");
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    protected override Tuple<int, int> SetVertical()
    {
        // 这里 y 坐标为 0 就是最顶行了
        var y = LineIndex * Config.BaseFontSize;
        return Tuple.Create(y, y);
    }
}

/// <summary>
/// 底部
/// </summary>
public class BottomDisplay : Display
{
    public BottomDisplay(Config config, Danmaku danmaku) : base(config, danmaku)
    {
        //Console.WriteLine("BottomDisplay constructor.");
    }

    /// <summary>
    /// 
    /// </summary>
    /// <returns></returns>
    protected override Tuple<int, int> SetVertical()
    {
        // 要让字幕不超出底部，减去高度
        var y = Config.ScreenHeight - (LineIndex * Config.BaseFontSize) - Height;
        // 再减去自定义的底部边距
        y -= Config.BottomMargin;
        return Tuple.Create(y, y);
    }
}

/// <summary>
/// 滚动
/// </summary>
public class ScrollDisplay : Display
{
    public int Distance;
    public int Speed;

    public ScrollDisplay(Config config, Danmaku danmaku) : base()
    {
        //Console.WriteLine("ScrollDisplay constructor.");

        Config = config;
        Danmaku = danmaku;
        LineIndex = 0;

        IsScaled = SetIsScaled();
        FontSize = SetFontSize();
        MaxLength = SetMaxLength();
        Width = SetWidth();
        Height = SetHeight();

        Horizontal = SetHorizontal();
        Vertical = SetVertical();

        Distance = SetDistance();
        Speed = SetSpeed();

        Duration = SetDuration();
        Leave = SetLeave();
    }

    /// <summary>
    /// ASS 的水平位置参考点是整条字幕文本的中点
    /// </summary>
    /// <returns></returns>
    protected override Tuple<int, int> SetHorizontal()
    {
        var x1 = Config.ScreenWidth + (int)Math.Floor(Width / 2.0);
        var x2 = 0 - (int)Math.Floor(Width / 2.0);
        return Tuple.Create(x1, x2);
    }

    protected override Tuple<int, int> SetVertical()
    {
        var baseFontSize = Config.BaseFontSize;

        // 垂直位置，按基准字体大小算每一行的高度
        var y = (LineIndex + 1) * baseFontSize;

        // 个别弹幕可能字体比基准要大，所以最上的一行还要避免挤出顶部屏幕
        // 坐标不能小于字体大小
        if (y < FontSize)
        {
            y = FontSize;
        }

        return Tuple.Create(y, y);
    }

    /// <summary>
    /// 字幕坐标点的移动距离
    /// </summary>
    /// <returns></returns>
    protected int SetDistance()
    {
        var x = Horizontal;
        return x.Item1 - x.Item2;
    }

    /// <summary>
    /// 字幕每个字的移动的速度
    /// </summary>
    /// <returns></returns>
    protected int SetSpeed()
    {
        // 基准时间，就是每个字的移动时间
        // 12 秒加上用户自定义的微调
        var baseDuration = 12 + Config.TuneDuration;
        if (baseDuration <= 0)
        {
            baseDuration = 1;
        }

        return Utils.IntCeiling(Config.ScreenWidth / baseDuration);
    }

    /// <summary>
    /// 计算每条弹幕的显示时长，同步方式
    /// 每个弹幕的滚动速度都一样，辨认度好，适合观看剧集类视频。
    /// </summary>
    /// <returns></returns>
    public int SyncDuration()
    {
        return Distance / Speed;
    }

    /// <summary>
    /// 计算每条弹幕的显示时长，异步方式
    /// 每个弹幕的滚动速度都不一样，动态调整，辨认度低，适合观看 MTV 类视频。
    /// </summary>
    /// <returns></returns>
    public int AsyncDuration()
    {
        var baseDuration = 6 + Config.TuneDuration;
        if (baseDuration <= 0)
        {
            baseDuration = 0;
        }

        float charCount = MaxLength / 2;

        int value;
        if (charCount < 6)
        {
            value = (int)(baseDuration + charCount);
        }
        else if (charCount < 12)
        {
            value = baseDuration + (int)(charCount / 2);
        }
        else if (charCount < 24)
        {
            value = baseDuration + (int)(charCount / 3);
        }
        else
        {
            value = baseDuration + 10;
        }

        return value;
    }

    /// <summary>
    /// 整条字幕的移动时间
    /// </summary>
    /// <returns></returns>
    protected override int SetDuration()
    {
        var methodName = Config.LayoutAlgorithm.Substring(0, 1).ToUpper() + Config.LayoutAlgorithm.Substring(1);
        methodName += "Duration";
        var method = typeof(ScrollDisplay).GetMethod(methodName);
        if (method != null)
        {
            return (int)method.Invoke(this, null);
        }

        return 0;
    }

    /// <summary>
    /// 离开碰撞时间
    /// </summary>
    /// <returns></returns>
    protected override int SetLeave()
    {
        // 对于滚动样式弹幕来说，就是最后一个字符离开最右边缘的时间
        // 坐标是字幕中点，在屏幕外和内各有半个字幕宽度
        // 也就是跑过一个字幕宽度的路程
        var duration = Width / Speed;
        return (int)(Danmaku.Start + duration);
    }
}