context: using UnityEngine;
  using UnityEditor;
  using System.Collections.Generic;
  
  namespace tk2dEditor.SpriteCollectionEditor
  {
  	// As nasty as this is, its a necessary evil for backwards compatibility
  	public class SpriteCollectionProxy
  	{
  		public SpriteCollectionProxy()
  		{
  		}
  		
  		public SpriteCollectionProxy(tk2dSpriteCollection obj)
  		{
  			this.obj = obj;
  			CopyFromSource();
  		}
  		
  		public void CopyFromSource()
  		{
  			this.obj.Upgrade(); // make sure its up to date
  
  			textureParams = new List<tk2dSpriteCollectionDefinition>(obj.textureParams.Length);
  			foreach (var v in obj.textureParams)
  			{
  				if (v == null) 
  					textureParams.Add(null);
  				else 
  				{
  					var t = new tk2dSpriteCollectionDefinition();
  					t.CopyFrom(v);
  					textureParams.Add(t);
  				}
  			}
  			
  			spriteSheets = new List<tk2dSpriteSheetSource>();
  			if (obj.spriteSheets != null)
  			{
  				foreach (var v in obj.spriteSheets)
  				{
  					if (v == null) 
  						spriteSheets.Add(null);
  					else
  					{
  						var t = new tk2dSpriteSheetSource();
  						t.CopyFrom(v);
  						spriteSheets.Add(t);
  					}
  				}
  			}
  			
  			fonts = new List<tk2dSpriteCollectionFont>();
  			if (obj.fonts != null)
  			{
  				foreach (var v in obj.fonts)
  				{
  					if (v == null)
  						fonts.Add(null);
  					else
  					{
  						var t = new tk2dSpriteCollectionFont();
  						t.CopyFrom(v);
  						fonts.Add(t);
  					}
  				}
  			}
  
  			attachPointTestSprites.Clear();
  			foreach (var v in obj.attachPointTestSprites) {
  				if (v.spriteCollection != null && v.spriteCollection.IsValidSpriteId(v.spriteId)) {
  					tk2dSpriteCollection.AttachPointTestSprite ap = new tk2dSpriteCollection.AttachPointTestSprite();
  					ap.CopyFrom(v);
  					attachPointTestSprites[v.attachPointName] = v;
  				}
  			}
  
  			UpgradeLegacySpriteSheets();
  			
  			var target = this;
  			var source = obj;
  			
  			target.platforms = new List<tk2dSpriteCollectionPlatform>();
  			foreach (tk2dSpriteCollectionPlatform plat in source.platforms)
  			{
  				tk2dSpriteCollectionPlatform p = new tk2dSpriteCollectionPlatform();
  				p.CopyFrom(plat);
  				target.platforms.Add(p);
  			}
  			if (target.platforms.Count == 0)
  			{
  				tk2dSpriteCollectionPlatform plat = new tk2dSpriteCollectionPlatform(); // add a null platform
  				target.platforms.Add(plat);
  			}
  
  			target.assetName = source.assetName;
  			target.loadable = source.loadable;
  
  			target.maxTextureSize = source.maxTextureSize;
  			target.forceTextureSize = source.forceTextureSize;
  			target.forcedTextureWidth = source.forcedTextureWidth;
  			target.forcedTextureHeight = source.forcedTextureHeight;
  			
  			target.textureCompression = source.textureCompression;
  			target.atlasWidth = source.atlasWidth;
  			target.atlasHeight = source.atlasHeight;
  			target.forceSquareAtlas = source.forceSquareAtlas;
  			target.atlasWastage = source.atlasWastage;
  			target.allowMultipleAtlases = source.allowMultipleAtlases;
  			target.removeDuplicates = source.removeDuplicates;
  			
  			target.spriteCollection = source.spriteCollection;
  			target.premultipliedAlpha = source.premultipliedAlpha;
  			
  			CopyArray(ref target.altMaterials, source.altMaterials);
  			CopyArray(ref target.atlasMaterials, source.atlasMaterials);
  			CopyArray(ref target.atlasTextures, source.atlasTextures);
  			
  			target.sizeDef.CopyFrom( source.sizeDef );
  			target.globalScale = source.globalScale;
  			target.globalTextureRescale = source.globalTextureRescale;
  			target.physicsDepth = source.physicsDepth;
  			target.disableTrimming = source.disableTrimming;
  			target.normalGenerationMode = source.normalGenerationMode;
  			target.padAmount = source.padAmount;
  			target.autoUpdate = source.autoUpdate;
  			target.editorDisplayScale = source.editorDisplayScale;
  
  			// Texture settings
  			target.filterMode = source.filterMode;
  			target.wrapMode = source.wrapMode;
  			target.userDefinedTextureSettings = source.userDefinedTextureSettings;
  			target.mipmapEnabled = source.mipmapEnabled;
  			target.anisoLevel = source.anisoLevel;
  		}
  		
  		void CopyArray<T>(ref T[] dest, T[] source)
  		{
  			if (source == null)
  			{
  				dest = new T[0];
  			}
  			else
  			{
  				dest = new T[source.Length];
  				for (int i = 0; i < source.Length; ++i)
  					dest[i] = source[i];
  			}
  		}
  		
  		void UpgradeLegacySpriteSheets()
  		{
  			if (spriteSheets != null)
  			{
  				for (int i = 0; i < spriteSheets.Count; ++i)
  				{
  					var spriteSheet = spriteSheets[i];
  					if (spriteSheet != null && spriteSheet.version == 0)
  					{
  						if (spriteSheet.texture == null)
  						{
  							spriteSheet.active = false;
  						}
  						else
  						{
  							spriteSheet.tileWidth = spriteSheet.texture.width / spriteSheet.tilesX;
  							spriteSheet.tileHeight = spriteSheet.texture.height / spriteSheet.tilesY;
  							spriteSheet.active = true;
  							
  							for (int j = 0; j < textureParams.Count; ++j)
  							{
  								var param = textureParams[j];
  								if (param.fromSpriteSheet && param.texture == spriteSheet.texture)
  								{
  									param.fromSpriteSheet = false;
  									param.hasSpriteSheetId = true;
  									param.spriteSheetId = i;
  									
  									param.spriteSheetX = param.regionId % spriteSheet.tilesX;
  									param.spriteSheetY = param.regionId / spriteSheet.tilesX;
  								}
  							}
  						}
  						
  						spriteSheet.version = tk2dSpriteSheetSource.CURRENT_VERSION;
  					}
  				}				
  			}
  		}
  
  		public void DeleteUnusedData()
  		{
  			foreach (tk2dSpriteCollectionFont font in obj.fonts)
  			{
  				bool found = false;
  				foreach (tk2dSpriteCollectionFont f in fonts)
  				{
  					if (f.data == font.data && f.editorData == font.editorData)
  					{
  						found = true;
  						break;
  					}
  				}
  				if (!found)
  				{
  					tk2dEditorUtility.DeleteAsset(font.data);
  					tk2dEditorUtility.DeleteAsset(font.editorData);
  				}
  			}
  
  			if (obj.altMaterials != null)
  			{
  				foreach (Material material in obj.altMaterials)
  				{
  					bool found = false;
  					if (altMaterials != null)
  					{
  						foreach (Material m in altMaterials)
  						{
  							if (m == material)
  							{
  								found = true;
  								break;
  							}
  						}
  					}
  					if (!found)
  						tk2dEditorUtility.DeleteAsset(material);
  				}
  			}
  
  			List<tk2dSpriteCollectionPlatform> platformsToDelete = new List<tk2dSpriteCollectionPlatform>();
  			if (obj.HasPlatformData && !this.HasPlatformData)
  			{
  				platformsToDelete = new List<tk2dSpriteCollectionPlatform>(obj.platforms);
  				atlasTextures = new Texture2D[0]; // clear all references
  				atlasMaterials = new Material[0];
  			}
  			else if (this.HasPlatformData && !obj.HasPlatformData)
  			{
  				// delete old sprite collection atlases and materials
  				foreach (Material material in obj.atlasMaterials)
  					tk2dEditorUtility.DeleteAsset(material);
  				foreach (Texture2D texture in obj.atlasTextures)
  					tk2dEditorUtility.DeleteAsset(texture);
  			}
  			else if (obj.HasPlatformData && this.HasPlatformData)
  			{
  				foreach (tk2dSpriteCollectionPlatform platform in obj.platforms)
  				{
  					bool found = false;
  					foreach (tk2dSpriteCollectionPlatform p in platforms)
  					{
  						if (p.spriteCollection == platform.spriteCollection)
  						{
  							found = true;
  							break;
  						}
  					}
  					if (!found) // platform existed previously, but does not any more
  						platformsToDelete.Add(platform);
  				}
  			}
  
  			foreach (tk2dSpriteCollectionPlatform platform in platformsToDelete)
  			{
  				if (platform.spriteCollection == null) continue;
  				tk2dSpriteCollection sc = platform.spriteCollection;
  				string path = AssetDatabase.GetAssetPath(sc.spriteCollection);
  
  				tk2dEditorUtility.DeleteAsset(sc.spriteCollection);
  				foreach (Material material in sc.atlasMaterials)
  					tk2dEditorUtility.DeleteAsset(material);
  				foreach (Texture2D texture in sc.atlasTextures)
  					tk2dEditorUtility.DeleteAsset(texture);
  				foreach (tk2dSpriteCollectionFont font in sc.fonts)
  				{
  					tk2dEditorUtility.DeleteAsset(font.editorData);
  					tk2dEditorUtility.DeleteAsset(font.data);
  				}
  
  				tk2dEditorUtility.DeleteAsset(sc);
  
  				string dataDirName = System.IO.Path.GetDirectoryName(path);
  				if (System.IO.Directory.Exists(dataDirName) && System.IO.Directory.GetFiles(dataDirName).Length == 0)
  					AssetDatabase.DeleteAsset(dataDirName);
  			}
  		}
  
  		public void ClearReferences() {
  			altMaterials = new Material[0];
  			atlasMaterials = new Material[0];
  			atlasTextures = new Texture2D[0];
  			spriteCollection = null;
  
  			obj.altMaterials = new Material[0];
  			obj.atlasMaterials = new Material[0];
  			obj.atlasTextures = new Texture2D[0];
  			obj.spriteCollection = null;
  		}
  
  		public void CopyToTarget()
  		{
  			CopyToTarget(obj);
  		}
  		
  		public void CopyToTarget(tk2dSpriteCollection target)
  		{
  			target.textureParams = textureParams.ToArray();
  			target.spriteSheets = spriteSheets.ToArray();
  			target.fonts = fonts.ToArray();
  
  			var source = this;
  			target.platforms = new List<tk2dSpriteCollectionPlatform>();
  			foreach (tk2dSpriteCollectionPlatform plat in source.platforms)
  			{
  				tk2dSpriteCollectionPlatform p = new tk2dSpriteCollectionPlatform();
  				p.CopyFrom(plat);
  				target.platforms.Add(p);
  			}
  			target.assetName = source.assetName;
  			target.loadable = source.loadable;
  			
  			target.maxTextureSize = source.maxTextureSize;
  			target.forceTextureSize = source.forceTextureSize;
  			target.forcedTextureWidth = source.forcedTextureWidth;
  			target.forcedTextureHeight = source.forcedTextureHeight;
  			
  			target.textureCompression = source.textureCompression;
  			target.atlasWidth = source.atlasWidth;
  			target.atlasHeight = source.atlasHeight;
  			target.forceSquareAtlas = source.forceSquareAtlas;
  			target.atlasWastage = source.atlasWastage;
  			target.allowMultipleAtlases = source.allowMultipleAtlases;
  			target.removeDuplicates = source.removeDuplicates;
  			
  			target.spriteCollection = source.spriteCollection;
  			target.premultipliedAlpha = source.premultipliedAlpha;
  			
  			CopyArray(ref target.altMaterials, source.altMaterials);
  			CopyArray(ref target.atlasMaterials, source.atlasMaterials);
  			CopyArray(ref target.atlasTextures, source.atlasTextures);
  
  			target.sizeDef.CopyFrom( source.sizeDef );
  			target.globalScale = source.globalScale;
  			target.globalTextureRescale = source.globalTextureRescale;
  			target.physicsDepth = source.physicsDepth;
  			target.disableTrimming = source.disableTrimming;
  			target.normalGenerationMode = source.normalGenerationMode;
  			target.padAmount = source.padAmount; 
  			target.autoUpdate = source.autoUpdate;
  			target.editorDisplayScale = source.editorDisplayScale;
  
  			// Texture settings
  			target.filterMode = source.filterMode;
  			target.wrapMode = source.wrapMode;
  			target.userDefinedTextureSettings = source.userDefinedTextureSettings;
  			target.mipmapEnabled = source.mipmapEnabled;
  			target.anisoLevel = source.anisoLevel;
  
  			// Attach point test data
  			// Make sure we only store relevant data
  			HashSet<string> attachPointNames = new HashSet<string>();
  			foreach (tk2dSpriteCollectionDefinition def in textureParams) {
  				foreach (tk2dSpriteDefinition.AttachPoint ap in def.attachPoints) {
  					attachPointNames.Add(ap.name);
  				}
  			}
  			target.attachPointTestSprites.Clear();
  			foreach (string name in attachPointTestSprites.Keys) {
  				if (attachPointNames.Contains(name)) {
  					tk2dSpriteCollection.AttachPointTestSprite lut = new tk2dSpriteCollection.AttachPointTestSprite();
  					lut.CopyFrom( attachPointTestSprites[name] );
  					lut.attachPointName = name;
  					target.attachPointTestSprites.Add(lut);
  				}
  			}
  		}
  		
  		public bool AllowAltMaterials
  		{
  			get
  			{
  				return !allowMultipleAtlases;
  			}
  		}
  		
  		public int FindOrCreateEmptySpriteSlot()
  		{
  			for (int index = 0; index < textureParams.Count; ++index)
  			{
  				if (textureParams[index].texture == null && textureParams[index].name.Length == 0)
  					return index;
  			}
  			textureParams.Add(new tk2dSpriteCollectionDefinition());
  			return textureParams.Count - 1;
  		}
  		
  		public int FindOrCreateEmptyFontSlot()
  		{
  			for (int index = 0; index < fonts.Count; ++index)
  			{
  				if (!fonts[index].active)
  				{
  					fonts[index].active = true;
  					return index;
  				}
  			}
  			var font = new tk2dSpriteCollectionFont();
  			font.active = true;
  			fonts.Add(font);
  			return fonts.Count - 1;
  		}
  		
  		public int FindOrCreateEmptySpriteSheetSlot()
  		{
  			for (int index = 0; index < spriteSheets.Count; ++index)
  			{
  				if (!spriteSheets[index].active)
  				{
  					spriteSheets[index].active = true;
  					spriteSheets[index].version = tk2dSpriteSheetSource.CURRENT_VERSION;
  					return index;
  				}
  			}
  			var spriteSheet = new tk2dSpriteSheetSource();
  			spriteSheet.active = true;
  			spriteSheet.version = tk2dSpriteSheetSource.CURRENT_VERSION;
  			spriteSheets.Add(spriteSheet);
  			return spriteSheets.Count - 1;
  		}
  		
  		public string FindUniqueTextureName(string name)
  		{
  			int at = name.LastIndexOf('@');
  			if (at != -1) {
  				name = name.Substring(0, at);
  			}
  
  			List<string> textureNames = new List<string>();
  			foreach (var entry in textureParams)
  			{
  				textureNames.Add(entry.name);
  			}
  			if (textureNames.IndexOf(name) == -1)
  				return name;
  			int count = 1;
  			do 
  			{
  
gt: 				string currName = name + " " + count.ToString();