function SurveyQuestion(certainSum, uncertainSum, uncertaintyLevel) {
  this.time = new Date();
  this.certainSum = certainSum;
  this.uncertainSum = uncertainSum;
  this.uncertaintyLevel = uncertaintyLevel;
}

SurveyQuestion.prototype.getSumText = function(sum) {
  var val = sum.toString();
  var result = "";
  
  var len = val.length;
  while (len > 3) {
    result = "," + val.substr(len-3,3) + result;
    len -= 3;
  }
  return val.substr(0,len) + result;
}

SurveyQuestion.prototype.getActionVerbText = function(sum) {
  if (sum >= 0) {
    return "לקבל";
  } else {
    return "לשלם";
  }
}

SurveyQuestion.prototype.getCertainOptionText = function() {
  var sum = this.certainSum;
  var verb = this.getActionVerbText(sum);
  return verb + " " + this.getSumText(Math.abs(sum)) + " שקל בוודאות";
}

SurveyQuestion.prototype.getUncertainOptionText = function() {
  var sum = this.uncertainSum;
  var verb = this.getActionVerbText(sum);
  var pPercent = (this.uncertaintyLevel * 100) + "%";
  var qPercent = ((1-this.uncertaintyLevel) * 100) + "%";
  
  return "בסיכוי " + qPercent + " לא " + verb + " דבר, " +
    "<br />ובסיכוי " + pPercent + " " + verb + " " + this.getSumText(Math.abs(sum)) + " שקל";
}

////////////////////////////////////////////////////////////////////////////////

function SurveyAnswer(question, isCertainChosen) {
  this.time = new Date();
  this.question = question;
  this.isCertainChosen = isCertainChosen;
}

////////////////////////////////////////////////////////////////////////////////

function TerrorManagementQuestion(questionId, storyText) {
  this.time = new Date();
  this.questionId = questionId;
  this.storyText = storyText;
}

////////////////////////////////////////////////////////////////////////////////

function TerrorManagementAnswer(question, severity, punishment) {
  this.questionTime = question.time;
  this.answerTime = new Date();
  this.questionId = question.questionId;
  this.severity = severity;
  this.punishment = punishment;
}

////////////////////////////////////////////////////////////////////////////////

function Survey() {
  this.group = Math.round(Math.random());
  this.testOrder = Math.round(Math.random());

  this.uncertaintyLevel = 0.5;
  this.sumRelativeResolution = 0.01;
  
  this.prospectFuncPoints = new Array();
  if (Math.round(Math.random()) == 0) { // choose small-amount
    this.prospectFuncPoints.push(1000);
    this.prospectFuncPoints.push(-1000);
  } else {
    this.prospectFuncPoints.push(2000);
    this.prospectFuncPoints.push(-2000);
  }
  if (Math.round(Math.random()) == 0) { // choose big-amount
    this.prospectFuncPoints.push(10000);
    this.prospectFuncPoints.push(-10000);
  } else {
    this.prospectFuncPoints.push(20000);
    this.prospectFuncPoints.push(-20000);
  }
  
  this.pastAnswers = new Object();
  
  for (var i in this.prospectFuncPoints) {
    // we force object members to start with a character, so that main object
    // can be serialized to valid xml
    this.pastAnswers["a" + this.prospectFuncPoints[i]] = new Array();
  }
  this.lastAnswerPoint = 0;
  
  this.terrorManagementAnswers = new Array();
}

Survey.prototype.serialize = function() {
  var serializer = new SoapSerializer();
  return serializer.object2SoapXml("survey", this);
}

Survey.prototype.groupEvent = function() {
  if (this.group == 0) {
    return "מותך";
  } else {
    return "ביקור עתידי אצל רופא שיניים";
  }
}

Survey.prototype.numTerrorManagementQuestionsLeft = function() {
  return (Survey.terrorManagementQuestions.length - this.terrorManagementAnswers.length);
}

Survey.prototype.nextTerrorManagementQuestion = function() {
  if (this.numTerrorManagementQuestionsLeft() == 0) {
    return null;
  }

  var numPossibleQuestions = Survey.terrorManagementQuestions.length;
  
  while (true) {
    // get random question id
    var questionId = Math.floor(Math.random() * numPossibleQuestions);
    if (questionId == numPossibleQuestions) {
      questionId -= 1;
    }
    
    // make sure it was not answered yet
    if (dojo.every(this.terrorManagementAnswers, function(item) { return item.questionId != questionId; })) {
      return new TerrorManagementQuestion(questionId, Survey.terrorManagementQuestions[questionId]);    
    }
  }
}

Survey.prototype.answerTerrorManagementQuestion = function(answer) {
  this.terrorManagementAnswers.push(answer);
}

