context: using UnityEngine;
  using System.Collections.Generic;
  
  #if UNITY_EDITOR || !UNITY_FLASH
  
  namespace tk2dRuntime.TileMap
  {
  	public static class BuilderUtil
  	{
  		/// Syncs layer data and makes sure data is valid
  		public static bool InitDataStore(tk2dTileMap tileMap)
  		{
  			bool dataChanged = false;
  			int numLayers = tileMap.data.NumLayers;
  			if (tileMap.Layers == null)
  			{
  				tileMap.Layers = new Layer[numLayers];
  				for (int i = 0; i < numLayers; ++i)
  					tileMap.Layers[i] = new Layer(tileMap.data.Layers[i].hash, tileMap.width, tileMap.height, tileMap.partitionSizeX, tileMap.partitionSizeY);
  				dataChanged = true;
  			}
  			else
  			{
  				// link up layer hashes
  				Layer[] newLayers = new Layer[numLayers];
  				
  				for (int i = 0; i < numLayers; ++i)
  				{
  					var layerInfo = tileMap.data.Layers[i];
  					bool found = false;
  	
  					// Find an existing layer with this hash
  					for (int j = 0; j < tileMap.Layers.Length; ++j)
  					{
  						if (tileMap.Layers[j].hash == layerInfo.hash)
  						{
  							newLayers[i] = tileMap.Layers[j];
  							found = true;
  							break;
  						}
  					}
  					
  					if (!found)
  						newLayers[i] = new Layer(layerInfo.hash, tileMap.width, tileMap.height, tileMap.partitionSizeX, tileMap.partitionSizeY);
  				}
  					
  				// Identify if it has changed
  				int numActiveLayers = 0;
  				foreach (var layer in newLayers)
  					if (!layer.IsEmpty)
  						numActiveLayers++;
  				
  				int numPreviousActiveLayers = 0;
  				foreach (var layer in tileMap.Layers)
  				{
  					if (!layer.IsEmpty)
  						numPreviousActiveLayers++;
  				}
  				
  				if (numActiveLayers != numPreviousActiveLayers)
  					dataChanged = true;
  				
  				tileMap.Layers = newLayers;
  			}
  			
  			if (tileMap.ColorChannel == null)
  			{
  				tileMap.ColorChannel = new ColorChannel(tileMap.width, tileMap.height, tileMap.partitionSizeX, tileMap.partitionSizeY);
  			}
  			
  			return dataChanged;
  		}		
  
  		static List<int> TilePrefabsX;
  		static List<int> TilePrefabsY;
  		static List<int> TilePrefabsLayer;
  		static List<GameObject> TilePrefabsInstance;
  
  		static GameObject GetExistingTilePrefabInstance(tk2dTileMap tileMap, int tileX, int tileY, int tileLayer) {
  			int n = tileMap.GetTilePrefabsListCount();
  			for (int i = 0; i < n; ++i) {
  				int x, y, layer;
  				GameObject instance;
  				tileMap.GetTilePrefabsListItem(i, out x, out y, out layer, out instance);
  				if (x == tileX && y == tileY && layer == tileLayer)
  					return instance;
  			}
  			return null;
  		}
  		
  		/// Spawns all prefabs for a given chunk
  		/// Expects the chunk to have a valid GameObject
  		public static void SpawnPrefabsForChunk(tk2dTileMap tileMap, SpriteChunk chunk, int baseX, int baseY, int layer, int[] prefabCounts)
  		{
  			var chunkData = chunk.spriteIds;
  			var tilePrefabs = tileMap.data.tilePrefabs;
  			Vector3 tileSize = tileMap.data.tileSize;
  			var parent = chunk.gameObject.transform;
  			
  			float xOffsetMult = 0.0f, yOffsetMult = 0.0f;
  			tileMap.data.GetTileOffset(out xOffsetMult, out yOffsetMult);
  
  			for (int y = 0; y < tileMap.partitionSizeY; ++y)
  			{
  				float xOffset = ((baseY + y) & 1) * xOffsetMult;
  				for (int x = 0; x < tileMap.partitionSizeX; ++x)
  				{
  					int tile = GetTileFromRawTile(chunkData[y * tileMap.partitionSizeX + x]);
  					if (tile < 0 || tile >= tilePrefabs.Length)
  						continue;
  
  					Object prefab = tilePrefabs[tile];
  					if (prefab != null)
  					{
  						prefabCounts[tile]++;
  
  						GameObject instance = GetExistingTilePrefabInstance(tileMap, baseX + x, baseY + y, layer);
  						bool foundExisting = (instance != null);
  
  					#if UNITY_EDITOR
  						if (instance != null) {
  							if (UnityEditor.PrefabUtility.GetCorrespondingObjectFromSource(instance) != prefab) {
  								instance = null;
  							}
  						}
  					#endif
  
  						if (instance == null) {
  					#if UNITY_EDITOR
  							instance = UnityEditor.PrefabUtility.InstantiatePrefab(prefab) as GameObject;
  					#else
  							instance = GameObject.Instantiate(prefab, Vector3.zero, Quaternion.identity) as GameObject;
  					#endif
  
  					#if UNITY_EDITOR && !(UNITY_3_5 || UNITY_4_0 || UNITY_4_0_1 || UNITY_4_1 || UNITY_4_2)
  							if (!Application.isPlaying) {
  								UnityEditor.Undo.RegisterCreatedObjectUndo(instance, "Instantiated Prefab");
  							}
  					#endif
  						}
  						
  						if (instance != null) {
  							GameObject prefabGameObject = prefab as GameObject;
  
  							Vector3 pos = new Vector3(tileSize.x * (x + xOffset), tileSize.y * y, 0);
  							bool enablePrefabOffset = false;
  							var tileInfo = tileMap.data.GetTileInfoForSprite(tile);
  							if (tileInfo != null)
  								enablePrefabOffset = tileInfo.enablePrefabOffset;
  							if (enablePrefabOffset && prefabGameObject != null)
  								pos += prefabGameObject.transform.position;
  
  							if (!foundExisting)
  								instance.name = prefab.name + " " + prefabCounts[tile].ToString();
  
  							tk2dUtil.SetTransformParent(instance.transform, parent);
  							instance.transform.localPosition = pos;
  
  							// Add to tilePrefabs list
  							TilePrefabsX.Add(baseX + x);
  							TilePrefabsY.Add(baseY + y);
  							TilePrefabsLayer.Add(layer);
  							TilePrefabsInstance.Add(instance);
  						}
  					}
  				}
  			}
  		}
  		
  		/// Spawns all prefabs for a given tilemap
  		/// Expects populated chunks to have valid GameObjects
  		public static void SpawnPrefabs(tk2dTileMap tileMap, bool forceBuild) 
  		{
  			// Restart these lists that will be stored in the tileMap tilePrefabsList
  			TilePrefabsX = new List<int>();
  			TilePrefabsY = new List<int>();
  			TilePrefabsLayer = new List<int>();
  			TilePrefabsInstance = new List<GameObject>();
  
  			int[] prefabCounts = new int[tileMap.data.tilePrefabs.Length];
  
  			int numLayers = tileMap.Layers.Length;
  			for (int layerId = 0; layerId < numLayers; ++layerId)
  			{
  				var layer = tileMap.Layers[layerId];
  				var layerData = tileMap.data.Layers[layerId];
  
  				// We skip offsetting the first one
  				if (layer.IsEmpty || layerData.skipMeshGeneration)
  					continue;
  
  				for (int cellY = 0; cellY < layer.numRows; ++cellY)
  				{
  					int baseY = cellY * layer.divY;
  					for (int cellX = 0; cellX < layer.numColumns; ++cellX)
  					{
  						int baseX = cellX * layer.divX;
  						var chunk = layer.GetChunk(cellX, cellY);
  						if (chunk.IsEmpty)
  							continue;
  						if (!forceBuild && !chunk.Dirty)
  							continue;
  						
  						SpawnPrefabsForChunk(tileMap, chunk, baseX, baseY, layerId, prefabCounts);
  					}
  				}
  			}
  
  			tileMap.SetTilePrefabsList(TilePrefabsX, TilePrefabsY, TilePrefabsLayer, TilePrefabsInstance);
  		}
  
  		/// <summary>
  		/// Moves the chunk's gameobject's children to the prefab root
  		/// </summary>
  		public static void HideTileMapPrefabs(tk2dTileMap tileMap) {
  			if (tileMap.renderData == null || tileMap.Layers == null) {
  				// No Render Data to parent Prefab Root to
  				return;
  			}
  
  			if (tileMap.PrefabsRoot == null) {
  				var go = tileMap.PrefabsRoot = tk2dUtil.CreateGameObject("Prefabs");
  				go.transform.parent = tileMap.renderData.transform;
  				go.transform.localPosition = Vector3.zero;
  				go.transform.localRotation = Quaternion.identity;
  				go.transform.localScale = Vector3.one;
  			}
  
  			int instListCount = tileMap.GetTilePrefabsListCount();
  			bool[] instExists = new bool[instListCount];
  
  			for (int i = 0; i < tileMap.Layers.Length; ++i) {
  				var layer = tileMap.Layers[i];
  				for (int j = 0; j < layer.spriteChannel.chunks.Length; ++j) {
  					var chunk = layer.spriteChannel.chunks[j];
  					if (chunk.gameObject == null)
  						continue;
  
  					var t = chunk.gameObject.transform;
  					int childCount = t.childCount;
  					for (int k = 0; k < childCount; ++k) {
  						GameObject go = t.GetChild(k).gameObject;
  						for (int q = 0; q < instListCount; ++q) {
  							int x, y, layerIdx;
  							GameObject instance;
  							tileMap.GetTilePrefabsListItem(q, out x, out y, out layerIdx, out instance);
  							if (instance == go) {
  								instExists[q] = true;
  								break;
  							}
  						}
  					}
  				}
  			}
  
  			Object[] prefabs = tileMap.data.tilePrefabs;
  			List<int> tileX = new List<int>();
  			List<int> tileY = new List<int>();
  			List<int> tileLayer = new List<int>();
  			List<GameObject> tileInst = new List<GameObject>();
  			for (int i = 0; i < instListCount; ++i) {
  				int x, y, layerIdx;
  				GameObject instance;
  				tileMap.GetTilePrefabsListItem(i, out x, out y, out layerIdx, out instance);
  				
  				// Is it already IN the list for some reason?
  				if (!instExists[i]) {
  					int tileId = (x >= 0 && x < tileMap.width && y >= 0 && y < tileMap.height) ? tileMap.GetTile(x, y, layerIdx) : -1;
  					if (tileId >= 0 && tileId < prefabs.Length && prefabs[tileId] != null) {
  						instExists[i] = true;
  					}
  				}
  				
  				if (instExists[i]) {
  					tileX.Add(x);
  					tileY.Add(y);
  					tileLayer.Add(layerIdx);
  					tileInst.Add(instance);
  
  					tk2dUtil.SetTransformParent(instance.transform, tileMap.PrefabsRoot.transform);
  				}
  			}
  			
  			tileMap.SetTilePrefabsList(tileX, tileY, tileLayer, tileInst);
  		}
  		
  		static Vector3 GetTilePosition(tk2dTileMap tileMap, int x, int y)
  		{
  			return new Vector3(tileMap.data.tileSize.x * x, tileMap.data.tileSize.y * y, 0.0f);	
  		}
  
  		/// Creates render data for given tilemap
  		public static void CreateRenderData(tk2dTileMap tileMap, bool editMode, Dictionary<Layer, bool> layersActive)
  		{
  			// Create render data
  			if (tileMap.renderData == null)
  				tileMap.renderData = tk2dUtil.CreateGameObject(tileMap.name + " Render Data");
  	
  			tileMap.renderData.transform.position = tileMap.transform.position;
  			
  			float accumulatedLayerZ = 0.0f;
  			
  			// Create all objects
  			int layerId = 0;
  			foreach (var layer in tileMap.Layers)
  			{
  				// We skip offsetting the first one
  				float layerInfoZ = tileMap.data.Layers[layerId].z;
  				if (layerId != 0)
  					accumulatedLayerZ -= layerInfoZ;
  				
  				if (layer.IsEmpty && layer.gameObject != null)
  				{
  					tk2dUtil.DestroyImmediate(layer.gameObject);
  					layer.gameObject = null;
  				}
  
gt: 				else if (!layer.IsEmpty && layer.gameObject == null)