using UnityEngine;
using UnityEditor;
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;
using com.deeperbeige.lib;

static public class NGUIHelpers
{
	[MenuItem("NGUI/Set depths from Z order")]
	static public void SetDepthsFromZ ()
	{
		List<UIWidget> widgets = new List<UIWidget>();
		List<UnityEngine.Object> prefabs = new List<UnityEngine.Object>();
		
		for (int i = 0; i < UIRoot.list.Count; ++i)
		{
			UIRoot root = UIRoot.list[i];
			UIWidget[] list = root.gameObject.GetComponentsInChildren<UIWidget>(true);
			for (int b = 0; b < list.Length; ++b)
				widgets.Add(list[b]);
		}

		if (widgets.Count > 0)
		{
			bool foundDisconnectedPrefab = false;
			widgets.Sort(delegate(UIWidget w1, UIWidget w2) { return -w1.transform.position.z.CompareTo(w2.transform.position.z); });

			int depth = 0;
			
			// Pass 1, Assign depths
			for (int i = 0; i < widgets.Count; ++i)
			{
				UIWidget w = widgets[i];
				w.depth = ++depth;
				
#if UNITY_EDITOR
				UnityEditor.EditorUtility.SetDirty(w);
#endif
			}
			
			// Pass 2, update all affected prefabs
			for (int i = 0; i < widgets.Count; ++i)
			{
				GameObject instance = widgets[i].gameObject;
				
				PrefabType type = PrefabUtility.GetPrefabType(instance);
				
				if (type == PrefabType.DisconnectedPrefabInstance)
				{
					Debug.LogWarning("Warning: Disconnected prefab found: " + GameObjects.FullPath(instance) + " (prefab parent = " + PrefabUtility.GetPrefabParent(instance).name + ")");
					foundDisconnectedPrefab = true;
					continue;
				}
				
				if (type != PrefabType.PrefabInstance) continue;
				
				instance = PrefabUtility.FindPrefabRoot(instance);
				type     = PrefabUtility.GetPrefabType(instance);
				
				UnityEngine.Object prefab = PrefabUtility.GetPrefabParent(instance);
				
				if (prefab == null)           continue;
				if (prefabs.Contains(prefab)) continue;
				prefabs.Add(prefab);
				
				PrefabUtility.ReplacePrefab(instance, prefab, ReplacePrefabOptions.ConnectToPrefab);
			}
			
			// Pass 3, revert all prefabs
			for (int i = 0; i < widgets.Count; ++i)
			{
				GameObject instance = widgets[i].gameObject;
				PrefabType type = PrefabUtility.GetPrefabType(instance);
				
				if (type != PrefabType.DisconnectedPrefabInstance && type != PrefabType.PrefabInstance) continue;
				
				instance = PrefabUtility.FindPrefabRoot(instance);
				type     = PrefabUtility.GetPrefabType(instance);
				
				if (type != PrefabType.DisconnectedPrefabInstance && type != PrefabType.PrefabInstance) continue;
				PrefabUtility.RevertPrefabInstance(instance);
			}
			
			if (foundDisconnectedPrefab)
			{
				Debug.Log("Disconnected prefabs not processed as this would corrupt the hierachy. Consider fixing by either reverting prefabs, or by fully disconnecting them. The easiest way to fully disconnect a prefab is to drag it into the project to make it into a *new* prefab, then break the prefab instance, then *delete* the new prefab.");
			}
		}
	}
	
