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

/// <summary>
/// The Save System helps to coordinate the saving and loading of game data
/// into scriptable objects that implement the ISaveLoad interface.
/// </summary>
[CreateAssetMenu(fileName = "SaveSystem", menuName = "ScriptableObjects/SaveLoadGame/SaveSystem")]
public class SaveSystem : ScriptableObject
{
    // The current save name. This is kept in memory for convenience.
    public string currentSaveName;
    // List of scriptable objects (that implement the ISaveLoad interface) that  save / load data
    public List<ScriptableObject> gameDataObjects;

    // UnityEvents that are triggered in the case of errors
    public UnityEvent<string> OnSaveError;
    public UnityEvent<string> OnLoadError;
    public UnityEvent<string> OnStartGameError;

    // Returns a list of objects that have implemented the ISaveLoad intefrace. 
    // Any object that has not implemented the interface will not be included.
    private List<ISaveLoad> GetSaveLoadObjects()
    {
        List<ISaveLoad> saveLoadObjects = new List<ISaveLoad>();
        foreach (ScriptableObject gameDataObject in gameDataObjects)
        {
            if (gameDataObject is ISaveLoad)
            {
                ISaveLoad saveloadObject = (ISaveLoad)gameDataObject;
                saveLoadObjects.Add(saveloadObject);
            }
        }
        return saveLoadObjects;
    }

    // Checks to see that objects that are saving and loading each save to
    // a unique file name.
    private bool SaveCollisionsDetected(string saveName)
    {
        List<ISaveLoad> saveLoadObjects = GetSaveLoadObjects();
        List<string> dataNames = new List<string>();
        foreach (ISaveLoad saveLoadObject in saveLoadObjects)
        {
            string dataName = saveLoadObject.GetDataName();
            if (dataNames.Contains(dataName))
            {
                OnSaveError.Invoke($"Save Collision Detected. Multiple Objects attempting to save using the same data name: {dataName}");
                return true;
            }
            else
            {
                dataNames.Add(dataName);
            }
        }
        return false;
    }

    // Goes through each game data object and invokes its save method
    public void SaveGame(string saveName)
    {
        // Do noting if the save name is not set / valid
        if (string.IsNullOrEmpty(saveName))
        {
            OnSaveError.Invoke($"The current save name {saveName} is not a valid name");
            return;
        }


        // Do a check to make sure there are no save collisions.
        if (SaveCollisionsDetected(saveName))
        {
            return;
        }

        // Set the current save name to the game we are saving
        currentSaveName = saveName;

        // Trigger each object's save method
        List<ISaveLoad> saveLoadObjects = GetSaveLoadObjects();
        foreach (ISaveLoad saveLoadObject in saveLoadObjects)
        {
            string errorMessage = saveLoadObject.Save(saveName);

            // stop saving data if an error is detected
            if (!string.IsNullOrEmpty(errorMessage))
            {
                OnSaveError.Invoke(errorMessage);
                return;
            }
        }
    }

    // Save each game data object using the current save file name we are using
    public void SaveGame()
    {
        SaveGame(currentSaveName);
    }

    // Goes through each game data object and invokes its load method
    public void LoadGame(string saveName)
    {
        if (SaveSystemUtilities.SaveExists(saveName))
        {
            // Set the current save name to the game we are loading
            currentSaveName = saveName;

            // Trigger each object's load method
            List<ISaveLoad> saveLoadObjects = GetSaveLoadObjects();
            foreach (ISaveLoad saveLoadObject in saveLoadObjects)
            {
                string errorMessage = saveLoadObject.Load(saveName);

                // stop loading data if an error is detected
                if (!string.IsNullOrEmpty(errorMessage))
                {
                    OnSaveError.Invoke(errorMessage);
                    return;
                }
            }
        }
        else
        {
            OnLoadError.Invoke($"The save file {saveName} does not exist");
        }
    }

    // Goes through each game data object and invokes its reset method
    public void SetupNewGame(string saveName)
    {
        // Set the current save name to the game we are starting
        currentSaveName = saveName;

        // Do noting if the save name is not set / valid
        if (string.IsNullOrEmpty(saveName))
        {
            OnStartGameError.Invoke($"The current save name {saveName} is not a valid name");
            return;
        }

        // Trigger each object's reset method
        List<ISaveLoad> saveLoadObjects = GetSaveLoadObjects();
        foreach (ISaveLoad saveLoadObject in saveLoadObjects)
        {
            string errorMessage = saveLoadObject.Reset();

            // stop resetting data if an error is detected
            if (!string.IsNullOrEmpty(errorMessage))
            {
                OnStartGameError.Invoke(errorMessage);
                return;
            }
        }

        // Save the game
        SaveGame();
    }

    public void DeleteGame(string saveName)
    {
        if (SaveSystemUtilities.SaveExists(saveName))
        {
            SaveSystemUtilities.DeleteSave(saveName);
        }
    }

}
