using UnityEditor;
using UnityEditorInternal;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.IO;
//using tk2dEditor.SpriteCollectionEditor;
//using tk2dEditor.SpriteAnimationEditor;
using System.Text.RegularExpressions;

[CustomEditor(typeof(AnimImport))]
	public class AnimImportEditor : Editor 
{
	static readonly string LIST_PROPERTY_NAME = "m_animations";
	static readonly string REGEX_PNG = @"(?<name>.*)_(?<id>\d+)\.png";
	
	AnimImport m_component;
	List<AnimImport.AnimImportData> m_items;	
	//SerializedObject m_targetObject = null;
	//SerializedProperty m_listProperty = null;
	
	//[SerializeField] string m_component.m_sourceDirectory = "";
	
	ReorderableList m_list;
	
	void OnEnable()
	{
	
		m_component = (AnimImport)target;
		m_items = m_component.m_animations;
		//serializedObject = new SerializedObject( target );	
		
		if ( Directory.Exists(m_component.m_sourceDirectory) == false )
			m_component.m_sourceDirectory = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Desktop);
				
		//m_listProperty = serializedObject.FindProperty(LIST_PROPERTY_NAME);
		
		m_list = new ReorderableList(serializedObject, serializedObject.FindProperty(LIST_PROPERTY_NAME),true, true, true, true);
		m_list.drawHeaderCallback = (Rect rect) => {  
			EditorGUI.LabelField(rect, "Animation Names and First Frames");
		};
		
