gt: 			sum = sum.add(score);
context: // <editor-fold defaultstate="collapsed" desc="GNU General Public License">
  //
  //   SynchroScore
  //   Copyright (C) 2009 Shayne Hughes
  //
  //   This program is free software: you can redistribute it and/or modify
  //   it under the terms of the GNU General Public License as published by
  //   the Free Software Foundation, either version 3 of the License, or
  //   (at your option) any later version.
  //
  //   This program is distributed in the hope that it will be useful,
  //   but WITHOUT ANY WARRANTY; without even the implied warranty of
  //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  //   GNU General Public License for more details.
  //
  //   You should have received a copy of the GNU General Public License
  //   along with this program.  If not, see <http://www.gnu.org/licenses/>.
  //
  // </editor-fold>
  package org.aquastarz.score.controller;
  
  import java.awt.Cursor;
  import java.io.File;
  import java.io.IOException;
  import java.math.BigDecimal;
  import java.text.DateFormat;
  import java.text.SimpleDateFormat;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.Comparator;
  import java.util.Date;
  import java.util.HashMap;
  import java.util.Iterator;
  import java.util.LinkedList;
  import java.util.List;
  import java.util.Map;
  import java.util.Map.Entry;
  import java.util.TreeMap;
  
  import javax.persistence.EntityManager;
  import javax.persistence.EntityTransaction;
  import javax.persistence.Query;
  import javax.swing.JFileChooser;
  import javax.swing.JOptionPane;
  import javax.swing.filechooser.FileNameExtensionFilter;
  
  import org.aquastarz.score.ScoreApp;
  import org.aquastarz.score.domain.Figure;
  import org.aquastarz.score.domain.FigureScore;
  import org.aquastarz.score.domain.FiguresParticipant;
  import org.aquastarz.score.domain.Level;
  import org.aquastarz.score.domain.Meet;
  import org.aquastarz.score.domain.Routine;
  import org.aquastarz.score.domain.RoutineLevel;
  import org.aquastarz.score.domain.Season;
  import org.aquastarz.score.domain.Swimmer;
  import org.aquastarz.score.domain.Team;
  import org.aquastarz.score.gui.FiguresParticipantsTableModel;
  import org.aquastarz.score.gui.SynchroFrame;
  import org.aquastarz.score.manager.FigureManager;
  import org.aquastarz.score.manager.MeetManager;
  import org.aquastarz.score.manager.RoutineManager;
  import org.aquastarz.score.report.FiguresMeetSheet;
  import org.aquastarz.score.report.Label;
  import org.aquastarz.score.report.StationResult;
  
  /**
   * 
   * @author Shayne Hughes <velocityfactor@gmail.com>
   */
  public class ScoreController {
  
  	private static org.apache.log4j.Logger logger = org.apache.log4j.Logger
  			.getLogger(ScoreController.class.getName());
  	EntityManager entityManager = ScoreApp.getEntityManager();
  	private final SynchroFrame mainFrame;
  
  	public ScoreController(Meet meet) {
  		if (meet == null) {
  			logger.info("ScoreController initialize new Meet.");
  			EntityTransaction transaction = entityManager.getTransaction();
  			transaction.begin();
  			try {
  				meet = new Meet();
  				meet.setName("Mock Meet");
  				DateFormat df = SimpleDateFormat
  						.getDateInstance(SimpleDateFormat.LONG);
  				meet.setMeetDate(df.format(new Date()));
  				meet.setSeason(ScoreApp.getCurrentSeason());
  				meet.setEu1(true);
  				meet.setEu2(false);
  				meet.setEu3(true);
  				meet.setEu4(false);
  				meet.setNov1Figure(FigureManager.findById("101"));
  				meet.setNov2Figure(FigureManager.findById("360"));
  				meet.setNov3Figure(null);
  				meet.setNov4Figure(null);
  				meet.setInt1Figure(FigureManager.findById("308"));
  				meet.setInt2Figure(FigureManager.findById("115"));
  				meet.setInt3Figure(null);
  				meet.setInt4Figure(null);
  
  				// 2015 Schedule
  				if ("June 17, 2015".equals(meet.getMeetDate())
  						|| "June 19, 2015".equals(meet.getMeetDate())
  						|| "July 8, 2015".equals(meet.getMeetDate())) {
  					// Group 1
  					meet.setNov3Figure(FigureManager.findById("301"));
  					meet.setNov4Figure(FigureManager.findById("361"));
  					meet.setInt3Figure(FigureManager.findById("311a"));
  					meet.setInt4Figure(FigureManager.findById("326"));
  				}
  				if ("June 10, 2015".equals(meet.getMeetDate())
  						|| "July 1, 2015".equals(meet.getMeetDate())
  						|| meet.getMeetDate().startsWith("July 17")) {
  					// Group 2
  					meet.setNov3Figure(FigureManager.findById("349"));
  					meet.setNov4Figure(FigureManager.findById("321"));
  					meet.setInt3Figure(FigureManager.findById("140"));
  					meet.setInt4Figure(FigureManager.findById("420"));
  				}
  				if ("June 24, 2015".equals(meet.getMeetDate())
  						|| "June 26, 2015".equals(meet.getMeetDate())) {
  					// Group 3
  					meet.setNov3Figure(FigureManager.findById("315"));
  					meet.setNov4Figure(FigureManager.findById("344"));
  					meet.setInt3Figure(FigureManager.findById("240"));
  					meet.setInt4Figure(FigureManager.findById("346"));
  				}
  
  				transaction.commit();
  			} catch (Exception e) {
  				logger.error("Error creating new meet.", e);
  				transaction.rollback();
  				// TODO feedback
  				System.exit(-1);
  			}
  		}
  		
  		logger.info("ScoreController starting for meetId=" + meet.getMeetId()
  				+ " \"" + meet.getName() + "\"");
  		mainFrame = new SynchroFrame(this, meet);
  		mainFrame.setRoundLabelsCheck(meet.isChamps());
  		mainFrame.setVisible(true);
  	}
  
  	public List<Team> getTeams() {
  		javax.persistence.Query query = entityManager
  				.createQuery("SELECT t FROM Team t order by t.name");
  		return query.getResultList();
  	}
  
  	public List<Figure> getFigures() {
  		javax.persistence.Query query = entityManager
  				.createQuery("SELECT f FROM Figure f order by f.figureId");
  		return query.getResultList();
  	}
  
  	public static List<Level> getLevels() {
  		javax.persistence.Query query = ScoreApp.getEntityManager()
  				.createQuery("SELECT l FROM Level l order by l.levelId");
  		return query.getResultList();
  	}
  
  	public static List<Meet> getMeets(Season season) {
  		javax.persistence.Query query = ScoreApp.getEntityManager()
  				.createNamedQuery("Meet.findAllBySeasonOrderByIdDesc");
  		query.setParameter("season", season);
  		return query.getResultList();
  	}
  
  	public static Meet findMeet(Season season, String name) {
  		javax.persistence.Query query = ScoreApp.getEntityManager()
  				.createNamedQuery("Meet.findBySeasonAndName");
  		query.setParameter("season", season);
  		query.setParameter("name", name);
  		try {
  			return (Meet) query.getSingleResult();
  		} catch (Exception e) {
  			return null;
  		}
  	}
  
  	public static List<Season> getSeasons() {
  		javax.persistence.Query query = ScoreApp.getEntityManager()
  				.createNamedQuery("Season.findAllOrderByName");
  		return query.getResultList();
  	}
  
  	public static Season getSeason(String name) {
  		javax.persistence.Query query = ScoreApp.getEntityManager()
  				.createNamedQuery("Season.findByName");
  		query.setParameter("name", name);
  		try {
  			return (Season) query.getSingleResult();
  		} catch (Exception e) {
  			return null;
  		}
  	}
  
  	public void saveMeet(Meet meet) {
  		EntityTransaction transaction = entityManager.getTransaction();
  		transaction.begin();
  		try {
  			entityManager.persist(meet);
  			transaction.commit();
  		} catch (Exception e) {
  			transaction.rollback();
  			logger.error("Error saving meet.", e);
  		}
  	}
  
  	public static FiguresParticipant findFiguresParticipant(Meet meet,
  			Swimmer swimmer) {
  		Query figureOrderQuery = ScoreApp.getEntityManager().createNamedQuery(
  				"FiguresParticipant.findByMeetAndSwimmer");
  		figureOrderQuery.setParameter("meet", meet);
  		figureOrderQuery.setParameter("swimmer", swimmer);
  		return (FiguresParticipant) figureOrderQuery.getSingleResult();
  	}
  
  	public static List<FiguresParticipant> findAllFiguresParticipantByMeetAndDivision(
  			Meet meet, boolean isNovice) {
  		Query figureOrderQuery = ScoreApp.getEntityManager().createNamedQuery(
  				"FiguresParticipant.findByMeetAndLevelOrderByTotalScore");
  		figureOrderQuery.setParameter("meet", meet);
  		figureOrderQuery.setParameter("level", isNovice ? "N%" : "I%");
  		List<FiguresParticipant> figuresParticipants = figureOrderQuery
  				.getResultList();
  		for (Iterator<FiguresParticipant> it = figuresParticipants.iterator(); it
  				.hasNext();) {
  			FiguresParticipant fp = it.next();
  			if (!figuresParticipantHasAllScores(fp)) {
  				it.remove();
  			}
  		}
  		return figuresParticipants;
  	}
  
  	public FiguresParticipant findFiguresParticipantByFigureOrder(Meet meet,
  			String figureOrder) {
  		Query figureOrderQuery = entityManager
  				.createNamedQuery("FiguresParticipant.findByMeetAndFigureOrder");
  		figureOrderQuery.setParameter("meet", meet);
  		figureOrderQuery.setParameter("figureOrder", figureOrder);
  		try {
  			FiguresParticipant figuresParticipant = (FiguresParticipant) figureOrderQuery
  					.getSingleResult();
  
  			return figuresParticipant;
  		} catch (Exception e) {
  			// not found
  			return null;
  		}
  	}
  
  	/*
  	 * Accepts a new meet with no swimmers or an existing meet with swimmers.
  	 * Creates new FiguresParticipants as needed and gives them an appropriate
  	 * figureOrder if figureOrders have been calculated. Deletes
  	 * FigureParticipants that are no longer needed.
  	 */
  	public void updateFiguresSwimmers(Meet meet, List<Swimmer> swimmers) {
  		List<FiguresParticipant> newList = new ArrayList<FiguresParticipant>();
  
  		EntityTransaction transaction = entityManager.getTransaction();
  		transaction.begin();
  
  		try {
  			HashMap<Swimmer, FiguresParticipant> map = new HashMap<Swimmer, FiguresParticipant>();
  			for (FiguresParticipant fp : meet.getFiguresParticipants()) {
  				map.put(fp.getSwimmer(), fp);
  			}
  
  			for (Swimmer s : swimmers) {
  				FiguresParticipant fp = map.remove(s);
  				if (fp == null) { // Adding a new swimmer
  					fp = new FiguresParticipant(meet, s);
  					if (meet.getFiguresOrderGenerated()) {
  						String maxOrder = "0";
  						Integer newLevelOrder = fp.getSwimmer().getLevel()
  								.getSortOrder();
  						for (FiguresParticipant levelFp : meet
  								.getFiguresParticipants()) {
  							Integer levelOrder = levelFp.getSwimmer()
  									.getLevel().getSortOrder();
  							if (levelFp.getFigureOrder().compareTo(maxOrder) > 0
  									&& newLevelOrder >= levelOrder) {
  								maxOrder = levelFp.getFigureOrder();
  							}
  						}
  						for (FiguresParticipant levelFp : newList) {
  							Integer levelOrder = levelFp.getSwimmer()
  									.getLevel().getSortOrder();
  							if (levelFp.getFigureOrder().compareTo(maxOrder) > 0
  									&& newLevelOrder >= levelOrder) {
  								maxOrder = levelFp.getFigureOrder();
  							}
  						}
  						String nextOrder = null;
  						char seq = maxOrder.charAt(maxOrder.length() - 1);
  						if (seq >= 'A') {
  							seq++;
  							nextOrder = maxOrder.substring(0,
  									maxOrder.length() - 1) + seq;
  						} else {
  							nextOrder = maxOrder + "A";
  						}
  						fp.setFigureOrder(nextOrder);
  					}
  					entityManager.persist(fp);
  				}
  				newList.add(fp);
  			}
  
  			// Remaining FiguresParticipants in map not needed, delete them
  			for (FiguresParticipant fp : map.values()) {
  				entityManager.remove(fp);
  			}
  			meet.setFiguresParticipants(newList);
  			if (newList.isEmpty()) {
  				meet.setFiguresOrderGenerated(false);
  				entityManager.persist(meet);
  			}
  			transaction.commit();
  		} catch (Exception e) {
  			logger.error("Error updating figures swimmers.", e);
  			if (transaction.isActive()) {
  				transaction.rollback();
  			}
  		}
  
  		mainFrame.updateFiguresStatus();
  	}
  
  	public boolean generateRandomFiguresOrder(Meet meet,boolean useAge) {
  		Map<Double, FiguresParticipant> map = new TreeMap<Double, FiguresParticipant>();
  		for (FiguresParticipant fp : meet.getFiguresParticipants()) {
  			if(useAge) {
  				map.put(Math.random() + fp.getSwimmer().getLevel().getSortOrder(),
  						fp);
  			}
  			else {
  				map.put(Math.random()+(fp.getSwimmer().getLevel().isNovice()?1:2), fp);
  			}
  		}
  
  		// Calculate length to zero pad numbers (could be trickier, but this is
  		// good enough)
  		String format = "%02d";
  		if (map.size() > 99) {
  			format = "%03d";
  		}
  		if (map.size() > 999) {
  			format = "%04d";
  		}
  
  		EntityTransaction transaction = entityManager.getTransaction();
  		transaction.begin();
  		try {
  			int i = 1;
  			for (FiguresParticipant fp : map.values()) {
  				fp.setFigureOrder(String.format(format, i));
  				i++;
  			}
  			meet.setFiguresOrderGenerated(true);
  			transaction.commit();
  		} catch (Exception e) {
  			logger.error("Error saving randomized figures order.", e);
  			transaction.rollback();
  			return false;
  		}
  
  		return true;
  	}
  
  	public static Figure getFigure(FigureScore fs) {
  		Meet meet = fs.getFiguresParticipant().getMeet();
  		Level level = fs.getFiguresParticipant().getSwimmer().getLevel();
  		if (level.isNovice()) {
  			switch (fs.getStation()) {
  			case 1:
  				return meet.getNov1Figure();
  			case 2:
  				return meet.getNov2Figure();
  			case 3:
  				return meet.getNov3Figure();
  			case 4:
  				return meet.getNov4Figure();
  			}
  		} else {
  			switch (fs.getStation()) {
  			case 1:
  				return meet.getInt1Figure();
  			case 2:
  				return meet.getInt2Figure();
  			case 3:
  				return meet.getInt3Figure();
  			case 4:
  				return meet.getInt4Figure();
  			}
  		}
  		return null;
  	}
  
  	/*
  	 * figureScores=null clears all scores for figuresParticipant
  	 */
  	public boolean saveFigureScores(FiguresParticipant figuresParticipant,
  			Collection<FigureScore> figureScores) {
  		logger.info("Saving figures score for figureOrder="
  				+ figuresParticipant.getFigureOrder());
  		// Mark meet as needing recalc
  		figuresParticipant.getMeet().clearPoints();
  
  		// Calculate totals before saving
  		if (figureScores != null) {
  			for (FigureScore figureScore : figureScores) {
  				if (logger.isDebugEnabled()) {
  					logger.debug("Getting total for station="
  							+ figureScore.getStation());
  				}
  				figureScore.setTotalScore(totalScore(figureScore));
  				if (figureScore.getTotalScore() == null) {
  					logger.error("Saving figure scores aborted because of error getting totalScore.");
  					return false;
  				} else {
  					if (logger.isDebugEnabled()) {
  						logger.debug("Total = "
  								+ figureScore.getTotalScore().toPlainString());
  					}
  				}
  			}
  		}
  
  		EntityTransaction transaction = entityManager.getTransaction();
  		transaction.begin();
  		try {
  			if (figureScores != null) {
  				for (FigureScore figureScore : figureScores) {
  					entityManager.persist(figureScore);
  					if (!figuresParticipant.getFiguresScores().contains(
  							figureScore)) {
  						figuresParticipant.getFiguresScores().add(figureScore);
  					}
  				}
  			} else {
  				for (FigureScore figureScore : figuresParticipant
  						.getFiguresScores()) {
  					entityManager.remove(figureScore);
  				}
  				figuresParticipant
  						.setFiguresScores(new ArrayList<FigureScore>());
  			}
  			figuresParticipant
  					.setTotalScore(calculateTotalScore(figuresParticipant));
  			figuresParticipant.setPlace(null);
  			figuresParticipant.setPoints(null);
  			entityManager.persist(figuresParticipant);
  
  			transaction.commit();
  
  			mainFrame.updateFiguresStatus();
  			logger.info("Saving figures score complete.");
  			return true;
  		} catch (Exception e) {
  			transaction.rollback();
  			logger.error("Error saving figures scores.", e);
  			return false;
  		}
  	}
  
  	public static List<StationResult> generateStationResults(Meet meet,
  			boolean isNovice) {
  		List<StationResult> results = new ArrayList<StationResult>();
  
  		for (FiguresParticipant fp : meet.getFiguresParticipants()) {
  			if (isNovice(fp) == isNovice) {
  				for (FigureScore fs : fp.getFiguresScores()) {
  					StationResult sr = new StationResult();
  					Figure figure = getFigure(fs);
  					sr.setFigureId(figure.getFigureId());
  					sr.setFigureName(figure.getName());
  					sr.setFigureDd(figure.getDegreeOfDifficulty().toString());
  					sr.setLastName(fp.getSwimmer().getLastName());
  					sr.setFirstName(fp.getSwimmer().getFirstName());
  					sr.setFigureOrder(fp.getFigureOrder());
  					sr.setScore(fs.getTotalScore());
  					sr.setLevel(fp.getSwimmer().getLevel().getLevelId());
  					sr.setLevelOrder(fp.getSwimmer().getLevel().getSortOrder()
  							.intValue());
  					sr.setTeam(fp.getSwimmer().getTeam().getTeamId());
  					results.add(sr);
  				}
  			}
  		}
  		Collections.sort(results);
  		return results;
  	}
  
  	public static List<Routine> generateRoutinesResults(Meet meet,
  			boolean showNovice, boolean showIntermediate) {
  		EntityManager entityManager = ScoreApp.getEntityManager();
  		Query query;
  		if (showNovice && !showIntermediate) {
  			query = entityManager
  					.createNamedQuery("Routine.findByMeetAndLevelIsNoviceOrderByLevelAndRoutineTypeAndPlace");
  		} else if (!showNovice && showIntermediate) {
  			query = entityManager
  					.createNamedQuery("Routine.findByMeetAndLevelIsIntermediateOrderByLevelAndRoutineTypeAndPlace");
  		} else if (showNovice && showIntermediate) {
  			query = entityManager
  					.createNamedQuery("Routine.findByMeetOrderByLevelAndRoutineTypeAndPlace");
  		} else {
  			return new ArrayList<Routine>();
  
  		}
  		query.setParameter("meet", meet);
  		return query.getResultList();
  	}
  
  	public static List<Label> generateRoutineLabels(Meet meet,
  			boolean showNovice, boolean showIntermediate, int part,
  			boolean withNames, int min, int max) {
  		List<Routine> routines = generateRoutinesResults(meet, showNovice,
  				showIntermediate);
  		LinkedList<Label> labels = new LinkedList<Label>();
  		for (Routine routine : routines) {
  			
  			if (part == 1
  					&& !routine
  							.getRoutineType().equals("Trio"))
  				continue;
  			if (part == 2
  					&& !routine
  							.getRoutineType().equals("Duet"))
  				continue;
  			if (part == 3
  					&& ( !routine
  							.getRoutineType().equals("Team")||routine.getLevel().getLevelId().equals("I11-18CT")))
  				continue;
  			if (part == 4
  					&& !routine
  							.getRoutineType().equals("Solo"))
  				continue;
  			if (part == 5
  					&& !routine.getLevel().getLevelId().equals("I11-18CT"))
  				continue;
  			
  			if (routine.getPlace() < min || routine.getPlace() > max)
  				continue;
  			List<String> names = null;
  			if(withNames) {
  				names=RoutineManager.getNames(routine);
  			}
  			for (int i = 0; i < routine.getNumSwimmers(); i++) {
  				String swimmerName = "";
  				if(names!=null&& names.size()>i) {
  					swimmerName=names.get(i);
  				}
  				String level;
  				if (withNames) {
  					String levelId = routine.getLevel().getLevelId();
  					if (levelId.equals("I11-18CT")) {
  						level = "I11-18 Combo";
  					} else {
  						if (levelId.equals("I15"))
  							levelId = "I15-18";
  						if (levelId.equals("I11"))
  							levelId = "I11-14";
  						level = levelId + " " + routine.getRoutineType();
  					}
  				} else {
  					if (routine.getLevel().getName().endsWith("Team")) {
  						level = routine.getLevel().getName();
  					} else {
  						level = routine.getLevel().getName() + " "
  								+ routine.getRoutineType();
  					}
  				}
  				Label label = new Label(level, routine.getLevel()
  						.getSortOrder(),
  						(!withNames || swimmerName.isEmpty()) ? routine
  								.getName() : (swimmerName + " in " + routine
  								.getName()), routine.getPlace(), routine
  								.getTeam().getTeamId(), routine.getTotalScore());
  				labels.add(label);
  			}
  		}
  		return labels;
  	}
  
  	public static List<Label> generateFiguresLabels(Meet meet, boolean isNovice) {
  		List<Label> results = new ArrayList<Label>();
  
  		for (FiguresParticipant fp : meet.getFiguresParticipants()) {
  			if (isNovice(fp) == isNovice && figuresParticipantHasAllScores(fp)) {
  				Label fl = new Label(fp.getSwimmer().getLevel().getName(), fp
  						.getSwimmer().getLevel().getSortOrder(), fp
  						.getSwimmer().getFirstName()
  						+ " "
  						+ fp.getSwimmer().getLastName(), fp.getPlace(), fp
  						.getSwimmer().getTeam().getTeamId(), fp.getTotalScore());
  				results.add(fl);
  			}
  		}
  		Collections.sort(results);
  		return results;
  	}
  
  	public static List<FiguresMeetSheet> generateFiguresMeetSheets(Meet meet,
  			boolean isNovice) {
  		List<FiguresMeetSheet> results = new ArrayList<FiguresMeetSheet>();
  
  		for (FiguresParticipant fp : meet.getFiguresParticipants()) {
  			if (isNovice(fp) == isNovice && figuresParticipantHasAllScores(fp)) {
  				results.add(new FiguresMeetSheet(fp));
  			}
  		}
  		Collections.sort(results);
  		return results;
  	}
  
  	public static boolean isValid(FigureScore figureScore) {
  		return figureScore.getScore1() != null
  				&& figureScore.getScore2() != null
  				&& figureScore.getScore3() != null
  				&& figureScore.getScore4() != null
  				&& figureScore.getScore5() != null
  				&& figureScore.getPenalty() != null
  				&& figureScore.getFiguresParticipant() != null
  				&& figureScore.getStation() >= 1
  				&& figureScore.getStation() <= 4;
  	}
  
  	public static boolean isNovice(FiguresParticipant fp) {
  		return fp.getSwimmer().getLevel().getLevelId().startsWith("N");
  	}
  
  	public static int numberOfFigures(FiguresParticipant fp) {
  		if (fp.getMeet().getSeason().getRulesRevision() < 2) {
  			// Before 2011
  			if (fp.getMeet().getType() != 'C'
  					&& "N8".equals(fp.getSwimmer().getLevel().getLevelId())) {
  				return 2; // Novice 8 & Under have two figures
  			} else {
  				return 4; // All others have four figures
  
  			}
  		} else {
  			return 4;
  		}
  	}
  
  	public static int percentCompleteFigures(Meet meet, boolean countNovice) {
  		int count = 0;
  		int possible = 0;
  		for (FiguresParticipant fp : meet.getFiguresParticipants()) {
  			if (isNovice(fp) == countNovice) {
  				possible += numberOfFigures(fp);
  				count += fp.getFiguresScores().size();
  			}
  		}
  		int percent = possible > 0 ? (count * 100) / possible : 0;
  
  		if (percent > 100) {
  			logger.error("percent = " + percent);
  		}
  
  		if (logger.isDebugEnabled()) {
  			if (countNovice) {
  				logger.debug("Novice figures are " + percent + "% complete.");
  			} else {
  				logger.debug("Intermediate figures are " + percent
  						+ "% complete.");
  			}
  		}
  		return percent;
  	}
  
  	public static BigDecimal totalScore(FigureScore fs) {
  		// Must be valid (values for all five scores, penalty, etc)
  		if (!isValid(fs)) {
  			return null;
  		}
  
  		// Drop the lowest and highest scores
  		List<BigDecimal> scores = new ArrayList<BigDecimal>(5);
  		scores.add(fs.getScore1());
  		scores.add(fs.getScore2());
  		scores.add(fs.getScore3());
  		scores.add(fs.getScore4());
  		scores.add(fs.getScore5());
  
  		BigDecimal max = BigDecimal.ZERO; // No score smaller than 0
  		BigDecimal min = BigDecimal.TEN; // No score larger than 10
  		for (BigDecimal score : scores) {
  			if (score.compareTo(max) > 0) {
  				max = score;
  			}
  			if (score.compareTo(min) < 0) {
  				min = score;
  			}
  		}
  		boolean minRemoved = false;
  		boolean maxRemoved = false;
  		List<BigDecimal> adjScores = new ArrayList<BigDecimal>(5);
  		for (BigDecimal score : scores) {
  			if (!minRemoved && score.compareTo(min) == 0) {
  				minRemoved = true;
  			} else if (!maxRemoved && score.compareTo(max) == 0) {
  				maxRemoved = true;
  			} else {
  				adjScores.add(score);
  			}
  		}
  
  		// We must now have three scores
  		if (adjScores.size() != 3) {
  			logger.error("Dropped min and max but didn't end up with three scores.");
  			logger.error("Scores = " + scores);
  			logger.error("AdjScores = " + adjScores);
  			return null;
  		}
  
  		// Sum the scores
  		BigDecimal sum = BigDecimal.ZERO;
  		for (BigDecimal score : adjScores) {
  