001package edu.pdx.cs410J.grader.poa;
002
003import com.google.common.annotations.VisibleForTesting;
004import com.google.common.base.Strings;
005import com.google.common.eventbus.EventBus;
006import com.google.common.eventbus.Subscribe;
007import com.google.inject.Inject;
008import com.google.inject.Singleton;
009import edu.pdx.cs410J.grader.gradebook.Assignment;
010import edu.pdx.cs410J.grader.gradebook.Grade;
011import edu.pdx.cs410J.grader.gradebook.Student;
012
013import java.util.Optional;
014
015@Singleton
016public class POAGradePresenter {
017  private final EventBus bus;
018  private final POAGradeView view;
019
020  private POASubmission submission;
021  private Assignment assignment;
022  private Student student;
023  private boolean isLate;
024  private Double score;
025  private CurrentGradeCalculator currentGradeCalculator;
026
027  @Inject
028  public POAGradePresenter(EventBus bus, POAGradeView view) {
029    this.bus = bus;
030    this.view = view;
031
032    this.bus.register(this);
033
034    this.view.addIsLateHandler(POAGradePresenter.this::setIsLate);
035    this.view.addScoreValueHandler(this::setScoreValue);
036    this.view.addRecordGradeHandler(this::publishScoreToMessageBusAndDisplayNextPOA);
037  }
038
039  private void publishScoreToMessageBusAndDisplayNextPOA() {
040    if (this.score == null) {
041      throw new IllegalStateException("No score specified");
042    }
043
044    RecordGradeEvent recordGrade = new RecordGradeEvent(this.score, this.student, this.assignment, this.isLate);
045    this.bus.post(recordGrade);
046    this.view.setScoreHasBeenRecorded(true);
047
048    SelectNextPOAEvent displayNextPOA = new SelectNextPOAEvent();
049    this.bus.post(displayNextPOA);
050  }
051
052  @Subscribe
053  public void determineIfSubmissionIsLate(POASubmissionSelected event) {
054    this.submission = event.getSubmission();
055
056    clearScore();
057    determineIfPOAIsLate();
058  }
059
060  private void clearScore() {
061    this.score = null;
062    this.view.setErrorInScore(false);
063    this.view.setScore("");
064    this.view.setScoreHasBeenRecorded(false);
065  }
066
067  @Subscribe
068  public void determineIfStudentsPOAIsLate(StudentSelectedEvent event) {
069    this.student = event.getSelectedStudent();
070
071    updateViewBasedOnCurrentState();
072  }
073
074  @Subscribe
075  public void determineIfAssignmentIsLate(AssignmentSelectedEvent event) {
076    this.assignment = event.getAssignment();
077
078    updateViewBasedOnCurrentState();
079  }
080
081  private void updateViewBasedOnCurrentState() {
082    clearScore();
083    determineIfPOAIsLate();
084    setTotalPointsValue();
085    setDefaultValueOfScore();
086  }
087
088  private void setTotalPointsValue() {
089    String totalPoints;
090    if (this.assignment == null) {
091      totalPoints = "??";
092
093    } else {
094      totalPoints = formatTotalPoints(this.assignment.getPoints());
095    }
096    this.view.setTotalPoints(totalPoints);
097  }
098
099  private void setDefaultValueOfScore() {
100    if (this.currentGradeCalculator == null) {
101      this.score = null;
102      this.view.setScore("");
103      this.view.setScoreHasBeenRecorded(false);
104      return;
105    }
106
107    Optional<Double> currentGrade = this.currentGradeCalculator.getCurrentGradeFor(this.assignment, this.student);
108    if (currentGrade.isPresent()) {
109      this.score = currentGrade.get();
110      this.view.setScore(formatTotalPoints(this.score));
111      this.view.setScoreHasBeenRecorded(true);
112
113    } else if (this.assignment == null) {
114      this.score = null;
115      this.view.setScore("");
116      this.view.setScoreHasBeenRecorded(false);
117
118    } else {
119      this.score = this.assignment.getPoints();
120      this.view.setScore(formatTotalPoints(this.score));
121      this.view.setScoreHasBeenRecorded(false);
122    }
123
124    this.view.setErrorInScore(false);
125  }
126
127  @Subscribe
128  public void createCurrentGradeCalculator(GradeBookLoaded event) {
129    this.currentGradeCalculator = new CurrentGradeCalculator();
130  }
131
132  private void determineIfPOAIsLate() {
133    if (this.submission != null && this.assignment != null && this.student != null) {
134      enableView();
135
136      if (this.assignment.isSubmissionLate(this.submission.getSubmitTime())) {
137        setIsLate(true);
138
139      } else {
140        setIsLate(false);
141      }
142
143    } else {
144      disableView();
145    }
146  }
147
148  private void setIsLate(boolean isLate) {
149    this.isLate = isLate;
150    this.view.setIsLate(isLate);
151  }
152
153  private void enableView() {
154    this.view.setIsEnabled(true);
155  }
156
157  private void disableView() {
158    setIsLate(false);
159    this.view.setIsEnabled(false);
160  }
161
162  @VisibleForTesting
163  boolean isLate() {
164    return this.isLate;
165  }
166
167  @VisibleForTesting
168  static String formatTotalPoints(double points) {
169    return String.format("%.2f", points);
170  }
171
172  public void setScoreValue(String scoreValue) {
173    if (Strings.isNullOrEmpty(scoreValue)) {
174      this.score = null;
175      this.view.setErrorInScore(false);
176      return;
177    }
178
179    try {
180      double score = Double.parseDouble(scoreValue);
181
182      validateScoreIsBetweenZeroAndAssignmentsTotalPoints(score);
183
184    } catch (NumberFormatException ex) {
185      this.score = null;
186      this.view.setErrorInScore(true);
187    }
188  }
189
190  private void validateScoreIsBetweenZeroAndAssignmentsTotalPoints(double score) {
191    if (score < 0.0) {
192      this.score = null;
193      this.view.setErrorInScore(true);
194
195    } else if (score > this.assignment.getPoints()) {
196      this.score = null;
197      this.view.setErrorInScore(true);
198
199    } else {
200      this.score = score;
201      this.view.setErrorInScore(false);
202    }
203  }
204
205  public Double getScore() {
206    return score;
207  }
208
209  private class CurrentGradeCalculator {
210
211    public CurrentGradeCalculator() {
212    }
213
214    public Optional<Double> getCurrentGradeFor(Assignment assignment, Student student) {
215      if (student == null || assignment == null) {
216        return Optional.empty();
217      }
218
219      Grade grade = student.getGrade(assignment);
220      if (grade != null) {
221        return Optional.of(grade.getScore());
222      } else {
223        return Optional.empty();
224      }
225    }
226  }
227}