using Framework.Utility;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using UnityEngine;

namespace Framework.GuardianSaveSystem
{
    /// <summary>
    /// A single moment in time as record by the save system and stored so that it 
    /// can be reconstructed at a later date. It's main purpose is to know where the 
    /// user last was (in respect to which scene) and to allow loading of a scene
    /// that is not the current scene in the save file. 
    /// </summary>
    [Serializable]
    public class Moment
    {
        private string name;
        private string currentSceneName;
        private List<string> sceneNames; 
        private Moment previousMoment; // The previous moment that this moment was created from, could be many moments back as a previous save could have been loaded

        public Moment() : this("Default Moment")
        {
        }

        public Moment(string name)
        {
            // TODO, Use regex to check name for odd characters and throw exception if they
            // appear
            this.name = name;

            string path = UnityFile.GetPath() + this.name + @"/";
            if (!Directory.Exists(path))
            {
                Debug.Log("Creating new moment director: " + path);
                Directory.CreateDirectory(path);

                this.currentSceneName = Application.loadedLevelName;
                this.sceneNames = new List<string>();

                // Save moment data
                UnityFile momentFile = new UnityFile(this.name + @"/" + this.name);
                momentFile.Write(this);
            }
            else
            {
                // Load moment data
                UnityFile momentFile = new UnityFile(this.name + @"/" + this.name);
                Moment moment = momentFile.Read<Moment>();
                this.currentSceneName = moment.currentSceneName;
                this.sceneNames = moment.sceneNames;
            }
        }

        /// <summary>
        /// Checks to see if a scene exists in this moment. 
        /// </summary>
        /// <param name="sceneName"></param>
        /// <returns></returns>
        public bool HasScene(string sceneName)
        {
            return this.sceneNames.Contains(sceneName);
        }

        /// <summary>
        /// Saves the current scene to this moment. &gt;&gt;&gt; TODO, Fast forward scenes from previous moments?
        /// </summary>
        public void SaveScene()
        {
            List<ObjectDataMap> datas = new List<ObjectDataMap>();
            SaveMarker[] saveMarkers = GameObject.FindObjectsOfType<SaveMarker>();
            for (int i = 0; i < saveMarkers.Length; i++)
            {
                datas.Add(saveMarkers[i].SaveComponents());
            }
            UnityFile file = new UnityFile(this.name + @"/" + Application.loadedLevelName);
            file.Write(datas);

            // Add data to this moment
            this.currentSceneName = Application.loadedLevelName;
            this.sceneNames.Add(Application.loadedLevelName);

            // Save moment data
            UnityFile momentFile = new UnityFile(this.name + @"/" + this.name);
            momentFile.Write(this);
        }

        public void LoadScene()
        {
            LoadScene(this.currentSceneName);
        }

        /// <summary>
        /// Loads a scene, specified by name, from this moment. If the scene cannot be found in this moment
        /// it should be searched for in the previous moment and loaded from there until no previous moment
        /// exists. 
        /// </summary>
        /// <param name="sceneName"></param>
        public void LoadScene(string sceneName)
        {
            UnityFile file = new UnityFile(this.name + @"/" + sceneName);
            if (file.Exists())
            {
                if (Application.loadedLevelName != sceneName)
                {
                    Log("Loading level: " + sceneName);
                    Application.LoadLevel(sceneName);
                }
                List<ObjectDataMap> datas = file.Read<List<ObjectDataMap>>();
                SaveMarker[] saveMarkers = GameObject.FindObjectsOfType<SaveMarker>();

                // TODO return
                //List<int> dataIndicesToClear = new List<int>();

                for (int i = 0; i < datas.Count; i++)
                {
                    SaveMarker saveMarker = saveMarkers.FirstOrDefault(s => s.id == datas[i].GetId());
                    if (saveMarker != null)
                    {
                        saveMarker.LoadComponents(datas[i]);
                    }
                    else
                    {
                        Log("Save Marker not found creating prefab...");
                        Log("Prefab: " + datas[i].GetPrefabName());
                        GameObject gameObject = (GameObject)GameObject.Instantiate(Resources.Load(datas[i].GetPrefabName()), Vector3.zero, new Quaternion());
                        gameObject.GetComponent<SaveMarker>().LoadComponents(datas[i]);
                    }
                }

                for (int i = 0; i < saveMarkers.Length; i++)
                {
                    if (datas.FirstOrDefault(d => d.GetId() == saveMarkers[i].id) == null)
                    {
                        GameObject.Destroy(saveMarkers[i].gameObject);
                    }
                }
                // TODO, Test
            }
        }

        private void Log(string message)
        {
            //Debug.Log(message);
        }
    }
}