		m_list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => 
		{			
			SerializedProperty listItem = m_list.serializedProperty.GetArrayElementAtIndex(index);
			rect.y += 2;
			EditorGUI.PropertyField( new Rect(rect.x, rect.y, rect.width-120, EditorGUIUtility.singleLineHeight),
				listItem.FindPropertyRelative("m_name"), GUIContent.none);		
			EditorGUI.LabelField(new Rect(rect.x + rect.width-90, rect.y, 90, EditorGUIUtility.singleLineHeight), "1st frame:");	
			EditorGUI.PropertyField( new Rect(rect.x + rect.width-30, rect.y, 30, EditorGUIUtility.singleLineHeight), 
			                        listItem.FindPropertyRelative("m_firstFrame"), GUIContent.none);
		};
	}
	
	
	override public void OnInspectorGUI() 
	{	
		//base.OnInspectorGUI();	
	
		if ( m_list == null )
			return;
			
		
		GUILayout.Space(5);
		
		//
		// PSD
		//
		EditorGUILayout.LabelField("Source Image File", EditorStyles.boldLabel);
		EditorGUILayout.BeginHorizontal();		
		if ( GUILayout.Button("Open in Editor") )
		{
			if ( string.IsNullOrEmpty(m_component.m_sourcePSD) == false )
			{
				if ( File.Exists(Application.dataPath+"/../"+m_component.m_sourcePSD) )
				{				
					Application.OpenURL(Application.dataPath+"/../"+m_component.m_sourcePSD);
				}
				else 
				{
					EditorUtility.DisplayDialog("Couldn't find file","The file isn't there anymore. Make sure the relative path from project directory is the same as when you set it.","OK");
				}
			}
			else
			{
				if ( EditorUtility.DisplayDialog("Path not set","You need to set the path first!","Sure", "Maybe later") )
				{
					string result = EditorUtility.OpenFilePanel("Select source PSD",m_component.m_sourcePSD,"");
					if ( string.IsNullOrEmpty(result) == false ) m_component.m_sourcePSD = result;
				}
			}
		}	
		
		m_component.m_sourcePSD = EditorGUILayout.TextField(m_component.m_sourcePSD);				
				
		if ( GUILayout.Button("...", EditorStyles.miniButton, GUILayout.Width(25)) )
		{		
			string result = EditorUtility.OpenFilePanel("Select source PSD",m_component.m_sourcePSD,"");
			if ( string.IsNullOrEmpty(result) == false ) m_component.m_sourcePSD = result;
		}
		if ( string.IsNullOrEmpty(m_component.m_sourcePSD) == false )
		{
			m_component.m_sourcePSD = MakeRelative(m_component.m_sourcePSD, Application.dataPath);
		}		
		EditorGUILayout.HelpBox("For reference only", MessageType.None);
		EditorGUILayout.EndHorizontal();
		
		GUILayout.Space(10);
		
		//
		// List
		//	
		EditorGUILayout.LabelField("Animation Specification", EditorStyles.boldLabel);
		serializedObject.Update();
		m_list.DoLayoutList();
		
		//EditorGUILayout.PropertyField( serializedObject.FindProperty("m_sourcePSD") );

		
		//
		// Source Directory/Import
		//		
		EditorGUILayout.LabelField("Settings", EditorStyles.boldLabel);
		EditorGUILayout.BeginHorizontal();
		m_component.m_sourceDirectory = EditorGUILayout.TextField( "Source PNG Directory", m_component.m_sourceDirectory );
		if ( GUILayout.Button("...",  GUILayout.Width(25)) )
		{
			string result = EditorUtility.OpenFolderPanel("Select Directory with Source PNGs",m_component.m_sourceDirectory,"");
			if ( string.IsNullOrEmpty(result) == false ) m_component.m_sourceDirectory = result;
		}	
		EditorGUILayout.EndHorizontal();
		m_component.m_deleteImportedPngs = EditorGUILayout.Toggle("Delete source PNGs", m_component.m_deleteImportedPngs);
		m_component.m_spriteDirectory = EditorGUILayout.TextField("Sprite Folder", m_component.m_spriteDirectory );	
		m_component.m_packingTag = EditorGUILayout.TextField("Packing Tag", m_component.m_packingTag);


		//EditorGUILayout.LabelField("Tools", EditorStyles.boldLabel);
		EditorGUILayout.Space();
		EditorGUILayout.HelpBox("NB: If animation is shortened, the extra sprites won't be deleted automatically. Delete them manually.", MessageType.None);
		EditorGUILayout.Space();
		EditorGUILayout.BeginHorizontal();
		bool importPNGs = GUILayout.Button( "Import Sprites" );
		bool createAnimations = GUILayout.Button( "Create Animations" );
		EditorGUILayout.EndHorizontal();	
		EditorGUILayout.Space();
		EditorGUILayout.Space();
		
		serializedObject.ApplyModifiedProperties();
		/*
		if (GUI.changed)
		{
			//serializedObject.ApplyModifiedProperties();	
			EditorUtility.SetDirty(target);	
			
			//RefreshTarget();
		}*/
		
		if ( importPNGs )
		{
			ImportPNGs();
			importPNGs = false;
		}
/*
		if ( buildAtlas )	
		{		
			AddToSpriteCollection();
			buildAtlas = false;
		}
		*/
		if ( createAnimations )	
		{		
			CreateAnimations();
			createAnimations = false;
		}
	}

	void ImportPNGs()
	{
		if ( string.IsNullOrEmpty(m_component.m_sourceDirectory) )
		{
			EditorUtility.DisplayDialog("Error","Failed to import, set source directory first!","OK");
			return;
		}
		
		if ( m_items.Count  == 0 )
		{
			EditorUtility.DisplayDialog("Error","Failed to import, set some anims up first!","OK");
			return;
		}
		
		string ourPath = GetSubdirectory(m_component.m_spriteDirectory);
		
		string[] sourceFileNames = Directory.GetFiles(m_component.m_sourceDirectory,"*_*.png");
		int[] sourceFileIds = new int[sourceFileNames.Length];
		string resultString = "";

		Regex pngRegex = new Regex(REGEX_PNG, RegexOptions.IgnoreCase | RegexOptions.Compiled );
		for ( int i = 0; i < sourceFileNames.Length; ++i )
		{
			int id = -1;
			Match match = pngRegex.Match( sourceFileNames[i] );
			id = int.Parse( match.Groups["id"].Value );
			sourceFileIds[i] = id;
		}

		for ( int i = 0; i < m_items.Count; ++i )
		{		
			int firstFrame = m_items[i].m_firstFrame;			
			int lastFrame = ( i+1 < m_items.Count) ? m_items[i+1].m_firstFrame : 1000;
			//if ( lastFrame < firstFrame )
				//lastFrame = 10000;
			string failString = null;
			int copiedCount = 0;


			for ( int spriteId = firstFrame; spriteId < lastFrame; ++spriteId )
			{		
				// Find path that matches prefix
				string sourcePath = null;
				
				int sourceIndex = System.Array.FindIndex(sourceFileIds, sourceId=>sourceId==spriteId);
				if ( sourceFileNames.IsIndexValid(sourceIndex) )
				{
					sourcePath = sourceFileNames[sourceIndex];
				}

				if ( string.IsNullOrEmpty(sourcePath) || string.IsNullOrEmpty(m_items[i].m_name) )
				{
					//failString = "Failed to copy from empty Path";
					break;
				}
				else 
				{
					
					string fileName = m_items[i].m_name + "_"+(spriteId-firstFrame).ToString()+".png";
					string targetPath = ourPath + fileName;
					//Debug.Log("source: "+sourcePath+", targetPath = " + targetPath);

					// Copy the file
					try
					{						
						File.Copy(sourcePath, targetPath, true);
						string relativeTargetPath = MakeRelative(targetPath, Application.dataPath);
						TextureImporter importer = TextureImporter.GetAtPath(relativeTargetPath) as TextureImporter;
						if ( importer == null )
							AssetDatabase.ImportAsset(relativeTargetPath, ImportAssetOptions.ForceUpdate);
						importer = TextureImporter.GetAtPath(relativeTargetPath) as TextureImporter;
						importer.spritePixelsPerUnit = 1;
						importer.filterMode = FilterMode.Point;
						importer.mipmapEnabled = false;
						importer.textureFormat = TextureImporterFormat.AutomaticTruecolor;
						importer.spritePackingTag = m_component.m_packingTag;
						AssetDatabase.ImportAsset(relativeTargetPath, ImportAssetOptions.ForceUpdate);
						copiedCount++;	
						//resultString += "Imported "+m_items[i].m_name + "_"+(spriteId-firstFrame).ToString()+".png"+"\n";
					}
					catch(System.Exception e )
					{
						if (e!= null){};
						if ( string.IsNullOrEmpty(failString) )
							failString = "Failed to copy from "+sourcePath+" to "+targetPath;
					}


					// Delete the copied file
					if ( m_component.m_deleteImportedPngs )
					{
						try
						{
							File.Delete(sourcePath);						
						}
						catch(System.Exception e )
						{
							if (e!= null){};
							if ( string.IsNullOrEmpty(failString) )
								failString = "Failed to delete "+sourcePath;
						}
					}

				}	
			}


			if ( string.IsNullOrEmpty(failString) )
			{
				if ( copiedCount <= 0 )
				{
					resultString += "Failed to import "+m_items[i].m_name + ": No frames found\n";
				}
				else if ( copiedCount == 1 )
				{
					// Imported Idle (1 frame)
					resultString += "Imported "+m_items[i].m_name + " (1 frame)\n";
				}
				else 
				{
					// Imported Idle (5 frames)
					resultString += "Imported "+m_items[i].m_name + " ("+copiedCount.ToString()+" frames)\n";
				}
			}
			else 
			{
				resultString += "Failed to import "+ m_items[i].m_name +" "+ failString+"\n";
			}

			
		}
		
		if ( string.IsNullOrEmpty(resultString) == false )
		{
			EditorUtility.DisplayDialog("Import Result",resultString,"OK");
		}
		else 
		{
			
			EditorUtility.DisplayDialog("Import Result","No sprites were found in "+m_component.m_sourceDirectory,"OK");
		}
		
		AssetDatabase.Refresh();
		
	}

