using Framework.DataStructures;
using Framework.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;

namespace Framework.GuardianSaveSystem
{
    /// <summary>
    /// Marks the object this is attached to to be saved and loaded by the Guardian Save System. 
    /// </summary>
    public class SaveMarker : CoreBehaviour, Savable
    {
        public int id; // The instance id of the behaviour
        public string prefabName; // The name of the prefab that this behaviour can be constructed from

        /// <summary>
        /// Saves all the <see cref="Savable"/> components on this behaviour.
        /// </summary>
        /// <returns></returns>
        public ObjectDataMap SaveComponents()
        {
            // Get all components on this object of interface type Savable
            Component[] savables = GetComponents(typeof(Savable));

            // Create new ObjectDataMap
            ObjectDataMap data = new ObjectDataMap(this.id, this.prefabName);
            data.SetComponents(savables);

            // Populate ObjectDataMap by calling component 'Save' methods
            for (int i = 0; i < savables.Length; i++)
            {
                ((Savable)savables[i]).Save(data);
            }

            // Return ObjectDataMap
            return data;
        }

        /// <summary>
        /// Loads all of the <see cref="Savable"/> components on this behaviour.
        /// </summary>
        /// <param name="data"></param>
        public void LoadComponents(ObjectDataMap data)
        {
            // Get all components on this object of interface type Savable
            Component[] savables = GetComponents(typeof(Savable)); // TODO: Make into a list to add new savable components, or re-get components before last for loop

            // TODO, In theory components should never change
            //// Delete any not in the ObjectDataMap
            //for (int i = 0; i < savables.Length; i++)
            //{
            //    //Debug.Log(savables[i].name);
            //    if (!data.GetComponents().ContainsKey(savables[i].GetInstanceID()))
            //    {
            //        Destroy(savables[i]);
            //    }
            //}

            //// Add any not present that are in the ObjectDataMap
            //// TODO: HOW? How to create a component that has a unique id, do you create it, get its new id and update the save file???
            //foreach (KeyValuePair<int, Type> component in data.GetComponents())
            //{
            //    // If savables contains no component with matching id
            //    if (savables.FirstOrDefault(i => i.GetInstanceID() == component.Key) == null)
            //    {
            //        // TODO: Create from type
            //    }
            //}

            // Call load on all components and pass in ObjectDataMap
            for (int i = 0; i < savables.Length; i++)
            {
                ((Savable)savables[i]).Load(data);
            }
        }

        /// <summary>
        /// Saves some default properties of this behaviour. Usually things controlled by the 
        /// UnityEngine e.g.: <see cref="Transform"/>, <see cref="Rigidbody"/> etc.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public ObjectDataMap Save(ObjectDataMap data)
        {
            if (this.transform != null)
            {
                data.Add("Transform", new TransformData(this.transform));
            }
            if (this.GetComponent<Rigidbody>() != null)
            {
                data.Add("Rigidbody", new RigidbodyData(this.GetComponent<Rigidbody>()));
            }
            return data;
        }

        /// <summary>
        /// Loads some default properties of this behaviour. Usually things controlled by the 
        /// UnityEngine e.g.: <see cref="Transform"/>, <see cref="Rigidbody"/> etc.
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public void Load(ObjectDataMap data)
        {
            if (data.ContainsKey("Transform"))
            {
                data.Get<TransformData>("Transform").ToTransform(this.transform);
            }
            if (data.ContainsKey("Rigidbody"))
            {
                data.Get<RigidbodyData>("Rigidbody").ToRigidbody(this.GetComponent<Rigidbody>());
            }
        }

        /// <summary>
        /// Setup this marker.
        /// </summary>
        protected override void InnerAwake()
        {
            id = this.gameObject.GetInstanceID();
            if (string.IsNullOrEmpty(this.prefabName))
            {
                if (this.gameObject.name.Length > 7 && this.gameObject.name.Substring(this.gameObject.name.Length - 7, 7) == "(Clone)")
                {
                    this.prefabName = this.gameObject.name.Substring(0, this.gameObject.name.Length - 7);
                }
                else
                {
                    this.prefabName = this.gameObject.name;
                }
            }
        }
    }
}
