
using KF3.Containers;
using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

public class KF3SceneContainer : SceneContainer
{
    public string BackgroundName = "";
    public Dictionary<string, GameObject> Backgrounds = new Dictionary<string, GameObject>();
    public string StageName = "";
    public Dictionary<string, GameObject> Stages = new Dictionary<string, GameObject>();
    public List<GameObject> InstantiatedObjects = new List<GameObject>();
    public List<FrameContent> Frames = new List<FrameContent>();

    public UIKF3SceneSettings Settings;

    protected KF3ModelViewerMain Main => KF3ModelViewerMain.Instance;

    public override void Init(ModelViewerMain main1)
    {
        var main = main1 as KF3ModelViewerMain;
        base.Init(main);
        Settings = UIKF3SceneSettings.Create(this);
        InstantiatedObjects.Add(Settings.gameObject);

        if (main.UI != null)
        {
            KF3ModelViewerInterface.SetDropdownData(Settings.StageDropdown, KF3ModelViewerMain.Instance.Assets.Stages.Select(s => s.DisplayName).ToList(), true);
            KF3ModelViewerInterface.SetDropdownData(Settings.BackgroundDropdown, KF3ModelViewerMain.Instance.Assets.Skies.Select(s => s.DisplayName).ToList(), true);
        }
    }

    public void SetBackground(string newBackground)
    {
        if (newBackground == BackgroundName) return;
        var currentBackground = BackgroundName;

        if (newBackground != "")
        {
            if (!Backgrounds.ContainsKey(newBackground))
            {
                Backgrounds[newBackground] = null;
                KF3ModelViewerMain.Instance.StartCoroutine(Main.Builder.SpawnAsset(newBackground, bg =>
                {
                    Backgrounds[newBackground] = bg;
                    InstantiatedObjects.Add(bg);
                }));
            }
            else
            {
                Backgrounds[newBackground]?.SetActive(true);
            }
        }

        if (currentBackground != "")
        {
            Backgrounds[currentBackground]?.SetActive(false);

            var currentFrame = TimelineController.Instance.CurrentFrame;
            bool containsCurrent = Frames.Where(f => (f.ObjectData as SceneKeyframe).Background == currentBackground && f.FrameNum != currentFrame).Any();

            if (!containsCurrent)
            {
                KF3ModelViewerMain.DisposeOf(Backgrounds[currentBackground]);
                Backgrounds.Remove(currentBackground);
            }
        }

        BackgroundName = newBackground;
    }

    public void SetStage(string newStage)
    {
        if (newStage == StageName) return;
        var currentStage = StageName;

        if (newStage != "")
        {
            if (!Stages.ContainsKey(newStage))
            {
                Stages[newStage] = null;
                KF3ModelViewerMain.Instance.StartCoroutine(Main.Builder.SpawnAsset(newStage, bg =>
                {
                    Stages[newStage] = bg;
                    InstantiatedObjects.Add(bg);
                }));
            }
            else
            {
                Stages[newStage]?.SetActive(true);
            }
        }

        if (currentStage != "")
        {
            Stages[currentStage]?.SetActive(false);

            var currentFrame = TimelineController.Instance.CurrentFrame;
            bool containsCurrent = Frames.Where(f => (f.ObjectData as SceneKeyframe).Stage == currentStage && f.FrameNum != currentFrame).Any();

            if (!containsCurrent)
            {
                KF3ModelViewerMain.DisposeOf(Stages[currentStage]);
                Stages.Remove(currentStage);
            }
        }

        StageName = newStage;
    }

    public KeyframeData SerializeFrame()
    {
        return new SceneKeyframe(this);
    }

    public override SceneSerializable Serialize()
    {
        return new KF3SceneSerializable(this);
    }

    public void Lerp(KeyframeData frame1, KeyframeData frame2, float amount)
    {
        var frame = (frame1 as SceneKeyframe).Lerp(frame2, amount) as SceneKeyframe;
        SetBackground(frame.Background);
        SetStage(frame.Stage);
    }

    public void GetClosestFrames(int frame, out FrameContent previousFrame, out FrameContent nextFrame)
    {
        previousFrame = null;
        nextFrame = null;

        var frames = Frames.OrderBy(f => f.FrameNum).ToArray();

        for (int i = 0; i < Frames.Count; i++)
        {
            if (Frames[i].FrameNum < frame)
            {
                previousFrame = nextFrame = frames[i];
            }
            else if (Frames[i].FrameNum == frame)
            {
                previousFrame = nextFrame = frames[i];
                break;
            }
            else if (Frames[i].FrameNum > frame)
            {
                if (previousFrame == null) previousFrame = frames[i];
                nextFrame = frames[i];
                break;
            }
        }
    }

    public virtual void SetKeyframe(int frameNum = -1)
    {
        if (frameNum == -1) frameNum = TimelineController.Instance.CurrentFrame;

        GetClosestFrames(frameNum, out var previousFrame, out var nextFrame);

        FrameContent currentFrame = null;
        if (previousFrame == null || previousFrame.FrameNum != frameNum)
        {
            currentFrame = new FrameContent(frameNum);
            Frames.Add(currentFrame);
            Frames = Frames.OrderBy(f => f.FrameNum).ToList();
        }
        else if (previousFrame.FrameNum == frameNum) currentFrame = previousFrame;
        else if (nextFrame.FrameNum == frameNum) currentFrame = nextFrame;

        currentFrame.SetObjectData(SerializeFrame());

        TimelineController.UpdateTimeline();
    }

    public void SetDefaultFrame()
    {
        Frames = new List<FrameContent>()
        {
            new FrameContent(0).SetObjectData(SerializeFrame())
        };
    }
}