Survey.prototype.numQuestionsLeft = function() {
  var questionsLeft = 0;
  
  for (var i in this.prospectFuncPoints) {
    var prospectFuncPoint = this.prospectFuncPoints[i];
    var bounds = this.getSpecificBounds(prospectFuncPoint);
    var diff = bounds.ub - bounds.lb;
    var resolution = Math.round(
      Math.abs(prospectFuncPoint * this.sumRelativeResolution));
    
    questionsLeft += Math.floor(Math.log(diff / resolution) / Math.LN2);
  }
  return questionsLeft;
}

Survey.prototype.nextQuestion = function() {
  // question for last answered prospect function point
  var questionForLastAnswerPoint = null;
  if (this.lastAnswerPoint != 0) {
    questionForLastAnswerPoint = this.nextSpecificQuestion(this.lastAnswerPoint);
  }

  // questions for other prospect function points
  var questions = new Array();
  for (var i in this.prospectFuncPoints) {
    var prospectFuncPoint = this.prospectFuncPoints[i];
    if (prospectFuncPoint != this.lastAnswerPoint) {
      var question = this.nextSpecificQuestion(prospectFuncPoint);
      if (question != null) {
        questions.push(question);
      }
    }
  }
 
  // must return two consecutive questions for same point
  var numPossibleQuestions = questions.length;
  if (numPossibleQuestions == 0) {
    return questionForLastAnswerPoint;
  }
  
  // return a random question for one of the other points
  var questionId = Math.floor(Math.random()*numPossibleQuestions);
  if (questionId == numPossibleQuestions) {
    questionId -= 1;
  }
  return questions[questionId];
}

Survey.prototype.userFriendlySum = function(sum, resolution) {
  var absSum = Math.abs(sum);
  var sign = ((absSum == sum) ? (1) : (-1));  
  var multiplier = Math.round(absSum / resolution);
  return sign * multiplier * resolution;
}

Survey.prototype.getValues = function() {
  var values = new Array();
  for (var i in this.prospectFuncPoints) {
    var prospectFuncPoint = this.prospectFuncPoints[i];
    var bounds = this.getSpecificBounds(prospectFuncPoint);
    var value = (bounds.ub + bounds.lb) / 2;
    values.push( {prospectFuncPoint:prospectFuncPoint, value:value} );
  }
  return values;
}

Survey.prototype.getSpecificBounds = function(prospectFuncPoint) {
  var resolution = Math.round(
    Math.abs(prospectFuncPoint * this.sumRelativeResolution));
  var pastAnswers = this.pastAnswers["a" + prospectFuncPoint];
  
  var ub = (prospectFuncPoint >= 0) ? (prospectFuncPoint) : (0);
  var lb = (prospectFuncPoint >= 0) ? (0) : (prospectFuncPoint);
  for (var i in pastAnswers) {
    if (pastAnswers[i].isCertainChosen) {
      ub = Math.min(ub, pastAnswers[i].question.certainSum);
    } else {
      lb = Math.max(lb, pastAnswers[i].question.certainSum);
    }
  }
  
  return { ub:ub, lb:lb };
}

Survey.prototype.nextSpecificQuestion = function(prospectFuncPoint) {
  var resolution = Math.round(
    Math.abs(prospectFuncPoint * this.sumRelativeResolution));
  var pastAnswers = this.pastAnswers["a" + prospectFuncPoint];
  
  // first question?
  var numPastAnswers = pastAnswers.length;
  if (numPastAnswers == 0) {
    return new SurveyQuestion(
      this.userFriendlySum(prospectFuncPoint*this.uncertaintyLevel, resolution),
      prospectFuncPoint,
      this.uncertaintyLevel);
  } else if (numPastAnswers >= 1) {
    var bounds = this.getSpecificBounds(prospectFuncPoint);
    
    if (bounds.ub - bounds.lb <= resolution) {
      return null;
    } else {    
      return new SurveyQuestion(
        this.userFriendlySum((bounds.lb + bounds.ub)/2, resolution),
        prospectFuncPoint,
        this.uncertaintyLevel);
    }
  }
}

Survey.prototype.answerQuestion = function(question, isCertainChosen) {
  var answer = new SurveyAnswer(question, isCertainChosen);
  var prospectFuncPoint = question.uncertainSum;
  this.pastAnswers["a" + prospectFuncPoint].push(answer);
  this.lastAnswerPoint = prospectFuncPoint;
}

