public AutoMapper_CrossObjectsProfile()
        {
            #region TestsDB_TestDTO -> AttemptsDB_AttemptDTO
            CreateMap <TestsDB_ChoiceOption, AttemptsDB_ChoiceOption>();
            CreateMap <TestsDB_PositionalOption[], AttemptsDB_PositionalOption[]>()
            .ConstructUsing(x => {
                var attemptOptions = new AttemptsDB_PositionalOption[x.Length];
                for (var i = 0; i < x.Length; i++)
                {
                    attemptOptions[i] = new AttemptsDB_PositionalOption {
                        CorrectIndex = i,
                        Text         = x[i].Text,
                        ImageUrl     = x[i].ImageUrl
                    }
                }
                ;

                return(attemptOptions);
            });

            CreateMap <TestsDB_QuestionDTO, AttemptsDB_QuestionDTO>()
            .ForMember(x => x.Options,
                       x => x.ResolveUsing(m => {
                switch (m.Type)
                {
                case QuestionTypes.SingleChoice:
                case QuestionTypes.MultipleChoice:
                    var choiceOptionsData = BsonSerializer.Deserialize <TestsDB_ChoiceOptionsContainer>(m.Options, null);

                    return(new AttemptsDB_ChoiceOptionsContainer {
                        Options = Mapper.Map <AttemptsDB_ChoiceOption[]>(m.Shuffle.Value ?
                                                                         AttemptConstructor.ShuffleArray(choiceOptionsData.Options) :
                                                                         choiceOptionsData.Options)
                    }.ToBsonDocument());

                case QuestionTypes.Matching:
                    var matchingOptionsData = BsonSerializer.Deserialize <TestsDB_MatchingOptionsContainer>(m.Options, null);

                    return(new AttemptsDB_MatchingOptionsContainer {
                        LeftSequence = AttemptConstructor.ShuffleArray(
                            Mapper.Map <AttemptsDB_PositionalOption[]>(matchingOptionsData.LeftSequence)),
                        RightSequence = AttemptConstructor.ShuffleArray(
                            Mapper.Map <AttemptsDB_PositionalOption[]>(matchingOptionsData.RightSequence))
                    }.ToBsonDocument());

                case QuestionTypes.Sequence:
                    return(new AttemptsDB_SequenceOptionsContainer {
                        Sequence = AttemptConstructor.ShuffleArray(
                            Mapper.Map <AttemptsDB_PositionalOption[]>(
                                BsonSerializer.Deserialize <TestsDB_SequenceOptionsContainer>(m.Options, null)
                                .Sequence))
                    }.ToBsonDocument());

                case QuestionTypes.FillIn:
                    return(new AttemptsDB_FillInOptionsContainer {
                        Options = Mapper.Map <AttemptsDB_FillInOption[]>(
                            BsonSerializer.Deserialize <TestsDB_FillInOptionsContainer>(m.Options, null)
                            .Options)
                    }.ToBsonDocument());

                default:
                    return(new BsonDocument());
                }
            }));
            CreateMap <KeyValuePair <string, TestsDB_QuestionDTO>, KeyValuePair <string, AttemptsDB_QuestionDTO> >()
            .ConstructUsing(x => new KeyValuePair <string, AttemptsDB_QuestionDTO>(
                                x.Key,
                                Mapper.Map <AttemptsDB_QuestionDTO>(x.Value)));

            CreateMap <TestsDB_SectionDTO, AttemptsDB_SectionDTO>()
            .ForMember(x => x.Questions,
                       x => x.MapFrom(m =>
                                      Mapper.Map <Dictionary <string, AttemptsDB_QuestionDTO> >(m.Questions)));
            CreateMap <KeyValuePair <string, TestsDB_SectionDTO>, KeyValuePair <string, AttemptsDB_SectionDTO> >()
            .ConstructUsing(x => new KeyValuePair <string, AttemptsDB_SectionDTO>(
                                x.Key,
                                Mapper.Map <AttemptsDB_SectionDTO>(x.Value)));

            CreateMap <TestsDB_TestDTO, AttemptsDB_AttemptDTO>()
            .ForMember(x => x.Id,
                       x => x.Ignore())
            .ForMember(x => x.TestId,
                       x => x.MapFrom(m =>
                                      m.Id))
            .ForMember(x => x.Sections,
                       x => x.MapFrom(m =>
                                      Mapper.Map <Dictionary <string, AttemptsDB_SectionDTO> >(
                                          AttemptConstructor.SelectSections(m))));
            #endregion
        }
        public static (bool, OperationErrorMessages, object) ApplyAnswer(Dictionary <string, AttemptsDB_SectionDTO> sections, string sectionId, string questionId, MemberAPI_CommonTestAnswerDTO testAnswerDTO)
        {
            if (!sections.ContainsKey(sectionId))
            {
                return(false, OperationErrorMessages.SectionNotFound, null);
            }

            var questions = sections[sectionId].Questions;

            if (!questions.ContainsKey(questionId))
            {
                return(false, OperationErrorMessages.QuestionNotFound, null);
            }

            var question = questions[questionId];

            if (question.Type == QuestionTypes.SingleChoice)
            {
                var selectedOption = testAnswerDTO.SelectedOption;
                if (selectedOption != null)
                {
                    var optionsContainer = BsonSerializer.Deserialize <AttemptsDB_ChoiceOptionsContainer>(question.Options);

                    if (selectedOption >= optionsContainer.Options.Length)
                    {
                        return(false, OperationErrorMessages.SelectedOptionOutOfRange, null);
                    }

                    for (var i = 0; i < optionsContainer.Options.Length; i++)
                    {
                        optionsContainer.Options[i].Chosen = i == selectedOption;
                    }

                    question.Options = optionsContainer.ToBsonDocument();
                }
            }
            else if (question.Type == QuestionTypes.MultipleChoice)
            {
                var selectedOptions = testAnswerDTO.SelectedOptions;
                if (selectedOptions != null)
                {
                    var optionsContainer = BsonSerializer.Deserialize <AttemptsDB_ChoiceOptionsContainer>(question.Options);
                    var optionsCount     = optionsContainer.Options.Length;

                    var uniques = new HashSet <int>();
                    for (var i = 0; i < selectedOptions.Length; i++)
                    {
                        if (selectedOptions[i] >= optionsCount)
                        {
                            return(false, OperationErrorMessages.SelectedOptionOutOfRange, null);
                        }

                        if (!uniques.Add(selectedOptions[i]))
                        {
                            return(false, OperationErrorMessages.NonUniqueOption, new CommonAPI_ErrorDTO {
                                ErrorSubject = selectedOptions[i]
                            });
                        }
                    }

                    for (var i = 0; i < optionsCount; i++)
                    {
                        optionsContainer.Options[i].Chosen = selectedOptions.Contains(i);
                    }

                    question.Options = optionsContainer.ToBsonDocument();
                }
            }
            else if (question.Type == QuestionTypes.Matching)
            {
                var leftSequence  = testAnswerDTO.LeftSequence;
                var rightSequence = testAnswerDTO.RightSequence;
                if (leftSequence != null && rightSequence != null)
                {
                    var optionsContainer = BsonSerializer.Deserialize <AttemptsDB_MatchingOptionsContainer>(question.Options);
                    var optionsCount     = optionsContainer.LeftSequence.Length;

                    if (leftSequence.Length != optionsCount)
                    {
                        return(false, OperationErrorMessages.WrongOptionsCount, new CommonAPI_ErrorDTO {
                            ErrorSubject = "left"
                        });
                    }

                    if (rightSequence.Length != optionsCount)
                    {
                        return(false, OperationErrorMessages.WrongOptionsCount, new CommonAPI_ErrorDTO {
                            ErrorSubject = "right"
                        });
                    }

                    var leftUniques  = new HashSet <int>();
                    var rightUniques = new HashSet <int>();
                    for (var i = 0; i < leftSequence.Length; i++)
                    {
                        if (leftSequence[i] >= optionsCount)
                        {
                            return(false, OperationErrorMessages.SelectedOptionOutOfRange, new CommonAPI_ErrorDTO {
                                ErrorSubject = new object[] { "left", leftSequence[i] }
                            });
                        }
                        if (rightSequence[i] >= optionsCount)
                        {
                            return(false, OperationErrorMessages.SelectedOptionOutOfRange, new CommonAPI_ErrorDTO {
                                ErrorSubject = new object[] { "right", rightSequence[i] }
                            });
                        }

                        if (!leftUniques.Add(leftSequence[i]))
                        {
                            return(false, OperationErrorMessages.NonUniqueOption, new CommonAPI_ErrorDTO {
                                ErrorSubject = new object[] { "left", leftSequence[i] }
                            });
                        }
                        if (!rightUniques.Add(rightSequence[i]))
                        {
                            return(false, OperationErrorMessages.NonUniqueOption, new CommonAPI_ErrorDTO {
                                ErrorSubject = new object[] { "right", rightSequence[i] }
                            });
                        }
                    }

                    var tempLeftSequence  = new AttemptsDB_PositionalOption[optionsCount];
                    var tempRightSequence = new AttemptsDB_PositionalOption[optionsCount];
                    for (var i = 0; i < optionsCount; i++)
                    {
                        tempLeftSequence[i]  = optionsContainer.LeftSequence[leftSequence[i]];
                        tempRightSequence[i] = optionsContainer.RightSequence[rightSequence[i]];
                    }
                    optionsContainer.LeftSequence  = tempLeftSequence;
                    optionsContainer.RightSequence = tempRightSequence;

                    question.Options = optionsContainer.ToBsonDocument();
                }
            }
            else if (question.Type == QuestionTypes.Sequence)
            {
                var sequence = testAnswerDTO.Sequence;
                if (sequence != null)
                {
                    var optionsContainer = BsonSerializer.Deserialize <AttemptsDB_SequenceOptionsContainer>(question.Options);
                    var optionsCount     = optionsContainer.Sequence.Length;

                    if (sequence.Length != optionsCount)
                    {
                        return(false, OperationErrorMessages.WrongOptionsCount, null);
                    }

                    var uniques = new HashSet <int>();
                    for (var i = 0; i < sequence.Length; i++)
                    {
                        if (sequence[i] >= optionsCount)
                        {
                            return(false, OperationErrorMessages.SelectedOptionOutOfRange, new CommonAPI_ErrorDTO {
                                ErrorSubject = sequence[i]
                            });
                        }

                        if (!uniques.Add(sequence[i]))
                        {
                            return(false, OperationErrorMessages.NonUniqueOption, new CommonAPI_ErrorDTO {
                                ErrorSubject = sequence[i]
                            });
                        }
                    }

                    for (var i = 0; i < optionsCount; i++)
                    {
                        optionsContainer.Sequence[i] = optionsContainer.Sequence[sequence[i]];
                    }

                    question.Options = optionsContainer.ToBsonDocument();
                }
            }
            else if (question.Type == QuestionTypes.FillIn)
            {
                var fills = testAnswerDTO.Fills;
                if (fills != null)
                {
                    var optionsContainer = BsonSerializer.Deserialize <AttemptsDB_FillInOptionsContainer>(question.Options);
                    var options          = optionsContainer.Options;
                    var blanksCount      = optionsContainer.Options
                                           .Where(option =>
                                                  option.IsBlank
                                                  ).ToArray()
                                           .Length;

                    if (fills.Length != blanksCount)
                    {
                        return(false, OperationErrorMessages.WrongOptionsCount, null);
                    }

                    var j = 0;
                    for (var i = 0; i < optionsContainer.Options.Length; i++)
                    {
                        if (optionsContainer.Options[i].IsBlank)
                        {
                            optionsContainer.Options[i].Text = fills[j];
                            j++;
                        }
                    }

                    question.Options = optionsContainer.ToBsonDocument();
                }
            }

            question.Touched    = true;
            question.BlurTime  += testAnswerDTO.BlurTimeAddition.Value;
            question.TotalTime += testAnswerDTO.TotalTimeAddition.Value;

            sections[sectionId].Questions[questionId] = question;

            return(true, OperationErrorMessages.NoError, sections);
        }