/*
	void AddToSpriteCollection()
	{

		if ( m_component.m_tkSprites == null || m_component.m_tkAnimation == null )
		{
			EditorUtility.DisplayDialog("Error","Failed to create anims, set a collection and animations first!","OK");
			return;
		}

		string ourPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(m_component.gameObject));
		Directory.CreateDirectory( ourPath + "/Sprites");
		ourPath += "/Sprites/";


		//
		// First add to atlas
		//
		{
			string resultString = null;
			SpriteCollectionProxy spriteCollectionProxy = new SpriteCollectionProxy(m_component.m_tkSprites);
			List<Texture2D> sprites = new List<Texture2D>();
			for ( int i = 0; i < m_items.Count; ++i )
			{				
				int firstFrame = m_items[i].m_firstFrame;			
				int lastFrame = ( i+1 < m_items.Count) ? m_items[i+1].m_firstFrame : 1000;
				for ( int spriteId = firstFrame; spriteId < lastFrame; ++spriteId )
				{
					Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(ourPath+m_items[i].m_name+"_"+(spriteId-firstFrame).ToString()+".png");
					if ((texture != null) && (spriteCollectionProxy.FindSpriteBySource(texture) == -1)) 
					{
						string name = spriteCollectionProxy.FindUniqueTextureName(texture.name);
						int slot = spriteCollectionProxy.FindOrCreateEmptySpriteSlot();
						spriteCollectionProxy.textureParams[slot].name = name;
						spriteCollectionProxy.textureParams[slot].colliderType = tk2dSpriteCollectionDefinition.ColliderType.UserDefined;
						spriteCollectionProxy.textureParams[slot].texture = texture;
						sprites.Add(texture);	
					}
				}
			}

			spriteCollectionProxy.DeleteUnusedData();
			spriteCollectionProxy.CopyToTarget();
			tk2dSpriteCollectionBuilder.ResetCurrentBuild();
			if (!tk2dSpriteCollectionBuilder.Rebuild( m_component.m_tkSprites )) 
			{
				EditorUtility.DisplayDialog("Failed to commit sprite collection", 
					"Please check the console for more details.", "Ok");
			}

			//string resultString = AddToAtlas(atlasInfo, sprites.ToArray());
			if ( string.IsNullOrEmpty(resultString) == false )
			{
				EditorUtility.DisplayDialog("Failed to add sprites to atlas",resultString,"OK");
				return; // errored, so bail					
			}
		}
		
		// Refresh asset database
		AssetDatabase.Refresh();
	}
	*/
	
	void CreateAnimations()
	{

		string spritePath = GetSubdirectory(m_component.m_spriteDirectory);

		string componentPath = Path.GetDirectoryName(AssetDatabase.GetAssetPath(m_component.gameObject));


		//
		// Create anims
		//


		{

			// Add new stuff/make edits

			for ( int i = 0; i < m_items.Count; ++i )
			{	
			
				// string clipName = m_items[i].m_name;

				int firstFrame = m_items[i].m_firstFrame;			
				int lastFrame = firstFrame+1;
				if ( i+1 < m_items.Count) 
				{
					lastFrame = m_items[i+1].m_firstFrame;
				}
				else 
				{
					// TODO: calculate actual last frame better (check how crawl does it!)
					for ( int spriteId = firstFrame; spriteId < 1000; ++spriteId )
					{
						lastFrame = spriteId;
						string fileName = m_items[i].m_name + "_"+(spriteId-firstFrame).ToString()+".png";

						if ( File.Exists(spritePath + fileName) == false )
						{
							break;
						}
					}
				}

				bool isNew = false;
				string animFileName = componentPath+"/"+ m_items[i].m_name+".asset";
				#if UNITY_5_0_0
					AnimationClip animClip = AssetDatabase.LoadAssetAtPath(animFileName, typeof(AnimationClip)) as AnimationClip;					
				#else
					AnimationClip animClip = AssetDatabase.LoadAssetAtPath<AnimationClip>(animFileName);
				#endif
				if ( animClip == null )
				{					
					animClip = new AnimationClip();
					isNew = true;
				}

				animClip.name = m_items[i].m_name;
				//animClip.legacy = true;

				EditorCurveBinding curveBinding = new EditorCurveBinding();
				// I want to change the sprites of the sprite renderer, so I put the typeof(SpriteRenderer) as the binding type.
				curveBinding.type = typeof(SpriteRenderer);
				// Regular path to the gameobject that will be changed (empty string means root)
				curveBinding.path = "";
				// This is the property name to change the sprite of a sprite renderer
				curveBinding.propertyName = "m_Sprite";

				animClip.frameRate = 10;

				ObjectReferenceKeyframe[] keyframes = new ObjectReferenceKeyframe[lastFrame - firstFrame];

				for ( int frameIndex = 0; frameIndex < keyframes.Length; ++frameIndex )
				{
					int spriteId = firstFrame + frameIndex;
					string fileName = m_items[i].m_name + "_"+(spriteId-firstFrame).ToString()+".png";
					string relativeTargetPath = MakeRelative( spritePath + fileName, Application.dataPath);

					keyframes[frameIndex] = new ObjectReferenceKeyframe();	
					keyframes[frameIndex].time = (1.0f/animClip.frameRate) * frameIndex;
					#if UNITY_5_0_0
					Sprite sprite = AssetDatabase.LoadAssetAtPath(relativeTargetPath, typeof(Sprite)) as Sprite;
					#else
					Sprite sprite = AssetDatabase.LoadAssetAtPath<Sprite>(relativeTargetPath);
					#endif
					keyframes[frameIndex].value = sprite;
				}

				AnimationUtility.SetObjectReferenceCurve(animClip,curveBinding,keyframes);

				if ( isNew )
				{
					// Save the animation
					AssetDatabase.CreateAsset(animClip, animFileName);
				}
			}

			//EditorUtility.SetDirty(m_component.m_tkAnimation);
			AssetDatabase.SaveAssets();

		}


		// Refresh asset database
		AssetDatabase.Refresh();


	}

	public static string MakeRelative(string filePath, string referencePath)
	{
		try 
		{
			var fileUri = new System.Uri(filePath);
			var referenceUri = new System.Uri(referencePath);
			if ( referenceUri.IsAbsoluteUri)
				return referenceUri.MakeRelativeUri(fileUri).ToString();
		}
		catch (System.Exception e)
		{
			if ( e != null ){}
		}
		return filePath;
	}

	// Gets a subdirectory of current game object directory, creating it if necessary
	string GetSubdirectory(string subDirectoryName )
	{
		string result = Path.GetDirectoryName(AssetDatabase.GetAssetPath(m_component.gameObject));
		if ( string.IsNullOrEmpty(subDirectoryName) == false )
		{
			result += "/" + subDirectoryName;
			Directory.CreateDirectory( result );
		}
		result = Path.GetFullPath(result);
		//result = MakeRelative(result, "");

		result = result.Replace("\\", "/");

		if (result.EndsWith("/") == false )
			result += "/";
		return result;
	}
	
	/*
	// Delegate
	void RefreshTarget()
	{
		serializedObject = new SerializedObject( target );	
		m_listProperty = serializedObject.FindProperty(LIST_PROPERTY_NAME);
	}*/
}