	[MenuItem("NGUI/Set depths from Z values")]
	static public void SetDepthsFromZPos ()
	{
		List<UIWidget> widgets = new List<UIWidget>();
		List<UnityEngine.Object> prefabs = new List<UnityEngine.Object>();
		
		for (int i = 0; i < UIRoot.list.Count; ++i)
		{
			UIRoot root = UIRoot.list[i];
			UIWidget[] list = root.gameObject.GetComponentsInChildren<UIWidget>(true);
			for (int b = 0; b < list.Length; ++b)
				widgets.Add(list[b]);
		}
		
		if (widgets.Count > 0)
		{
			bool foundDisconnectedPrefab = false;
			
			// Pass 1, Assign depths
			for (int i = 0; i < widgets.Count; ++i)
			{
				UIWidget w = widgets[i];
				w.depth = -Mathf.RoundToInt(100 * w.transform.position.z);
				
				#if UNITY_EDITOR
				UnityEditor.EditorUtility.SetDirty(w);
				#endif
			}
			
			// Pass 2, update all affected prefabs
			for (int i = 0; i < widgets.Count; ++i)
			{
				GameObject instance = widgets[i].gameObject;
				
				PrefabType type = PrefabUtility.GetPrefabType(instance);
				
				if (type == PrefabType.DisconnectedPrefabInstance)
				{
					Debug.LogWarning("Warning: Disconnected prefab found: " + GameObjects.FullPath(instance) + " (prefab parent = " + PrefabUtility.GetPrefabParent(instance).name + ")");
					foundDisconnectedPrefab = true;
					continue;
				}
				
				if (type != PrefabType.PrefabInstance) continue;
				
				instance = PrefabUtility.FindPrefabRoot(instance);
				type     = PrefabUtility.GetPrefabType(instance);
				
				UnityEngine.Object prefab = PrefabUtility.GetPrefabParent(instance);
				
				if (prefab == null)           continue;
				if (prefabs.Contains(prefab)) continue;
				prefabs.Add(prefab);
				
				PrefabUtility.ReplacePrefab(instance, prefab, ReplacePrefabOptions.ConnectToPrefab);
			}
			
			// Pass 3, revert all prefabs
			for (int i = 0; i < widgets.Count; ++i)
			{
				GameObject instance = widgets[i].gameObject;
				PrefabType type = PrefabUtility.GetPrefabType(instance);
				
				if (type != PrefabType.DisconnectedPrefabInstance && type != PrefabType.PrefabInstance) continue;
				
				instance = PrefabUtility.FindPrefabRoot(instance);
				type     = PrefabUtility.GetPrefabType(instance);
				
				if (type != PrefabType.DisconnectedPrefabInstance && type != PrefabType.PrefabInstance) continue;
				PrefabUtility.RevertPrefabInstance(instance);
			}
			
			if (foundDisconnectedPrefab)
			{
				Debug.Log("Disconnected prefabs not processed as this would corrupt the hierachy. Consider fixing by either reverting prefabs, or by fully disconnecting them. The easiest way to fully disconnect a prefab is to drag it into the project to make it into a *new* prefab, then break the prefab instance, then *delete* the new prefab.");
			}
		}
	}
	
	[MenuItem("NGUI/Rewiring/01 - Create atlas report")]
    public static void AtlasReport()
    {
	    string                  filename   = "atlas-report.txt";
		string                  result     = "";
		List<string>            allSprites = new List<string>();
		Dictionary<string, int> totals     = new Dictionary<string, int>();
		
		// Dump all atlasses
        var atlasses = Resources.FindObjectsOfTypeAll(typeof (UIAtlas)) as UIAtlas[];
		foreach (var atlas in atlasses)
		{
			result += "atlas:" + atlas.name + "\n";
			
			var sprites = atlas.GetListOfSprites();
			
			foreach (string spriteName in sprites)
			{
				UISpriteData sprite = atlas.GetSprite(spriteName);
				
				result += "  sprite:" + spriteName + "|";
				result += sprite.width + "|" + sprite.height + "|";
				result += sprite.borderLeft + "|" + sprite.borderRight + "|" + sprite.borderTop + "|" + sprite.borderBottom + "|";
				result += sprite.paddingLeft + "|" + sprite.paddingRight + "|" + sprite.paddingTop + "|" + sprite.paddingBottom;
				result += "\n";
				
				if (allSprites.Contains(spriteName)) Debug.LogWarning("Duplicate sprite name: " + spriteName);
				
				allSprites.Add(spriteName);
				totals.Add(atlas.name + "/" + sprite.name, 0);
			}
		}
		
		result += "\n";
		
		// Dump heirachy
		var widgets = Resources.FindObjectsOfTypeAll(typeof (UIWidget)) as UIWidget[];
		foreach (var widget in widgets)
		{
			string widgetType = widget.GetType().ToString();
			switch (widgetType)
			{
				case "UISprite":
					UISprite sprite = widget as UISprite;
					
					if (! sprite.atlas)
					{
						Debug.LogWarning("Sprite " + pathTo(sprite) + " has no atlas");
						continue;
					}
					
					result += "UISprite|" + pathTo(widget) + "|" + sprite.spriteName + "|" + sprite.atlas.name + "\n";
					string key = sprite.atlas.name + "/" + sprite.spriteName;
					if (! totals.ContainsKey(key)) totals[key] = 0;
					totals[key]++;
					break;
				
				case "UILabel":
					UILabel label = widget as UILabel;
					result += "UILabel|" + pathTo(widget) + "|" + label.bitmapFont + "\n";
					break;
				
				case "UITexture":
					UITexture texture = widget as UITexture;
					string mainTextureName = "NONE";
					if (texture.mainTexture != null) mainTextureName = texture.mainTexture.name;
					result += "UITexture|" + pathTo(widget) + "|" + mainTextureName + "\n";
					break;
				
				default:
					Debug.LogWarning("Unknown widget type " + widgetType);
					break;
			}
		}
		
		result += "\n";
		
		// Dump usage totals
		foreach (KeyValuePair<string, int> entry in totals)
		{
			result += entry.Key + ": " + entry.Value + "\n";
		}
		
		File.WriteAllText(filename, result);
		Debug.Log("Saved report to " + filename);
    }
	
