public Models.Scoring GetReport(Assessment assessment)
        {
            var report = new Models.Scoring(assessment);
            report.ResponseId = assessment.ResponseId;
            report.ScoreOverall = GetOverallScore(assessment);

            // rounded score (level)
            int roundScore =  GetRoundScore(report.ScoreOverall);

            report.RoundScoreOverall = roundScore;
            report.LevelName = GetLevelName(roundScore);
            report.AltLevelName = GetAltLevelName(roundScore);

            // peer score of the top 10% of peers
            report.PeerScoreOverall_Top10Pct = Helper.DecimalValue(ResourceRepo.GetDefaultValue(_peerScore_Overall + _top10pct));
            report.PeerScoreIndustry_Top10Pct = GetPeerScore(_peerScore_Industry + _top10pct, report.Industry);
            report.PeerScoreOrgSize_Top10Pct = GetPeerScore(_peerScore_OrgSize + _top10pct, report.OrgSize);
            report.PeerScoreRegion_Top10Pct = GetPeerScore(_peerScore_Region + _top10pct, report.Location);

            // percentage of peers at this level
            report.PeerPctOverall = Helper.IntValue(ResourceRepo.GetDefaultValue(_peerPct_Overall + "_Level" + roundScore));

            // difference in level between respondent and top 10% in given domain
            report.LevelDiffIndustry = GetLevelDiff(roundScore, report.PeerScoreIndustry_Top10Pct);
            report.LevelDiffOrgSize = GetLevelDiff(roundScore, report.PeerScoreOrgSize_Top10Pct);
            report.LevelDiffRegion = GetLevelDiff(roundScore, report.PeerScoreRegion_Top10Pct);

            report.Q2 = GetAnswers(assessment, 2);
            report.Q3 = GetAnswers(assessment, 3);
            report.Q5 = GetAnswers(assessment, 5);
            report.Q6 = GetAnswers(assessment, 6);
            report.Q8 = GetAnswers(assessment, 8);
            report.Q9 = GetAnswers(assessment, 9);
            report.Q10 = GetAnswers(assessment, 10);

            return report;
        }
        // 1. read from the UI by answer Name or question Name
        // 2. match question definitiions stored in cashe by question No and answer No
        // 3. save in the db by question Id, question item Id and response Id
        private Assessment ProcessFormData(FormCollection form)
        {
            var model = new Assessment();

            // page number
            string name = "CurrentPage";
            string value = form[name];
            int pageNo = Helper.IntValue(value);
            model.CurrentPageNo = pageNo;

            // questions
            var questions = QuestionCache.GetQuestions(pageNo);

            // answers
            foreach(var question in questions)
            {
                var aq = new Models.Assessment.Question()
                {
                    Name = question.Name,
                    OrderNo = question.OrderNo,
                    AssessmentQuestionId = question.AssessmentQuestionId,
                    Type = question.Type,
                    PageNo = question.PageNo,
                };

                model.Questions.Add(aq);
                bool selected = false;

                foreach(var item in question.Answers)
                {
                    switch (item.Type)
                    {
                        case AnswerType.Integer:
                        case AnswerType.Text:
                            name = item.Name;       // item.Name
                            value = form[name];
                            selected = !string.IsNullOrEmpty(value);
                            break;
                        case AnswerType.DropDown:
                        case AnswerType.Radio:
                            name = question.Name;   // question.Name
                            value = form[name];
                            long choiceId = 0;
                            if (long.TryParse(value, out choiceId))
                            {
                                selected = choiceId == item.AnswerChoiceId;
                                value = null;
                            }
                            break;
                        case AnswerType.Checkbox:
                            name = item.Name;       // item.Name
                            value = form[name];

                            // false,false or true,false
                            var val = value.Split(',');
                            bool val0 = false;
                            bool val1 = false;

                            if (val.Length > 0)
                            {
                                bool.TryParse(val[0], out val0);
                                if (val.Length > 1) bool.TryParse(val[1], out val1);
                            }

                            selected = val0 || val1;
                            value = selected.ToString();
                            break;
                    }

                    // single choice questions: consider selected item
                    // multiple choice questions: consider all items
                    if (selected || item.Type == AnswerType.Checkbox)
                    {
                        var aac = new Models.Assessment.Answer()
                        {
                            Value = value,
                            Type = item.Type,
                            QuestionItemId = item.QuestionItemId,
                            AnswerChoiceId = item.AnswerChoiceId,
                            Name = item.Name,
                            Score = item.Score,
                            Selected = selected
                        };

                        aq.Answers.Add(aac);
                    }
                }
            }

            return model;
        }
        public ViewResult Questionnaire(string page)
        {
            var model = new Assessment();
            long responseId = 0;
            int pageNo = 1;
            if (!string.IsNullOrEmpty(page) && !int.TryParse(page, out pageNo)) pageNo = 1;

            if (Session["ResponseId"] != null) long.TryParse(Session["ResponseId"].ToString(), out responseId);
            string responseKey = Session["ResponseKey"] != null ? Session["ResponseKey"].ToString() : null;

            if (Session["TargetPage"] == null) Session["TargetPage"] = "1";

            if (responseId == 0)
            {
                if (pageNo > 1) return null;    // should not be possible

                // record new response
                responseId = _db.CreateResponse(responseKey);
                Session["ResponseId"] = responseId;
                model = QuestionCache.GetAssessment(pageNo);
                Logger.Log(LogLevel.Info, "Assessment requested: ResponseId = " + responseId);
            }
            else
            {
                // retrieve previously stored answers
                var answeredQuestions = _db.GetAnswers(QuestionCache.AssessmentName, _culture, responseId).Questions;
                var questions = QuestionCache.GetAssessment(pageNo).Questions;

                // complete questions list with choices not found in the saved answers
                foreach(var question in questions)
                {
                    var answeredQuestion = answeredQuestions.FirstOrDefault(x => x.Name == question.Name);
                    if (answeredQuestion != null) model.Questions.Add(answeredQuestion);
                    else model.Questions.Add(question);
                }

                model.CurrentPageNo = pageNo;
            }

            return View(model);
        }
        public ViewResult OrgProfile()
        {
            var model = new Assessment();
            long responseId = 0;
            int pageNo = 2;

            if (Session["ResponseId"] != null) long.TryParse(Session["ResponseId"].ToString(), out responseId);
            string responseKey = Session["ResponseKey"] != null ? Session["ResponseKey"].ToString() : null;

            if (responseId == 0)
            {
                return null;
            }
            else
            {
                // retrieve previously stored answers
                var answeredQuestions = _db.GetAnswers(QuestionCache.AssessmentName, _culture, responseId).Questions;
                var questions = QuestionCache.GetAssessment(pageNo).Questions;

                // complete questions list with choices not found in the saved answers
                foreach (var question in questions)
                {
                    var answeredQuestion = answeredQuestions.FirstOrDefault(x => x.Name == question.Name);
                    if (answeredQuestion != null) model.Questions.Add(answeredQuestion);
                    else model.Questions.Add(question);
                }

                model.CurrentPageNo = pageNo;
            }

            return View(model);
        }
        private QuestionCache(Assessment assessment)
        {
            _assessmentName = assessment.Name;
            _assessment = assessment;

            _questions = new Dictionary<int, Question>();
            _answers = new Dictionary<int, Answer>();

            foreach (var question in assessment.Questions)
            {
                if (!_questions.ContainsKey(question.OrderNo)) _questions.Add(question.OrderNo, question);

                foreach (var answer in question.Answers)
                {
                    int key = question.OrderNo * 1000 + answer.OrderNo;
                    if (!_answers.ContainsKey(key)) _answers.Add(key, answer);
                }
            }
        }
        public static Assessment GetAssessment(int pageNo)
        {
            var assessment = new Assessment()
            {
                Name = _assessmentName,
                CurrentPageNo = pageNo,
                Questions = GetQuestions(pageNo)
            };

            return assessment;
        }
        private bool[] GetAnswers(Assessment assessment, int orderNo)
        {
            bool[] answers = null;
            var q = assessment.Questions.FirstOrDefault(x => x.OrderNo == orderNo);

            // complete items list
            if (q == null) q = QuestionCache.GetQuestion(orderNo);
            else
            {
                var qiList = QuestionCache.GetQuestion(orderNo).Answers;
                foreach (var item in qiList)
                {
                    if (!q.Answers.Any(x => x.AnswerChoiceId == item.AnswerChoiceId)) { q.Answers.Add(item); }
                }
            }

            int count = 0;
            var list = q.Answers.Where(x => !x.AlternativeAnswer);
            if (list != null) count = list.Count();
            if (count > 0)
            {
                int i = 0;
                answers = new bool[count];
                foreach (var item in list.OrderBy(x => x.OrderNo)) answers[i++] = item.Selected;
            }

            return answers;
        }
        private decimal GetOverallScore(Assessment assessment)
        {
            decimal score = 0;
            decimal scaled_score = 0;

            foreach (var question in assessment.Questions)
            {
                foreach (var answer in question.Answers)
                {
                    if (answer.Selected && answer.Score.HasValue)
                    {
                        score += (decimal)answer.Score;
                    }
                }
            }

            // scale score (from 0 to 100) to stages (from 1 to 4)
            scaled_score = score / 25;

            return scaled_score;
        }
        private decimal? GetDomainScore(int qNo, Assessment assessment)
        {
            decimal? score = null;

            var question = assessment.Questions.FirstOrDefault(x => x.OrderNo == qNo);
            if (question != null)
            {
                var answer = question.Answers.FirstOrDefault();
                if (answer != null) score = answer.Score;
            }

            return score;
        }