using System;
using System.Collections.Generic;

/// <summary>
/// 谱面运行数据类,负责将谱面数据解析之后生成运行时数据项,与控制层通讯,这些数据项是查询现有数据项的唯一依据
/// 集合在比较
/// </summary>
public class ChartRuntimeModel
{
    /// <summary>
    /// 负责管理在项目运行时所产生的物件数据,可以生成Chart对象
    /// </summary>
    public SortedSet<RuntimeBaseNoteData> ChartRuntimeSet = new SortedSet<RuntimeBaseNoteData>(new UniversalBaseNoteComparer());
    /// <summary>
    /// 输入搜索的开始与结束时间,返回符合条件的对象视图
    /// </summary>
    public SortedSet<RuntimeBaseNoteData> SearchRange(float startTime, float endTime)
    {
        var view = ChartRuntimeSet.GetViewBetween(new TempBaseNoteComparer(startTime), new TempBaseNoteComparer(endTime));
        return view;
    }
    /// <summary>
    /// 删除指定的数据项
    /// </summary>
    public void DeleteDataItem(RuntimeBaseNoteData obj)
    {
        ChartRuntimeSet.Remove(obj);
    }
    /// <summary>
    /// 获得指定的数据项引用,如果不存在对应的数据项则会返回null
    /// </summary>
    public RuntimeBaseNoteData GetDataItem(RuntimeBaseNoteData obj)
    {
        if (ChartRuntimeSet.TryGetValue(obj, out var Value))
        {
            return Value;
        }
        else
        {
            return null;
        }
    }
    /// <summary>
    /// 添加新的数据项
    /// </summary>
    public void AddDataItem(RuntimeBaseNoteData obj)
    {
        ChartRuntimeSet.Add(obj);
    }
    /// <summary>
    /// 遍历所有的数据项,将其转换为持久化谱面数据对象
    /// </summary>
    public void GetChartData(Chart chart)
    {
        foreach (var item in ChartRuntimeSet)
        {
            switch (item)
            {
                case RuntimeNoteData:
                    {
                        chart.noteList.Add(new NoteListItem(
                            item.xPos,
                            item.yPos,
                            item.targetTime,
                            item.bpmGroup.GroupNum));
                        break;
                    }
                case RuntimeDragData:
                    {
                        chart.dragList.Add(new DragListItem(
                            item.xPos,
                            item.yPos,
                            item.targetTime,
                            item.bpmGroup.GroupNum));
                        break;
                    }

                case RuntimeFlickData: 
                    {
                        var flick = item as RuntimeFlickData;
                        chart.flickList.Add(new FlickListItem(
                            item.xPos,
                            item.yPos,
                            item.targetTime,
                            item.bpmGroup.GroupNum,
                            flick.direction
                            ));
                        break; 
                    }
                case RuntimeSnakeData: 
                    {
                        var snake = item as RuntimeSnakeData;
                        chart.dataFlowList.Add(new DataFlowListItem(
                            snake.xPos,
                            snake.yPos,
                            snake.endXPos,
                            snake.endYPos,
                            snake.targetTime,
                            snake.endTime,
                            true,
                            snake.bpmGroup.GroupNum,
                            snake.referencePoint
                            ));
                        break; 
                    }
            }

        }

    }
}
public class UniversalBaseNoteComparer : IComparer<RuntimeBaseNoteData>
{
    public int Compare(RuntimeBaseNoteData x, RuntimeBaseNoteData y)
    {
        if (x == null && y == null) return 0;
        if (x == null) return -1;
        if (y == null) return 1;

        int timeComparison = x.targetTime.CompareTo(y.targetTime);
        if (timeComparison != 0) return timeComparison;

        int xComparison = x.xPos.CompareTo(y.xPos);
        if (xComparison != 0) return xComparison;

        int yComparison = x.yPos.CompareTo(y.yPos);
        if (yComparison != 0) return yComparison;

        // 如果是蛇节点，比较特有参数
        if (x is RuntimeSnakeData xSnake && y is RuntimeSnakeData ySnake)
        {
            // 比较蛇节点的特有参数
            int endXComparison = xSnake.endXPos.CompareTo(ySnake.endXPos);
            if (endXComparison != 0) return endXComparison;

            int endYComparison = xSnake.endYPos.CompareTo(ySnake.endYPos);
            if (endYComparison != 0) return endYComparison;

            int endTimeComparison = xSnake.endTime.CompareTo(ySnake.endTime);
            if (endTimeComparison != 0) return endTimeComparison;

            int referencePointComparison = xSnake.referencePoint.CompareTo(ySnake.referencePoint);
            if (referencePointComparison != 0) return referencePointComparison;
        }

        // 只比较类型
        Type xType = x.GetType();
        Type yType = y.GetType();

        // 如果类型相同，返回 0（表示相等，SortedSet 不会添加重复项）
        // 如果类型不同，按类型名称排序
        return string.Compare(xType.FullName, yType.FullName, StringComparison.Ordinal);
    }
}
/// <summary>
/// 用于在运行时传递数据的基类
/// </summary>
public class RuntimeBaseNoteData
{
    public float targetTime { get; set; }
    public float xPos { get; set; }
    public float yPos { get; set; }
    public BPMGroup bpmGroup { get; set; }
}
public class TempBaseNoteComparer : RuntimeBaseNoteData
{
    public TempBaseNoteComparer(float time)
    {
        targetTime = time;
    }
}
public class RuntimeNoteData : RuntimeBaseNoteData
{
    public RuntimeNoteData(float targetTime, float xPos, float yPos, BPMGroup bpmGroup)
    {
        this.targetTime = targetTime;
        this.xPos = xPos;
        this.yPos = yPos;
        this.bpmGroup = bpmGroup;
    }
}
public class RuntimeDragData : RuntimeBaseNoteData
{
    public RuntimeDragData(float targetTime, float xPos, float yPos, BPMGroup bpmGroup)
    {
        this.targetTime = targetTime;
        this.xPos = xPos;
        this.yPos = yPos;
        this.bpmGroup = bpmGroup;
    }
}
public class RuntimeFlickData : RuntimeBaseNoteData
{
    public FlickDirection direction { get; set; }
    public RuntimeFlickData(float targetTime, float xPos, float yPos, FlickDirection direction, BPMGroup bpmGroup)
    {
        this.targetTime = targetTime;
        this.xPos = xPos;
        this.yPos = yPos;
        this.direction = direction;
        this.bpmGroup = bpmGroup;
    }
}
public class RuntimeSnakeData : RuntimeBaseNoteData
{
    public float endXPos { get; set; }
    public float endYPos { get; set; }
    public float endTime { get; set; }
    public ReferencePointLocation referencePoint { get; set; }
    public RuntimeSnakeData(float targetTime, float xPos, float yPos, float endXPos, float endYPos, float endTime, ReferencePointLocation referencePoint, BPMGroup bpmGroup)
    {
        this.targetTime = targetTime;
        this.xPos = xPos;
        this.yPos = yPos;
        this.endXPos = endXPos;
        this.endYPos = endYPos;
        this.endTime = endTime;
        this.referencePoint = referencePoint;
        this.bpmGroup = bpmGroup;
    }
}
