/** Returns if \a node is connected to it's neighbour in the specified direction */
		public static bool CheckConnection (LevelGridNode node, int dir) {
			return node.GetConnection (dir);
		}
		/** Updates penalty for the node.
		 * This function sets penalty to zero (0) and then adjusts it if #penaltyPosition is set to true.
		 */
		public virtual void UpdatePenalty (LevelGridNode node) {

			node.Penalty = 0;//Mathf.RoundToInt (Random.value*100);
			node.Penalty = initialPenalty;

			if (penaltyPosition) {
				node.Penalty += (uint)Mathf.RoundToInt ((node.position.y-penaltyPositionOffset)*penaltyPositionFactor);
			}
		}
		public override void ScanInternal (OnScanStatus statusCallback) {

			if (nodeSize <= 0) {
				return;
			}

			GenerateMatrix ();

			if (width > 1024 || depth > 1024) {
				Debug.LogError ("One of the grid's sides is longer than 1024 nodes");
				return;
			}

			lastScannedWidth = width;
			lastScannedDepth = depth;

			SetUpOffsetsAndCosts ();

			LevelGridNode.SetGridGraph (active.astarData.GetGraphIndex(this), this);

			maxClimb = Mathf.Clamp (maxClimb,0,characterHeight);

			var linkedCells = new LinkedLevelCell[width*depth];

			collision = collision ?? new GraphCollision ();
			collision.Initialize (matrix,nodeSize);

			for (int z = 0; z < depth; z ++) {
				for (int x = 0; x < width; x++) {

					linkedCells[z*width+x] = new LinkedLevelCell ();

					LinkedLevelCell llc = linkedCells[z*width+x];

					Vector3 pos = matrix.MultiplyPoint3x4 (new Vector3 (x+0.5F,0,z+0.5F));


					RaycastHit[] hits = collision.CheckHeightAll (pos);

					for (int i=0;i<hits.Length/2;i++) {
						RaycastHit tmp = hits[i];

						hits[i] = hits[hits.Length-1-i];
						hits[hits.Length-1-i] = tmp;
					}

					if (hits.Length > 0) {

						LinkedLevelNode lln = null;

						for (int i=0;i<hits.Length;i++) {

							var tmp = new LinkedLevelNode ();
							tmp.position = hits[i].point;

							if (lln != null) {
								/** \todo Use hit.distance instead */
								if (tmp.position.y - lln.position.y <= mergeSpanRange) {
										lln.position = tmp.position;
										lln.hit = hits[i];
										lln.walkable = collision.Check (tmp.position);
									continue;
								}
							}

							tmp.walkable = collision.Check (tmp.position);
							tmp.hit = hits[i];
							tmp.height = float.PositiveInfinity;

							if (llc.first == null) {
								llc.first = tmp;
								lln = tmp;
							} else {
								lln.next = tmp;

								lln.height = tmp.position.y - lln.position.y;
								lln = lln.next;
							}

						}
					} else {
						var lln = new LinkedLevelNode ();
						lln.position = pos;
						lln.height = float.PositiveInfinity;
						lln.walkable = !collision.unwalkableWhenNoGround;
						llc.first = lln;
					}
				}
			}


			int spanCount = 0;
			layerCount = 0;
			// Count the total number of nodes in the graph
			for (int z = 0; z < depth; z ++) {
				for (int x = 0; x < width; x++) {

					LinkedLevelCell llc = linkedCells[z*width+x];

					LinkedLevelNode lln = llc.first;
					int cellCount = 0;
					// Loop through all nodes in this cell
					do {
						cellCount++;
						spanCount++;
						lln = lln.next;
					} while (lln != null);

					layerCount = cellCount > layerCount ? cellCount : layerCount;
				}
			}

			if (layerCount > LevelGridNode.MaxLayerCount) {
				Debug.LogError ("Too many layers, a maximum of LevelGridNode.MaxLayerCount are allowed (found "+layerCount+")");
				return;
			}

			// Create all nodes
			nodes = new LevelGridNode[width*depth*layerCount];
			for (int i=0;i<nodes.Length;i++) {
				nodes[i] = new LevelGridNode (active);
				nodes[i].Penalty = initialPenalty;
			}

			int nodeIndex = 0;

			// Max slope in cosinus
			float cosAngle = Mathf.Cos (maxSlope*Mathf.Deg2Rad);

			for (int z = 0; z < depth; z++) {
				for (int x = 0; x < width; x++) {

					LinkedLevelCell llc = linkedCells[z*width+x];
					LinkedLevelNode lln = llc.first;

					llc.index = nodeIndex;

					int count = 0;
					int layerIndex = 0;
					do {
						var node = nodes[z*width+x + width*depth*layerIndex];
#if ASTAR_SET_LEVELGRIDNODE_HEIGHT
						node.height = lln.height;
#endif
						node.SetPosition ((Int3)lln.position);
						node.Walkable = lln.walkable;

						// Adjust penalty based on the surface slope
						if (lln.hit.normal != Vector3.zero && (penaltyAngle || cosAngle < 1.0f)) {
							//Take the dot product to find out the cosinus of the angle it has (faster than Vector3.Angle)
							float angle = Vector3.Dot (lln.hit.normal.normalized,collision.up);

							// Enqueue penalty based on normal
							if (penaltyAngle) {
								node.Penalty += (uint)Mathf.RoundToInt ((1F-angle)*penaltyAngleFactor);
							}

							// Check if the slope is flat enough to stand on
							if (angle < cosAngle) {
								node.Walkable = false;
							}
						}

						node.NodeInGridIndex = z*width+x;

						if (lln.height < characterHeight) {
							node.Walkable = false;
						}

						node.WalkableErosion = node.Walkable;

						nodeIndex++;
						count++;
						lln = lln.next;
						layerIndex++;
					} while (lln != null);

					for (;layerIndex<layerCount;layerIndex++) {
						nodes[z*width+x + width*depth*layerIndex] = null;
					}

					llc.count = count;
				}
			}

			nodeIndex = 0;

			nodeCellIndices = new int[linkedCells.Length];

			for (int z = 0; z < depth; z ++) {
				for (int x = 0; x < width; x++) {
					for (int i=0;i<layerCount;i++) {
						GraphNode node = nodes[z*width+x + width*depth*i];
						CalculateConnections (nodes,node,x,z,i);
					}
				}
			}

			uint graphIndex = (uint)active.astarData.GetGraphIndex(this);

			for (int i=0;i<nodes.Length;i++) {
				var lgn = nodes[i];
				if (lgn == null) continue;

				UpdatePenalty (lgn);

				lgn.GraphIndex = graphIndex;

				// Set the node to be unwalkable if it hasn't got any connections
				if (!lgn.HasAnyGridConnections ()) {
					lgn.Walkable = false;
					lgn.WalkableErosion = lgn.Walkable;
				}
			}

			ErodeWalkableArea ();
		}
		/** Recalculates single cell.
		 *
		 * \param x X coordinate of the cell
		 * \param z Z coordinate of the cell
		 * \param preserveExistingNodes If true, nodes will be reused, this can be used to preserve e.g penalty when recalculating
		 *
		 * \returns If new layers or nodes were added. If so, you need to call
		 * AstarPath.active.DataUpdate() after this function to make sure pathfinding works correctly for them
		 * (when doing a scan, that function does not need to be called however).
		 *
		 * \note Connections are not recalculated for the nodes.
		 */
		public bool RecalculateCell (int x, int z, bool preserveExistingNodes) {

			var llc = new LinkedLevelCell ();

			Vector3 pos = matrix.MultiplyPoint3x4 (new Vector3 (x+0.5F,0,z+0.5F));


			RaycastHit[] hits = collision.CheckHeightAll (pos);

			for (int i=0;i<hits.Length/2;i++) {
				RaycastHit tmp = hits[i];

				hits[i] = hits[hits.Length-1-i];
				hits[hits.Length-1-i] = tmp;
			}

			bool addedNodes = false;

			if (hits.Length > 0) {
				LinkedLevelNode lln = null;

				for (int i=0;i<hits.Length;i++) {

					var tmp = new LinkedLevelNode ();
					tmp.position = hits[i].point;

					if (lln != null) {
						/** \todo Use hit.distance instead */
						if (tmp.position.y - lln.position.y <= mergeSpanRange) {
							lln.position = tmp.position;
							lln.hit = hits[i];
							lln.walkable = collision.Check (tmp.position);
							continue;
						}
					}

					tmp.walkable = collision.Check (tmp.position);
					tmp.hit = hits[i];
					tmp.height = float.PositiveInfinity;

					if (llc.first == null) {
						llc.first = tmp;
						lln = tmp;
					} else {
						lln.next = tmp;

						lln.height = tmp.position.y - lln.position.y;
						lln = lln.next;
					}

				}
			} else {
				var lln = new LinkedLevelNode ();
				lln.position = pos;
				lln.height = float.PositiveInfinity;
				lln.walkable = !collision.unwalkableWhenNoGround;
				llc.first = lln;
			}


			//=========

			uint graphIndex = (uint)active.astarData.GetGraphIndex(this);

			{
				//llc
				LinkedLevelNode lln = llc.first;

				int count = 0;
				int layerIndex = 0;
				do {

					if (layerIndex >= layerCount) {
						if (layerIndex+1 > LevelGridNode.MaxLayerCount) {
							Debug.LogError ("Too many layers, a maximum of LevelGridNode.MaxLayerCount are allowed (required "+(layerIndex+1)+")");
							return addedNodes;
						}

						AddLayers (1);
						addedNodes = true;
					}

					var node = nodes[z*width+x + width*depth*layerIndex];

					if (node == null || !preserveExistingNodes) {
						//Create a new node
						nodes[z*width+x + width*depth*layerIndex] = new LevelGridNode(active);
						node = nodes[z*width+x + width*depth*layerIndex];
						node.Penalty = initialPenalty;
						node.GraphIndex = graphIndex;
						addedNodes = true;
					}

					//node.connections = null;
#if ASTAR_SET_LEVELGRIDNODE_HEIGHT
					node.height = lln.height;
#endif
					node.SetPosition ((Int3)lln.position);
					node.Walkable = lln.walkable;
					node.WalkableErosion = node.Walkable;

					//Adjust penalty based on the surface slope
					if (lln.hit.normal != Vector3.zero) {
						//Take the dot product to find out the cosinus of the angle it has (faster than Vector3.Angle)
						float angle = Vector3.Dot (lln.hit.normal.normalized,collision.up);

						//Enqueue penalty based on normal
						if (penaltyAngle) {
							node.Penalty += (uint)Mathf.RoundToInt ((1F-angle)*penaltyAngleFactor);
						}

						//Max slope in cosinus
						float cosAngle = Mathf.Cos (maxSlope*Mathf.Deg2Rad);

						//Check if the slope is flat enough to stand on
						if (angle < cosAngle) {
							node.Walkable = false;
						}
					}

					node.NodeInGridIndex = z*width+x;

					if (lln.height < characterHeight) {
						node.Walkable = false;
					}
					count++;
					lln = lln.next;
					layerIndex++;
				} while (lln != null);

				for (;layerIndex<layerCount;layerIndex++) {
					nodes[z*width+x + width*depth*layerIndex] = null;
				}

				llc.count = count;
			}

			return addedNodes;
		}
		public override void DeserializeExtraInfo (GraphSerializationContext ctx)
		{

			int count = ctx.reader.ReadInt32();
			if (count == -1) {
				nodes = null;
				return;
			}

			nodes = new LevelGridNode[count];
			for (int i=0;i<nodes.Length;i++) {
				if (ctx.reader.ReadInt32() != -1 ){
					nodes[i] = new LevelGridNode (active);
					nodes[i].DeserializeNode(ctx);
				} else {
					nodes[i] = null;
				}
			}
		}