using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEditor.SceneManagement;
using UnityEditor;

public class MapExport {

	public delegate void onMapEventDel();

	public const string PATH = "Assets/Export";
	public const string PATH_FAST_EXPORT = "Assets/FastExport";

	public static event onMapEventDel onBeforeMapExport;
	public static event onMapEventDel onAfterMapExport;

	[MenuItem("Ravenfield Tools/Map/Export Open Scene as Map")]
	public static void ExportMap() {

		if(!Paths.HasExecutablePath()) {
			EditorUtility.DisplayDialog("Could not export map", "No executable set. Please find your game executable file with Ravenfield Tools -> Set Game Executable", "Ok");
			return;
		}

		string path;

		bool ok = BuildBundle(BuildTargetSelection.buildTarget, BuildMode.Smallest, out path, false);

		if(ok) {
			EditorUtility.DisplayDialog("Export completed", "Map was successfully exported to "+path, "Ok");
		}
		else {
			EditorUtility.DisplayDialog("Export failed", "Map couldn't export properly. Please see the console for error messages.", "Ok");
		}
	}

	public static string GetExportFileName(Scene scene) {
		return $"{MetaDataUtils.FormatAssetBundleName(scene.name)}.rfl";
	}

	static string GetExportLocalDirectory(BuildMode mode)
	{
		switch(mode)
		{
			case BuildMode.Fastest:
				return PATH_FAST_EXPORT;

			case BuildMode.Smallest:
				return PATH;
		}

		throw new System.NotImplementedException();
	}

	public static string GetExportFilePath(Scene scene, BuildMode mode) {
		return $"{Paths.ProjectPath()}{GetExportLocalDirectory(mode)}/{GetExportFileName(scene)}";
	}

	public static bool BuildBundle(BuildTarget buildTarget, BuildMode buildMode, out string filepath, bool ignoreGraphCacheWarning) {
		onBeforeMapExport?.Invoke();
		var result = BuildBundleInternal(buildTarget, buildMode, out filepath, ignoreGraphCacheWarning);
		onAfterMapExport?.Invoke();

		return result;
	}

	static bool BuildBundleInternal(BuildTarget buildTarget, BuildMode buildMode, out string filepath, bool ignoreGraphCacheWarning) {
		filepath = "";

		if(!BuildModeSelector.allowFastExportMode)
		{
			buildMode = BuildMode.Smallest;
		}

		Debug.ClearDeveloperConsole();
		Debug.Log("\n\n");
		Debug.Log("\n--- Building Map, Build Target: " + buildTarget + " ---");

		AssetBundleBuild build = new AssetBundleBuild();

		if (!AssetDatabase.IsValidFolder(PATH)) {
			Debug.Log("No Export folder found, creating one.");
			AssetDatabase.CreateFolder("Assets", "Export");
		}

		if (!AssetDatabase.IsValidFolder(PATH_FAST_EXPORT))
		{
			Debug.Log("No Fast Export folder found, creating one.");
			AssetDatabase.CreateFolder("Assets", "FastExport");
		}

		string exportDirectory = buildMode == BuildMode.Smallest ? PATH : PATH_FAST_EXPORT;


		EditorSceneManager.SaveOpenScenes();
		Scene currentScene = EditorSceneManager.GetSceneAt(0);

		try {

			if (!SanityCheck.DoSanityCheck(ignoreGraphCacheWarning)) {
				Debug.Log("Sanity check failed, aborting export!");
				return false;
			}


			//Bundle graphcache if available.
			TextAsset graphCacheAsset = CacheGenerator.GetGraphCacheFile();
			TextAsset graphCacheCoverPointAsset = CacheGenerator.GetGraphCacheCoverPointFile();

			if (graphCacheAsset != null && graphCacheCoverPointAsset != null) {
				Debug.Log("Graphcache was found, bundling!");
				SetupGraphCache(graphCacheAsset, graphCacheCoverPointAsset);
				EditorSceneManager.MarkAllScenesDirty();
				EditorSceneManager.SaveOpenScenes();
			}
			else {
				Debug.Log("WARNING: Couldn't find graphcache.");
			}

			build.assetBundleName = GetExportFileName(currentScene);

			BuildAssetBundleOptions buildOptions = BuildAssetBundleOptions.None;

			switch(buildMode)
			{
				case BuildMode.Fastest:
					buildOptions = BuildAssetBundleOptions.UncompressedAssetBundle;
					break;

				case BuildMode.Smallest:
					buildOptions = BuildAssetBundleOptions.ForceRebuildAssetBundle;
					break;
			}


			build.assetNames = new string[] { currentScene.path };

			AssetBundleBuild[] buildMap = new AssetBundleBuild[] { build };

			BuildPipeline.BuildAssetBundles(exportDirectory, buildMap, buildOptions, buildTarget);
		} catch (System.Exception e) {
			EditorUtility.DisplayDialog("Could not export map", "Could not create the .rfl file.\n\nDetails: " + e.Message, "Ok");
			Debug.LogException(e);
			return false;
		}

		string exportPath = GetExportFilePath(currentScene, buildMode);

		MapMetaData metaData;
		if (!MetaDataUtils.ReadMetaData(currentScene.path, out metaData, MapMetaData.Default)) {
			metaData = MapMetaData.Default;
		}

		MapMetaData.AutoGenerateMetadataInfo(currentScene, ref metaData);

		MetaDataUtils.WriteMetaData(currentScene.path, metaData);
		MetaDataUtils.CopyMetaData(currentScene.path, exportPath);

		Debug.Log("Successfully exported " + build.assetBundleName);

		if (Paths.HasExecutablePath()) {

			string levelDestinationPath = Paths.ExecutableToolsStagingPath() + "/" + build.assetBundleName;

			try {
				MetaDataUtils.CopyFileAndMetaData(exportPath, levelDestinationPath);
			} catch (System.Exception e) {
				EditorUtility.DisplayDialog("Could not export map", "Could not copy map to the mod staging folder.\n\nDetails: " + e.Message, "Ok");
				Debug.LogException(e);
				return false;
			}

			Debug.Log("Copied exported map to " + levelDestinationPath);
			filepath = levelDestinationPath;
			return true;
		}

		return false;
	}

	/*[MenuItem("Ravenfield Tools/Debug Path")]
	static void DebugPath() {
		Debug.Log("Executable: "+Paths.ExecutablePath(true));
		Debug.Log("Data Path: "+Paths.ExecutableDataPath());
		Debug.Log("Staging: "+Paths.ExecutableToolsStagingPath());
		Debug.Log("Saved Game Path: "+PlayerPrefs.GetString("executable path"));
	}*/

	static void SetupGraphCache(TextAsset asset, TextAsset coverPointAsset) {
		CustomGraphCache cacheComponent = GameObject.FindObjectOfType<CustomGraphCache>();
		if(cacheComponent == null) {
			GameObject graphCacheObject = new GameObject("Graph Cache (Automatically Generated)");
			cacheComponent = graphCacheObject.AddComponent<CustomGraphCache>();
		}

		cacheComponent.cache = asset;
		cacheComponent.cacheCoverPoints = coverPointAsset;
	}
}