	private static string pathTo(UIWidget widget)
	{
		string path = widget.name;
		
		Transform tx = widget.transform;
		while (tx.parent != null)
		{
			path = tx.name + "/" + path;
			tx = tx.parent;
		}
		
		return path;
	}
	
	[MenuItem("NGUI/Rewiring/02 - Copy atlas attributes from atlas-report")]
    public static void FixBordersAndPadding()
    {
	    string   filename     = "atlas-report.txt";
		string   fileContents = File.ReadAllText(filename);
		string[] lines        = fileContents.Split('\n');
		int      fixes        = 0;
		
		foreach (string line in lines)
		{
			Match match = Regex.Match(line, @"^  sprite:(.+?)\|(.+)$");
			if (! match.Success) continue;
			
			string   spriteName = match.Groups[1].Value;
			string   data       = match.Groups[2].Value;
			string[] values     = data.Split('|');
			
			//int width  = int.Parse(values[0]);
			//int height = int.Parse(values[1]);
			
			int borderLeft    = int.Parse(values[2]);
			int borderRight   = int.Parse(values[3]);
			int borderTop     = int.Parse(values[4]);
			int borderBottom  = int.Parse(values[5]);
			
			int paddingLeft   = int.Parse(values[6]);
			int paddingRight  = int.Parse(values[7]);
			int paddingTop    = int.Parse(values[8]);
			int paddingBottom = int.Parse(values[9]);
			
			UIAtlas atlas = FindAtlasContaining(spriteName);
			
			if (atlas == null)
			{
				bool alert = false;
				if (borderLeft    != 0) alert = true;
				if (borderRight   != 0) alert = true;
				if (borderTop     != 0) alert = true;
				if (borderBottom  != 0) alert = true;
				if (paddingLeft   != 0) alert = true;
				if (paddingRight  != 0) alert = true;
				if (paddingTop    != 0) alert = true;
				if (paddingBottom != 0) alert = true;
				
				if (alert) Debug.LogWarning(spriteName + " not found in new atlasses");
				continue;
			}
			
			UISpriteData sprite = atlas.GetSprite(spriteName);
			sprite.SetBorder(borderLeft, borderBottom, borderRight, borderTop);
			sprite.SetPadding(paddingLeft, paddingBottom, paddingRight, paddingTop);
			fixes++;
		}
		
		Debug.Log("Fixed " + fixes + " attributes");
	}
	
	[MenuItem("NGUI/Rewiring/03 - Rewire sprite names to new atlases")]
    public static void RewireSprites()
    {
		// Dump heirachy
		var widgets = Resources.FindObjectsOfTypeAll(typeof (UIWidget)) as UIWidget[];
		foreach (var widget in widgets)
		{
			string widgetType = widget.GetType().ToString();
			switch (widgetType)
			{
				case "UISprite":
					UISprite sprite = widget as UISprite;
					string spriteName = sprite.spriteName;
				
					UIAtlas atlas = FindAtlasContaining(spriteName);
					if (atlas == null)
					{
						Debug.LogWarning(spriteName + " not found for " + pathTo(sprite));
					} else {
						//Debug.Log ("Rewiring " + sprite + " (" + spriteName + ") to " + atlas.name);
						sprite.atlas = atlas;
						sprite.spriteName = spriteName;
					}
					break;
				
				case "UILabel":
					break;
				
				case "UITexture":
					break;
				
				default:
					Debug.LogError("Unknown widget type " + widgetType);
					break;
			}
		}
	}
	
	private static UIAtlas FindAtlasContaining(string spriteName)
	{
		// Search only these atlasses
		string[] newAtlasses = new string[] {"atlasA", "atlasB", "atlasC", "atlasD", "atlasE"};
		
        var atlasses = Resources.FindObjectsOfTypeAll(typeof (UIAtlas)) as UIAtlas[];
		foreach (var atlas in atlasses)
		{
			// Check this is one of the atlasses we can use
			if (Array.IndexOf(newAtlasses, atlas.name) == -1) continue;
			
			// If this atlas has the named sprite, we're done
			var sprites = atlas.GetListOfSprites();
			foreach (string sprite in sprites)
			{
				if (spriteName == sprite) return atlas;
			}
		}
		
		return null;
	}
	
	[MenuItem("NGUI/Rewiring/04 - Unmangle fonts")]
    public static void FixFonts()
    {
        var fonts = Resources.FindObjectsOfTypeAll(typeof (UIFont)) as UIFont[];
        Debug.Log(string.Format("Found {0} font(s):", fonts.Length));
        foreach (var font in fonts)
        {
            var sprite = font.spriteName;
            Debug.Log(string.Format("{0} with sprite {1}", font.name, sprite));
            font.spriteName = "";
            font.spriteName = sprite;
            EditorUtility.SetDirty(font.gameObject);
        }
    }
}
