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

public class KF3ModelViewerMain : ModelViewerMain
{
    public float SpecialDistance = 1;
    public bool IsOnline = true;
    public static KF3ModelViewerMain Instance => GetInstance<KF3ModelViewerMain>();

    public KF3ModelViewerInterface UI => ModelViewerInterface.GetInstance<KF3ModelViewerInterface>();
    public KF3AssetLibrary Assets => AssetLibrary.GetInstance<KF3AssetLibrary>();
    public KF3ModelBuilder Builder => ModelBuilder.GetInstance<KF3ModelBuilder>();
    public AdditionalResources Resources => SharedResources.Instance as AdditionalResources;
    public AnimationClip OverrideClip;

    protected override void Awake()
    {
        Debug.LogError("Fix this");
        Settings.Load();
        _backupTimers = _backupTimes.ToArray();
    }

    private IEnumerator Start()
    {
        Application.targetFrameRate = 30;
        Error.Log(Color.green, "Viewer version: " + Application.version);

        float warningTimer = 0;

        yield return GetComponent<KF3AssetLibrary>().Init();
        while (KF3ModelBuilder.Instance == null || KF3ModelViewerInterface.Instance == null || SharedResources.Instance == null)
        {
            warningTimer += Time.deltaTime;
            if (warningTimer > 2)
            {
                warningTimer = 0;
                string report = $"AssetLibrary: {KF3AssetLibrary.Instance != null}\nModelBuilder: {KF3ModelBuilder.Instance != null}\nInterface: {KF3ModelViewerInterface.Instance != null}\nResources: {SharedResources.Instance != null}";
                Error.Log(report, Color.red);
            }
            yield return 0;
        }

        _mainInstance = this;
        CurrentScene = SceneContainer.Create<KF3SceneContainer>(this);
        
        KF3ModelViewerInterface.SetDropdownData(GetCurrentScene<KF3SceneContainer>().Settings.StageDropdown, KF3ModelViewerMain.Instance.Assets.Stages.Select(s => s.DisplayName).ToList(), true);
        KF3ModelViewerInterface.SetDropdownData(GetCurrentScene<KF3SceneContainer>().Settings.BackgroundDropdown, KF3ModelViewerMain.Instance.Assets.Skies.Select(s => s.DisplayName).ToList(), true);
    }

    private void Update()
    {
        AutoSaveUpdate();
    }

    public override void SelectionClear()
    {
        base.SelectionClear();
        KF3ModelViewerInterface.Instance.SelectionClear();
    }

    public static void DisposeOf(GameObject go)
    {
        if (go == null) return;
        var rends = go.GetComponentsInChildren<Renderer>();
        foreach(var rend in rends)
        {
            for(int i=rend.materials.Length-1; i>=0; i--)
            {
                Destroy(rend.materials[i]);
            }
        }
        Destroy(go);
        UnityEngine.Resources.UnloadUnusedAssets();
        System.GC.Collect();
    }

    public override IEnumerator LoadScene(SceneSerializable bc)
    {
        Debug.Log("Emptying scene");
        EmptyScene<KF3SceneContainer>();

        foreach (var c in bc.Objects)
        {
            Debug.Log($"Loading {c.Filename} {c.GUID} {c.Frames.Count}");
            if (c.GUID == "cameraOrbit")
            {
                MainCameraOrbit.DeserializeFrames(c);
            }
            else
            {
                yield return Builder.SpawnSerializedCoroutine(c);
            }
        }
        bc.Timeline.Deserialize(TimelineController.Instance);
        (CurrentScene as KF3SceneContainer).Frames = (bc as KF3SceneSerializable).Frames.Select(f => new FrameContent(f)).ToList();
        yield return 0;
        TimelineController.SetCurrentFrame(TimelineController.Instance.CurrentFrame);
    }

    public static void FocusOn(Camera camera, GameObject focusedObject, float cameraDistance)
    {
        Bounds bounds = GetBoundsWithChildren(focusedObject);

        Vector3 objectSizes = bounds.max - bounds.min;
        float objectSize = Mathf.Max(objectSizes.x, objectSizes.y, objectSizes.z);
        float cameraView = 2.0f * Mathf.Tan(0.5f * Mathf.Deg2Rad * Camera.main.fieldOfView); // Visible height 1 meter in front
        float distance = cameraDistance * objectSize / cameraView; // Combined wanted distance from the object
        distance += 0.5f * objectSize; // Estimated offset from the center to the outside of the object
        Camera.main.transform.position = bounds.center - distance * Camera.main.transform.forward;
    }

    public static Bounds GetBoundsWithChildren(GameObject gameObject)
    {
        Renderer parentRenderer = gameObject.GetComponent<Renderer>();

        Renderer[] childrenRenderers = gameObject.GetComponentsInChildren<Renderer>();

        Bounds bounds = parentRenderer != null
           ? parentRenderer.bounds
           : childrenRenderers.FirstOrDefault(x => x.enabled).bounds;

        if (childrenRenderers.Length > 0)
        {
            foreach (Renderer renderer in childrenRenderers)
            {
                if (renderer.enabled)
                {
                    bounds.Encapsulate(renderer.bounds);
                }
            }
        }

        return bounds;
    }

}
