/// <inheritdoc/>
        protected internal override void PrepareTileData(IBrushContext context, TileData tile, int variationIndex)
        {
            var tileSystem = context.TileSystem;

            // Find actual orientation of target tile.
            int actualOrientation = OrientationUtility.DetermineTileOrientation(tileSystem, context.Row, context.Column, context.Brush, tile.PaintedRotation);
            // Try to find nearest match, assume default scenario.
            int bestOrientation = this.FindClosestOrientationMask(actualOrientation);

            // Select tile for painting using tile brush.
            var orientation = this.FindOrientation(bestOrientation);

            if (orientation == null)
            {
                Debug.LogWarning(string.Format("Brush '{0}' orientation '{1}' not defined", this.name, OrientationUtility.NameFromMask(bestOrientation)));
                return;
            }

            int orientationVariationCount = orientation.VariationCount;

            // Randomize variation?
            if (variationIndex == RANDOM_VARIATION)
            {
                if (!tileSystem.BulkEditMode || tileSystem.IsEndingBulkEditMode)
                {
                    variationIndex = orientation.PickRandomVariationIndex();
                }
            }
            // Negative offset from variations of orientation.
            else if (variationIndex < 0)
            {
                variationIndex = Mathf.Max(0, orientationVariationCount + variationIndex);
            }

            // Ensure variation index is within bounds of orientation!
            int wrappedVariationIndex = variationIndex;

            if (wrappedVariationIndex >= orientationVariationCount)
            {
                wrappedVariationIndex = 0;
            }

            if (orientationVariationCount > 0)
            {
                //
                // Could re-insert following to 'fix' state of randomly selected tiles.
                // Note: Randomization is lost when erasing tiles from mass filled areas.
                //
                //// Persist wrapped variation when painting tile?
                //if (!system.BulkEditMode || system.IsEndingBulkEditMode) {
                //    variationIndex = wrappedVariationIndex;
                //}

                // Fetch nested brush reference (if there is one).
                var nestedBrush = orientation.GetVariation(wrappedVariationIndex) as Brush;
                if (nestedBrush != null)
                {
                    // Prepare tile data using nested brush.
                    nestedBrush.PrepareTileData(context, tile, wrappedVariationIndex);
                }
            }

            tile.orientationMask = (byte)bestOrientation;
            tile.variationIndex  = (byte)variationIndex;

            // Do not attempt automatic rotation where rotation has been manually specified.
            int rotation = tile.PaintedRotation + orientation.Rotation;

            if (rotation > 3)
            {
                rotation -= 4;
            }
            tile.Rotation = rotation;
        }
        /// <inheritdoc/>
        protected internal override void CreateTile(IBrushContext context, TileData tile)
        {
            var orientation = this.FindOrientation(tile.orientationMask);

            if (orientation == null)
            {
                Debug.LogWarning(string.Format("Brush '{0}' orientation '{1}' not defined", this.name, OrientationUtility.NameFromMask(tile.orientationMask)));
                return;
            }

            if (orientation.VariationCount == 0)
            {
                Debug.LogWarning(string.Format("Brush '{0}' orientation '{1}' has no variations", this.name, OrientationUtility.NameFromMask(tile.orientationMask)));
                return;
            }

            // Note: Do not update variation index in tile data because this may be unintended!
            int variationIndex = (int)tile.variationIndex;

            if (variationIndex >= orientation.VariationCount)
            {
                variationIndex = 0;
            }

            var orientedVariation = orientation.GetVariation(variationIndex);

            // Orchestrate a "nested" brush?
            var nestedBrush = orientedVariation as Brush;

            if (nestedBrush != null)
            {
                // Force override flags with that of nested brush?
                //
                // Note: Naming a little backwards in this scenario, we want to preserve
                //       flags from nested brush!
                //
                if (!this.forceOverrideFlags)
                {
                    // Remove all user flags, solid flag and replace with flags from nested brush.
                    tile.flags = (tile.flags & ~0x8FFFF) | nestedBrush.TileFlags;
                }

                nestedBrush.CreateTile(context, tile);
                nestedBrush.PostProcessTile(context, tile);
                return;
            }

            var variationPrefab = orientedVariation as GameObject;

            if (variationPrefab == null)
            {
                return;
            }

            // Actually create tile!
            tile.gameObject = InstantiatePrefabForTile(variationPrefab, tile, context.TileSystem);
        }
        /// <summary>
        /// Finds mask of orientation that best matches the specified orientation mask.
        /// </summary>
        /// <param name="mask">Bit mask of tile orientation.</param>
        /// <returns>
        /// Bit mask of the closest available orientation.
        /// </returns>
        public int FindClosestOrientationMask(int mask)
        {
            // Is desired orientation available?
            if (this.FindOrientation(mask) != null)
            {
                return(mask);
            }

            // Find nearest match.
            int strongestConnections = 2;
            int weakConnections      = 0;
            int strongestOrientation = this.defaultOrientationMask;

            int childOrientation, s, w;

            if (FallbackMode == Tile.FallbackMode.NextBest)
            {
                for (int i = 0; i < this.orientations.Length; ++i)
                {
                    childOrientation = this.orientations[i].Mask;

                    // If there are at least 3 strong connections...
                    s = OrientationUtility.CountStrongConnections(mask, childOrientation);
                    if (s > strongestConnections)
                    {
                        // Strong connections overule any previous weak connection matches!
                        strongestConnections = s;
                        weakConnections      = OrientationUtility.CountWeakConnections(mask, childOrientation);
                        strongestOrientation = childOrientation;
                    }
                    // If this connection is just as strong as the previous then we can determine
                    // which one is better by looking at the weak connections.
                    else if (s == strongestConnections)
                    {
                        // Choose the connection that has the most weak connections!
                        w = OrientationUtility.CountWeakConnections(mask, childOrientation);
                        if (w > weakConnections)
                        {
                            strongestOrientation = childOrientation;
                            weakConnections      = w;
                        }
                    }
                }
            }
            else if (FallbackMode == Tile.FallbackMode.UseDefault)
            {
                for (int i = 0; i < this.orientations.Length; ++i)
                {
                    childOrientation = this.orientations[i].Mask;

                    // When using default mode there must be exactly 4 strong connections!
                    if (OrientationUtility.CountStrongConnections(mask, childOrientation) == 4)
                    {
                        w = OrientationUtility.CountWeakConnections(mask, childOrientation);

                        // Has a strong connection been found for the first time?
                        // Otherwise, choose the connection that has the most weak connections!
                        if (strongestConnections == 2 || w > weakConnections)
                        {
                            strongestConnections = 4;
                            weakConnections      = w;
                            strongestOrientation = childOrientation;
                        }
                    }
                }
            }
            else if (FallbackMode == Tile.FallbackMode.UseDefaultStrict)
            {
                for (int i = 0; i < this.orientations.Length; ++i)
                {
                    childOrientation = this.orientations[i].Mask;

                    // When using default mode there must be exactly 4 strong connections!
                    if (OrientationUtility.CountStrongConnections(mask, childOrientation) == 4 && OrientationUtility.CountWeakConnections(mask, childOrientation) == 4)
                    {
                        strongestOrientation = childOrientation;
                    }
                }
            }

            return(strongestOrientation);
        }