using System;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine.Events;
using System.Runtime.InteropServices;
using static Codice.CM.WorkspaceServer.DataStore.WkTree.WriteWorkspaceTree;
using System.ComponentModel;

public class ModelViewerMain : MonoBehaviour
{
    protected static ModelViewerMain _mainInstance;

    [Header("Required")]
    public CameraContainer MainCameraOrbit;
    public CameraContainer WorkCameraOrbit;

    [Header("Runtime")]
    public OperationMode Mode;
    public SceneContainer CurrentScene;
    public ObjectContainer SelectedObject;
    public List<ObjectContainer> SelectedObjects = new List<ObjectContainer>();
    public static UnityEvent<ObjectContainer> OnObjectDeleteEvent = new UnityEvent<ObjectContainer>();

    protected float[] _backupTimes = new float[] { 15 * 60, 5 * 60, 60 };
    protected float[] _backupTimers;

    public enum OperationMode
    {
        Single,
        Multiple
    }

    public static ModelViewerMain GetInstance()
    {
        return _mainInstance;
    }

    public static T GetInstance<T>() where T : ModelViewerMain
    {
        return _mainInstance as T;
    }

    protected virtual void Awake()
    {
        ModelViewerSettings.Load();

        _backupTimers = _backupTimes.ToArray();

        if(CurrentScene == null)
        {
            Debug.LogError("Custom model viewer should create a new scene in Awake()");
            CurrentScene = SceneContainer.Create<SceneContainer>(this);
        }

        _mainInstance = this;
    }

    public CameraOrbit GetCamera()
    {
        return MainCameraOrbit.Camera;
    }

    public CameraContainer GetCameraHandler()
    {
        if (!MainCameraOrbit.LineRenderer.enabled)
            return MainCameraOrbit;
        else
            return WorkCameraOrbit;
    }

    public static void RegisterObject(ObjectContainer container)
    {
        if (!GetInstance().CurrentScene.AllObjects.Contains(container))
        {
            GetInstance().CurrentScene.AllObjects.Add(container);
        }
        var selector = UITimelineObjectEntry.Create(container);
        container.InstantiatedObjects.Add(selector.gameObject);
    }

    public static void UnregisterObject(ObjectContainer container)
    {
        var instance = GetInstance();
        instance.CurrentScene.AllObjects.Remove(container);
        instance.SelectionRemove(container);

        if (instance.SelectedObject == container)
        {
            instance.SelectObject(null);
        }
    }

    public T GetSelectedObject<T>() where T: ObjectContainer
    {
        return SelectedObject as T;
    }

    public T GetCurrentScene<T>() where T : SceneContainer
    {
        return CurrentScene as T;
    }

    public void SelectObject(ObjectContainer container)
    {
        if (SelectedObject == container) return;

        SelectedObject?.Deselect();
        SelectedObject = container;
        SelectedObject?.Select();

        TimelineController.UpdateTimeline();
    }

    public void SelectionAdd(ObjectContainer container)
    {
        if (container == null) return;

        if (SelectedObjects.Count == 0)
        {
            SelectedObjects.Add(container);
            SelectObject(container);
        }
        else if(container != null && !SelectedObjects.Contains(container))
        {
            SelectedObjects.Add(container);
        }
    }

    public void SelectionRemove(ObjectContainer container)
    {
        if (container == null) return;

        bool wasSelected = container == SelectedObject;

        if (container != null && SelectedObjects.Contains(container))
        {
            SelectedObjects.Remove(container);
            if (wasSelected)
            {
                if(SelectedObjects.Count > 0)
                {
                    SelectObject(SelectedObjects[0]);
                }
                else
                {
                    SelectObject(null);
                }
            }
        }
    }

    public virtual void SelectionClear()
    {
        SelectedObjects.Clear();
    }

    public void PrepareForLoad()
    {
        if (Mode == OperationMode.Single)
        {
            EmptySceneDefault();
        }
    }

    /// <summary> Replace with custom type if game requires it </summary>
    public virtual void EmptySceneDefault()
    {
        EmptyScene<SceneContainer>();
    }

    public void EmptyScene<T>() where T : SceneContainer, new()
    {
        CurrentScene?.Destroy();
        CurrentScene = SceneContainer.Create<T>(this);

        ModelViewerMain.GetInstance().SelectionClear();
        TimelineController.UpdateTimeline();
    }

    public SceneSerializable SaveScene()
    {
        return CurrentScene.Serialize();
    }

    public virtual IEnumerator LoadScene(SceneSerializable bc)
    {
        EmptySceneDefault();
        ObjectContainerSerializable cameraFrameData = null;

        foreach (var c in bc.Objects)
        {
            if (c.GUID == "cameraOrbit")
            {
                cameraFrameData = c;
            }
            else
            {
                yield return ModelBuilder.GetInstance().SpawnSerializedCoroutine(c);
            }
        }

        //deserialize camera last
        yield return 0;
        MainCameraOrbit.DeserializeFrames(cameraFrameData);
        bc.Timeline.Deserialize(TimelineController.Instance);
        TimelineController.SetCurrentFrame(TimelineController.Instance.CurrentFrame);

        //CurrentScene.AllObjects[1].PastePose(CurrentScene.AllObjects[1].Frames[0].ObjectData, PoseLoadOptions.All());
    }

    protected void AutoSaveUpdate()
    {
        bool skip = false;
        for (int i = 0; i < _backupTimes.Length; i++)
        {
            _backupTimers[i] -= Time.deltaTime;
            if (skip)
            {
                _backupTimers[i] = _backupTimes[i];
            }
            else if (_backupTimers[i] <= 0)
            {
                _backupTimers[i] = _backupTimes[i];

                var scene = SaveScene();
                scene.Filename = $"AutoBackup_{_backupTimes[i]}";
                UISceneContainer.CreateNew(scene, true).Save(true);

                skip = true;
            }
        }
    }
}