Survey.terrorManagementQuestions = [
  'פורץ מתוסכל השחית יצירת חייו של הפסל הדגול שבוע לפני השלמתה וחשיפתה לציבור. הפורץ שהתאכזב מהשלל המועט כפת את הפסל ולנגד עיניו היכה בפטיש כבד בפסל החימר עד שניתצו לרסיסים. הפסל ההמום: "תשע עשרה שנות עבודה – המיטב שבכשרוני הפך לגל אבנים".',
  
  '"אבי הוא הנפגע העיקרי" אומר בעליו של מפעל המתכת הוותיק שהתמוטט בעקבות מעשה תרמית של חשב המפעל בשיתוף חברה מתחרה. "אני התגברתי, יש לי תוכניות חדשות ומרתקות לעתיד, אך את ליבו השבור של אבי נראה כי לא ניתן לתקן: הוא הקים את המפעל, ניהלו במשך שנים והעבירו לידיי בציפיה להתרחבותו ושגשוגו. מאז הסגירה חשך עליו עולמו, כאילו עם אובדן המפעל אבד רצונו לחיות".',
  
  '"הרכב פגע בי אך בני הוא הקורבן" אומר המורה שנפגע לעיני בנו, עת צעיר חצה שכונת מגורים במהירות של 130 קמ"ש. "חצי שנה אחרי התאונה, אני הבראתי לחלוטין והוא עדיין נחרד לשמע כל מכונית עוברת, אין הוא מסוגל לנסוע ברכב – הולך וחוזר ברגל מבית ספרו המרוחק 3 ק"מ מביתנו, משתדל להתרחק מכל כביש. הילד שהיה שמח ומשוחרר הפך חרד ונרדף".',
  
  'זיהוי מוטעה של נגיפי איידס בגופו של צעיר גרם לבידודו החברתי. "חברתי, חבריי הקרובים – כולם נבהלו והתרחקו. אפילו חוג ריקודי העם שהיווה מוקד חיי החברתיים דחה אותי מתוכו. הפכתי מסוכן לחברה. גם עכשיו, משנודעה הטעות, עדיין חוששים, לא רוצים להסתכן ואני לא מאשים אף אחד חוץ ממנו – אותו רופא שהיה עסוק מכדי להעיף מבט נוסף בתוצאות הבדיקה".',
  
  'אצבעותיו של הפסנתרן המוכשר בגדו בו. הקלילות האופיינית, ההקשה הייחודית בקלידים, הוירטואוזיות המוכרת לא נראו, לא נשמעו... קונצרט ראשון של העילוי הצעיר לאחר החלמתו מהתאונה בה נפגע בידיו, הבהיר באופן חד-משמעי: הצעירה שלא עצרה באדום – פגעה קלות בגוף, אך פגעה אנושות בקריירה של הפסנתרן.',
  
  'הרופא בלבל בין גליונות המחלה של חולות בעלות שם משפחה זהה, וקטע רגלה של החולה הלא-נכונה. "הורדמתי לניתוח פשוט בברכי והתעוררתי ללא רגל, לא ייתכן שרגלי איננה..." סיפרה האישה, מביטה כלא מאמינה על קטע המיטה הריק במקום בו היתה אמורה לנוח רגלה השמאלית.',
  
  'התאונה הרסה את חיי החברה של הנער. הנהג שבהיותו שיכור סטה למדרכה ופגע בנער, נתבע על הנזק הנפשי שנגרם. סיפר הנער: "שנה שכבתי בבית החולים ובבית. בהדרגה הגוף החלים, אך אני נשכחתי על-ידי חבריי שהמשיכו בשגרת חייהם. אין לי אפילו דרך להחזיר את המצב לקדמותו – כולם עברו לחטיבה בבי"ס אחר ואני נשארתי כיתה וצריך להתחיל מחדש – לא שייך לחבורה הישנה ולא לחדשה – פשוט לא שייך".',
  
  '"פצעי האם יגלידו, אך פצעי נפשה של ביתה יוותרו לעד", דברי הפסיכולוג המטפל בילדה, נאמרו עם הוודע דבר לכידתו של נהג הסוברו שפגע באם וברח. הילדה בת החמש שהתייתמה מאביה בהיותה בת שנה, הופרדה מאימה בשל אישפוזה של האם, למשך יותר משנה.',
  
  '"יהירותו של הרופא הבכיר הביאה אותו להחלטה על-סמך מידע חלקי בלבד לכרות רחמה של האישה הצעירה שלא לצורך". כך קבע השופט בתום משפטו של הרופא. בצאתה מאולם בית המשפט אמרה הצעירה: "לעולם לא אהיה עוד אדם שלם, יש לי שלושה ילדים. לא התכוונתי ללדת עוד, אך למרות זאת חלק חשוב ביותר של זהותי אבד לנצח, יש באמת משהו שיכול לפצות על זה?"',
  
  'תרופה פגומה גרמה לקטיעת רגלו של נער. שיקולים כלכליים הביאו את מפעל התרופות להפיץ סדרת תרופות פגומה במקום להשמידה. סיפר רופאו של הנער חולה הסכרת שרגלו נקטעה עקב כך: "חשבנו על הכל, רק לא על זה, במשך שבועיים הדרדר מצבו... מי היה מעלה בדעתו שהתרופה המרכזית שמיוצרת במפעל כה ותיק היא הגורם. זה קשה שחייבים לקטוע רגל לחולה מבוגר והוא הרי כל כך צעיר..."'
];