private async Task FixInnerSquare(List<IRotation> solution, CubeConfiguration<FaceColour> configuration)
        {
            var upperColour = configuration.Faces[FaceType.Front].GetEdge(configuration.MinInnerLayerIndex(), Edge.Top).Centre();
            var downColour = configuration.Faces[FaceType.Front].GetEdge(configuration.MinInnerLayerIndex(), Edge.Bottom).Centre();

            var correctFaceForTopRow = FaceRules.GetFaceOfColour(upperColour, configuration);
            var correctFaceForBottomRow = FaceRules.GetFaceOfColour(downColour, configuration);

            await MoveRowToCorrectFace(correctFaceForTopRow, solution, configuration, true).ConfigureAwait(false);

            await MoveRowToCorrectFace(correctFaceForBottomRow, solution, configuration, false).ConfigureAwait(false);

        }
        public async Task<IEnumerable<IRotation>> Solve(CubeConfiguration<FaceColour> configuration)
        {
            if (m_layer < 0)
            {
                m_layer = configuration.MinInnerLayerIndex();
            }

            var solution = new List<IRotation>();

            var rotationToBottom = await CommonActions.PositionOnFront(configuration, m_faceColour).ConfigureAwait(false);

            if (rotationToBottom != null) solution.Add(rotationToBottom);

            for (int i = 0; i <= 3; i++)
            {
                await CheckFace(configuration, FaceType.Upper, solution).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(CubeRotations.ZClockwise, solution, configuration).ConfigureAwait(false);

            }

            await CheckFace(configuration, FaceType.Back, solution, false, true).ConfigureAwait(false);


            return solution;
        }
        private async Task CheckMiddleLayersOnFace(CubeConfiguration<FaceColour> configuration, List<IRotation> solution, FaceType face)
        {
            var frontFaceColour = configuration.Faces[FaceType.Front].LeftCentre();
            var leftFaceColour = configuration.Faces[FaceType.Left].RightCentre();

            var rightEdgeOnFace = configuration.Faces[face].GetEdge(Edge.Right);
            var top = rightEdgeOnFace[configuration.MinInnerLayerIndex()];
            var bottom = rightEdgeOnFace[configuration.MaxInnerLayerIndex()];

            var joiningFace = FaceRules.FaceAtRelativePositionTo(face, RelativePosition.Right);
            var leftEdgeOnJoiningFace = configuration.Faces[joiningFace].GetEdge(Edge.Left);
            var joiningFaceEdgeTop = leftEdgeOnJoiningFace[configuration.MinInnerLayerIndex()];
            var joiningFaceEdgeBottom = leftEdgeOnJoiningFace[configuration.MaxInnerLayerIndex()];


            if ((top == frontFaceColour && joiningFaceEdgeTop == leftFaceColour) ||
                (bottom == frontFaceColour && joiningFaceEdgeBottom == leftFaceColour) ||
                (top == leftFaceColour && joiningFaceEdgeTop == frontFaceColour) ||
                (bottom == leftFaceColour && joiningFaceEdgeBottom == frontFaceColour))
            {
                var rotationToBringEdgeToFront = GetRotationToPutTredgeOnFront((top == frontFaceColour && joiningFaceEdgeTop == leftFaceColour) || (top == leftFaceColour && joiningFaceEdgeTop == frontFaceColour), face, configuration.MinInnerLayerIndex());
                await CommonActions.ApplyAndAddRotation(rotationToBringEdgeToFront, solution, configuration).ConfigureAwait(false);

            }

            if ((top == frontFaceColour && joiningFaceEdgeTop == leftFaceColour) ||
                (bottom == frontFaceColour && joiningFaceEdgeBottom == leftFaceColour))
            {
                await PerformFlip(solution, configuration).ConfigureAwait(false);

            }

            rightEdgeOnFace = configuration.Faces[face].GetEdge(Edge.Right);
            top = rightEdgeOnFace[configuration.MinInnerLayerIndex()];
            bottom = rightEdgeOnFace[configuration.MaxInnerLayerIndex()];

            leftEdgeOnJoiningFace = configuration.Faces[joiningFace].GetEdge(Edge.Left);
            joiningFaceEdgeTop = leftEdgeOnJoiningFace[configuration.MinInnerLayerIndex()];
            joiningFaceEdgeBottom = leftEdgeOnJoiningFace[configuration.MaxInnerLayerIndex()];


            if (top == leftFaceColour && joiningFaceEdgeTop == frontFaceColour)
            {
                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerUpperClockwise, solution, configuration).ConfigureAwait(false);

            }
            if (bottom == leftFaceColour && joiningFaceEdgeBottom == frontFaceColour)
            {
                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerDownAntiClockwise, solution, configuration).ConfigureAwait(false);

            }
        }
        private async Task CheckFlipped(CubeConfiguration<FaceColour> configuration, List<IRotation> solution)
        {
            var frontFaceEdge = configuration.Faces[FaceType.Front].GetEdge(Edge.Left);
            var leftFaceEdge = configuration.Faces[FaceType.Left].GetEdge(Edge.Right);

            var frontColour = frontFaceEdge.Centre();
            var leftColour = leftFaceEdge.Centre();

            if (frontFaceEdge[configuration.MinInnerLayerIndex()] == leftColour && frontFaceEdge[configuration.MaxInnerLayerIndex()] == leftColour &&
                leftFaceEdge[configuration.MinInnerLayerIndex()] == frontColour && leftFaceEdge[configuration.MaxInnerLayerIndex()] == frontColour)
            {
                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerUpperAntiClockwise, solution, configuration).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerDownClockwise, solution, configuration).ConfigureAwait(false);

                await PerformFlip(solution, configuration).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerUpperClockwise, solution, configuration).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerDownAntiClockwise, solution, configuration).ConfigureAwait(false);

            }
        }
        private async Task MoveRowToCorrectFace(FaceType faceToMoveToo, List<IRotation> solution, CubeConfiguration<FaceColour> configuration, bool upper)
        {
            // If we get an upper or down face it would imply that we do not have completed rows meaning we have probably skipped steps (some tests do this)
            if (faceToMoveToo == FaceType.Upper || faceToMoveToo == FaceType.Down)
            {
                return;
            }

            IRotation rotation = null;
            var face = upper ? FaceType.Upper : FaceType.Down;
            RotationDirection direction;

            var position = FaceRules.RelativePositionBetweenFaces(FaceType.Front, faceToMoveToo);
            switch (position)
            {
                case RelativePosition.Left:
                    direction = upper ? RotationDirection.Clockwise : RotationDirection.AntiClockwise;
                    rotation = Rotations.ByFace(face, direction, configuration.MinInnerLayerIndex());
                    break;

                case RelativePosition.Right:
                    direction = !upper ? RotationDirection.Clockwise : RotationDirection.AntiClockwise;
                    rotation = Rotations.ByFace(face, direction, configuration.MinInnerLayerIndex());
                    break;

                case RelativePosition.Opposite:
                    rotation = Rotations.ByFaceTwice(face, configuration.MinInnerLayerIndex());
                    break;
            }

            if (rotation != null)
            {
                await CommonActions.ApplyAndAddRotation(rotation, solution, configuration).ConfigureAwait(false);

            }
        }
        private static async Task CheckFrontRightEdge(CubeConfiguration<FaceColour> configuration, List<IRotation> solution)
        {
            var frontFaceColour = configuration.Faces[FaceType.Front].LeftCentre();
            var leftFaceColour = configuration.Faces[FaceType.Left].RightCentre();

            var rightEdgeOnFace = configuration.Faces[FaceType.Front].GetEdge(Edge.Right);
            var frontFaceTop = rightEdgeOnFace[configuration.MinInnerLayerIndex()];
            var frontFaceBottom = rightEdgeOnFace[configuration.MaxInnerLayerIndex()];

            var leftEdgeOnRightFace = configuration.Faces[FaceType.Right].GetEdge(Edge.Left);
            var rightFaceTop = leftEdgeOnRightFace[configuration.MinInnerLayerIndex()];
            var rightFaceBottom = leftEdgeOnRightFace[configuration.MaxInnerLayerIndex()];

            if (frontFaceTop == frontFaceColour && rightFaceTop == leftFaceColour)
            {
                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerDownClockwise, solution, configuration).ConfigureAwait(false);

                await PerformFlip(solution, configuration).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerDownAntiClockwise, solution, configuration).ConfigureAwait(false);

            }

            if (frontFaceBottom == frontFaceColour && rightFaceBottom == leftFaceColour)
            {
                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerUpperAntiClockwise, solution, configuration).ConfigureAwait(false);

                await PerformFlip(solution, configuration).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerUpperClockwise, solution, configuration).ConfigureAwait(false);

            }

            if (frontFaceTop == leftFaceColour && rightFaceTop == frontFaceColour)
            {
                await PerformFlip(solution, configuration).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerUpperAntiClockwise, solution, configuration).ConfigureAwait(false);

                await PerformFlip(solution, configuration).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerUpperClockwise, solution, configuration).ConfigureAwait(false);

            }

            if (frontFaceBottom == leftFaceColour && rightFaceBottom == frontFaceColour)
            {
                await PerformFlip(solution, configuration).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerUpperAntiClockwise, solution, configuration).ConfigureAwait(false);

                await PerformFlip(solution, configuration).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerUpperClockwise, solution, configuration).ConfigureAwait(false);

            }
        }
        private static async Task CheckBackLeftEdge(CubeConfiguration<FaceColour> configuration, List<IRotation> solution)
        {
            var frontFaceColour = configuration.Faces[FaceType.Front].LeftCentre();
            var leftFaceColour = configuration.Faces[FaceType.Left].RightCentre();

            var rightEdgeOnFace = configuration.Faces[FaceType.Back].GetEdge(Edge.Right);
            var backFaceTop = rightEdgeOnFace[configuration.MinInnerLayerIndex()];
            var backFaceBottom = rightEdgeOnFace[configuration.MaxInnerLayerIndex()];

            var leftEdgeOnLeftFace = configuration.Faces[FaceType.Left].GetEdge(Edge.Left);
            var leftFaceTop = leftEdgeOnLeftFace[configuration.MinInnerLayerIndex()];
            var leftFaceBottom = leftEdgeOnLeftFace[configuration.MaxInnerLayerIndex()];

            if ((backFaceTop == frontFaceColour && leftFaceTop == leftFaceColour) ||
                (backFaceBottom == frontFaceColour && leftFaceBottom == leftFaceColour) ||
                (backFaceTop == leftFaceColour && leftFaceTop == frontFaceColour) ||
                (backFaceBottom == leftFaceColour && leftFaceBottom == frontFaceColour))
            {
                await CommonActions.ApplyAndAddRotation(Rotations.Back2, solution, configuration).ConfigureAwait(false);

                await CommonActions.ApplyAndAddRotation(Rotations.Right2, solution, configuration).ConfigureAwait(false);

                await CheckFrontRightEdge(configuration, solution).ConfigureAwait(false);

            }
        }
        private static TredgeMatch MatchColoursOnDownFaceEdge(CubeConfiguration<FaceColour> configuration, FaceColour frontColour, FaceColour leftColour, FaceType face)
        {
            var bottomEdge = configuration.Faces[face].GetEdge(Edge.Bottom);
            var frontBottomLeft = bottomEdge[configuration.MinInnerLayerIndex()];
            var frontBottomRight = bottomEdge[configuration.MaxInnerLayerIndex()];

            Edge edge;
            switch (face)
            {
                case FaceType.Front:
                    edge = Edge.Top; break;
                case FaceType.Back:
                    edge = Edge.Bottom; break;
                case FaceType.Left:
                    edge = Edge.Left; break;
                case FaceType.Right:
                    edge = Edge.Right; break;
                default:
                    throw new InvalidOperationException("Cannot get connecting edge as down layer does not connect to " + face);
            }

            var downLayerEdge = configuration.Faces[FaceType.Down].GetEdge(edge);
            if (face == FaceType.Back || face == FaceType.Left)
            {
                downLayerEdge = downLayerEdge.Reverse().ToArray();
            }
            var downTopLeft = downLayerEdge[configuration.MinInnerLayerIndex()];
            var downTopRight = downLayerEdge[configuration.MaxInnerLayerIndex()];

            if (frontBottomLeft == frontColour && downTopLeft == leftColour)
            {
                return TredgeMatch.FrontLeftMatchesCenter;
            }
            if (frontBottomRight == frontColour && downTopRight == leftColour)
            {
                return TredgeMatch.FrontRightMatchesCenter;
            }

            if (frontBottomLeft == leftColour && downTopLeft == frontColour)
            {
                return TredgeMatch.DownLeftMatchesCenter;
            }
            if (frontBottomRight == leftColour && downTopRight == frontColour)
            {
                return TredgeMatch.DownRightMatchesCenter;
            }

            return TredgeMatch.None;
        }
        private static async Task CheckUpperBottomLeft(CubeConfiguration<FaceColour> configuration, List<IRotation> solution, FaceColour frontFaceColour)
        {
            var bottomLeft = configuration.Faces[FaceType.Upper].GetEdge(configuration.MinInnerLayerIndex(), Edge.Bottom)[configuration.MinInnerLayerIndex()];
            if (bottomLeft == frontFaceColour)
            {
                for (int i = 0; i <= 3; i++)
                {
                    if (configuration.Faces[FaceType.Front].GetEdge(configuration.MinInnerLayerIndex(), Edge.Top)[configuration.MinInnerLayerIndex()] != frontFaceColour)
                    {
                        await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerLeftAntiClockwise, solution, configuration).ConfigureAwait(false);

                        await CommonActions.ApplyAndAddRotation(Rotations.UpperAntiClockwise, solution, configuration).ConfigureAwait(false);

                        await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerLeftClockwise, solution, configuration).ConfigureAwait(false);

                        await CommonActions.ApplyAndAddRotation(Rotations.UpperAntiClockwise, solution, configuration).ConfigureAwait(false);

                        await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerLeftAntiClockwise, solution, configuration).ConfigureAwait(false);

                        await CommonActions.ApplyAndAddRotation(Rotations.Upper2, solution, configuration).ConfigureAwait(false);

                        await CommonActions.ApplyAndAddRotation(Rotations.SecondLayerLeftClockwise, solution, configuration).ConfigureAwait(false);

                        break;
                    }

                    await CommonActions.ApplyAndAddRotation(Rotations.FrontClockwise, solution, configuration).ConfigureAwait(false);

                }
            }
        }