package ca.josephroque.bowlingcompanion.utilities; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import java.util.List; import ca.josephroque.bowlingcompanion.Constants; import ca.josephroque.bowlingcompanion.database.Contract; import ca.josephroque.bowlingcompanion.database.DatabaseHelper; /** * Created by Joseph Roque on 2015-07-25. Methods and constants for identifying and recording statistics. */ public final class StatUtils { /** Identifies output from this class in Logcat. */ @SuppressWarnings("unused") private static final String TAG = "StatUtils"; /** Indicates all the stats related to the specified bowler should be loaded. */ public static final byte LOADING_BOWLER_STATS = 0; /** Indicates all the stats related to the specified league should be loaded. */ public static final byte LOADING_LEAGUE_STATS = 1; /** Indicates all the stats related to the specified series should be loaded. */ public static final byte LOADING_SERIES_STATS = 2; /** Indicates only the stats related to the specified game should be loaded. */ public static final byte LOADING_GAME_STATS = 3; /** Indicates index for stat category. */ public static final byte STAT_CATEGORY_GENERAL = 0; /** Indicates index for stat in array. */ public static final byte STAT_MIDDLE_HIT = 0; /** Indicates index for stat in array. */ public static final byte STAT_LEFT_OF_MIDDLE = 1; /** Indicates index for stat in array. */ public static final byte STAT_RIGHT_OF_MIDDLE = 2; /** Indicates index for stat in array. */ public static final byte STAT_STRIKES = 3; /** Indicates index for stat in array. */ public static final byte STAT_SPARE_CONVERSIONS = 4; /** Indicates index for stat category. */ public static final byte STAT_CATEGORY_FIRST_BALL = 1; /** Indicates index for stat in array. */ public static final byte STAT_HEAD_PINS = 0; /* Indicates index for stat in array. */ // public static final byte STAT_HEAD_PINS_SPARED = 1; /** Indicates index for stat in array. */ public static final byte STAT_LEFT = 2; /* Indicates index for stat in array. */ // public static final byte STAT_LEFT_SPARED = 3; /** Indicates index for stat in array. */ public static final byte STAT_RIGHT = 4; /* Indicates index for stat in array. */ // public static final byte STAT_RIGHT_SPARED = 5; /** Indicates index for stat in array. */ public static final byte STAT_ACES = 6; /* Indicates index for stat in array. */ // public static final byte STAT_ACES_SPARED = 7; /** Indicates index for stat in array. */ public static final byte STAT_CHOP = 8; /* Indicates index for stat in array. */ // public static final byte STAT_CHOP_SPARED = 9; /** Indicates index for stat in array. */ public static final byte STAT_LEFT_CHOP = 10; /* Indicates index for stat in array. */ // public static final byte STAT_LEFT_CHOP_SPARED = 11; /** Indicates index for stat in array. */ public static final byte STAT_RIGHT_CHOP = 12; /* Indicates index for stat in array. */ // public static final byte STAT_RIGHT_CHOP_SPARED = 13; /** Indicates index for stat in array. */ public static final byte STAT_SPLIT = 14; /* Indicates index for stat in array. */ // public static final byte STAT_SPLIT_SPARED = 15; /** Indicates index for stat in array. */ public static final byte STAT_LEFT_SPLIT = 16; /* Indicates index for stat in array. */ // public static final byte STAT_LEFT_SPLIT_SPARED = 17; /** Indicates index for stat in array. */ public static final byte STAT_RIGHT_SPLIT = 18; /** Indicates index for stat in array. */ public static final byte STAT_RIGHT_SPLIT_SPARED = 19; /** Indicates index for stat category. */ public static final byte STAT_CATEGORY_FOULS = 2; /** Indicates index for stat in array. */ public static final byte STAT_FOULS = 0; /** Indicates index for stat category. */ public static final byte STAT_CATEGORY_PINS = 3; /** Indicates index for stat in array. */ public static final byte STAT_PINS_LEFT = 0; /** Indicates index for stat in array. */ public static final byte STAT_PINS_AVERAGE = 1; /** Indicates index for stat category. */ public static final byte STAT_CATEGORY_AVERAGE_BY_GAME = 4; /** Indicates index for stat category. */ public static final byte STAT_CATEGORY_MATCH_PLAY = 5; /** Indicates index for stat in array. */ public static final byte STAT_WON = 0; /** Indicates index for stat in array. */ public static final byte STAT_LOST = 1; /** Indicates index for stat in array. */ public static final byte STAT_TIED = 2; /** Indicates index for stat category. */ public static final byte STAT_CATEGORY_OVERALL = 6; /** Indicates index for stat in array. */ public static final byte STAT_AVERAGE = 0; /** Indicates index for stat in array. */ public static final byte STAT_HIGH_SINGLE = 1; /** Indicates index for stat in array. */ public static final byte STAT_HIGH_SERIES = 2; /** Indicates index for stat in array. */ public static final byte STAT_TOTAL_PINS = 3; /** Indicates index for stat in array. */ public static final byte STAT_NUMBER_OF_GAMES = 4; /** * Gets the name of a stat based on its category and index in that category. * * @param statCategory category of the stat * @param statIndex index of the stat * @param chanceName if true, returns a name which describes the chances the user had to increase the stat, or null * if such a name does not exist. If false, returns the name of the stat * @return name of the stat or the "chances" name of the stat */ public static String getStatName(int statCategory, int statIndex, boolean chanceName) { switch (statCategory) { case StatUtils.STAT_CATEGORY_GENERAL: return getGeneralStatName(statIndex, chanceName); case StatUtils.STAT_CATEGORY_FIRST_BALL: return getFirstBallStatName(statIndex, chanceName); case StatUtils.STAT_CATEGORY_FOULS: return getFoulStatName(statIndex, chanceName); case StatUtils.STAT_CATEGORY_PINS: return getPinStatName(statIndex, chanceName); case StatUtils.STAT_CATEGORY_AVERAGE_BY_GAME: return getAverageByGameStatName(statIndex, chanceName); case StatUtils.STAT_CATEGORY_MATCH_PLAY: return getMatchPlayStatName(statIndex, chanceName); case StatUtils.STAT_CATEGORY_OVERALL: return getOverallStatName(statIndex, chanceName); default: throw new IllegalArgumentException("invalid stat category: " + statCategory); } } /** * Gets the name of a stat in the "overall" category. * * @param statIndex index of the stat * @param chanceName if true, method returns null * @return name of the "overall" stat */ private static String getOverallStatName(int statIndex, boolean chanceName) { if (chanceName) return null; switch (statIndex) { case STAT_AVERAGE: return "Average"; case STAT_HIGH_SINGLE: return "High Single"; case STAT_HIGH_SERIES: return "High Series"; case STAT_TOTAL_PINS: return "Total Pinfall"; case STAT_NUMBER_OF_GAMES: return "# of Games"; default: throw new IllegalArgumentException( "invalid index " + statIndex + " for overall category"); } } /** * Gets the name of a stat in the "match" category. * * @param statIndex index of the stat * @param chanceName if true, returns a stat name representing the chances a user could have achieved a stat. If * false, returns the name of the achievable stat * @return name of the "match" stat */ private static String getMatchPlayStatName(int statIndex, boolean chanceName) { if (chanceName) return "Total Match Play Games"; switch (statIndex) { case STAT_WON: return "Games Won"; case STAT_LOST: return "Games Lost"; case STAT_TIED: return "Games Tied"; default: throw new IllegalArgumentException( "invalid index " + statIndex + " for match play category"); } } /** * Gets the name of a stat in the "average by game" category. * * @param statIndex index of the stat * @param chanceName if true, returns null * @return name of the "average by game" stat */ private static String getAverageByGameStatName(int statIndex, boolean chanceName) { if (chanceName) return null; else if (statIndex >= 0 && statIndex < Constants.MAX_NUMBER_EVENT_GAMES) return "Average in Game " + (statIndex + 1); else throw new IllegalArgumentException("invalid index " + statIndex + "for game average"); } /** * Gets the name of a stat in the "pins" category. * * @param statIndex index of the stat * @param chanceName if true, returns null * @return name of the "pins" stat */ private static String getPinStatName(int statIndex, boolean chanceName) { if (chanceName) return null; switch (statIndex) { case STAT_PINS_LEFT: return "Total Pins Left"; case STAT_PINS_AVERAGE: return "Average Pins Left"; default: throw new IllegalArgumentException( "invalid index " + statIndex + " for pins category"); } } /** * Gets the name of a stat in the "foul" category. * * @param statIndex index of the stat * @param chanceName if true, returns null * @return name of the "foul" stat */ private static String getFoulStatName(int statIndex, boolean chanceName) { if (chanceName) return null; switch (statIndex) { case STAT_FOULS: return "Fouls"; default: throw new IllegalArgumentException( "invalid index " + statIndex + " for fouls category"); } } /** * Gets the name of a stat in the "first ball" category. * * @param statIndex index of the stat * @param chanceName if true, returns a stat name representing the chances a user could have achieved a stat. If * false, returns the name of the achievable stat * @return name of the "first ball" stat */ private static String getFirstBallStatName(int statIndex, boolean chanceName) { boolean spared = (statIndex % 2 == 1); if (spared) statIndex--; final String statName; switch (statIndex) { case STAT_HEAD_PINS: statName = "Head Pins"; break; case STAT_LEFT: statName = "Lefts"; break; case STAT_RIGHT: statName = "Rights"; break; case STAT_ACES: statName = "Aces"; break; case STAT_CHOP: statName = "Chop Offs"; break; case STAT_LEFT_CHOP: statName = "Left Chop Offs"; break; case STAT_RIGHT_CHOP: statName = "Right Chop Offs"; break; case STAT_SPLIT: statName = "Splits"; break; case STAT_LEFT_SPLIT: statName = "Left Splits"; break; case STAT_RIGHT_SPLIT: statName = "Right Splits"; break; default: throw new IllegalArgumentException( "invalid index " + statIndex + " for first ball category"); } if (chanceName) return (spared) ? "Total " + statName : "Total Shots at Middle"; else if (spared) return statName + " Spared"; else return statName; } /** * Gets the name of a stat in the "general" category. * * @param statIndex index of the stat * @param chanceName if true, returns a stat name representing the chances a user could have achieved a stat. If * false, returns the name of the achievable stat * @return name of the "general" stat */ private static String getGeneralStatName(int statIndex, boolean chanceName) { switch (statIndex) { case STAT_LEFT_OF_MIDDLE: return (chanceName) ? "Total Shots at Middle" : "Hits Left of Middle"; case STAT_RIGHT_OF_MIDDLE: return (chanceName) ? "Total Shots at Middle" : "Hits Right of Middle"; case STAT_MIDDLE_HIT: return (chanceName) ? "Total Shots at Middle" : "Middle Hits"; case STAT_STRIKES: return (chanceName) ? "Total Shots at Middle" : "Strikes"; case STAT_SPARE_CONVERSIONS: return (chanceName) ? "Total Spare Chances" : "Spare Conversions"; default: throw new IllegalArgumentException( "invalid index " + statIndex + " for general category"); } } /** * Queries the database for the base averages or average of the bowler or league that the stats are being loaded * for. * * @param context to access database * @param id unique id of the league or bowler to get the stats of * @param getLeagueStats {@code true} if getting the stats of a single league, false otherwise * @param baseAverages to append the list of base averages for the league or bowler * @param baseGameCounts to append the list of number of games for the respective base average for the league or * bowler */ public static void getBaseAverages(Context context, long id, boolean getLeagueStats, List<Short> baseAverages, List<Integer> baseGameCounts) { String baseAverageAndGamesQuery = "SELECT " + Contract.LeagueEntry.COLUMN_BASE_AVERAGE + ", " + Contract.LeagueEntry.COLUMN_BASE_GAMES + " FROM " + Contract.LeagueEntry.TABLE_NAME + ( (getLeagueStats) ? " WHERE " + Contract.LeagueEntry._ID + "=?" : " WHERE " + Contract.LeagueEntry.COLUMN_BASE_AVERAGE + ">?" + " AND " + Contract.LeagueEntry.COLUMN_BOWLER_ID + "=?"); String[] rawBaseArgs; if (getLeagueStats) rawBaseArgs = new String[]{String.valueOf(id)}; else rawBaseArgs = new String[]{String.valueOf(0), String.valueOf(id)}; SQLiteDatabase database = DatabaseHelper.getInstance(context).getReadableDatabase(); Cursor cursor = database.rawQuery(baseAverageAndGamesQuery, rawBaseArgs); if (cursor.moveToFirst()) { while (!cursor.isAfterLast()) { baseAverages.add(cursor.getShort(cursor.getColumnIndex(Contract.LeagueEntry.COLUMN_BASE_AVERAGE))); baseGameCounts.add(cursor.getInt(cursor.getColumnIndex(Contract.LeagueEntry.COLUMN_BASE_GAMES))); cursor.moveToNext(); } } cursor.close(); } /** * Returns the indicated state of the pins after a ball was thrown. * * @param firstBall the ball thrown * @param countH2 indicates if H2 should be counted as H * @param countS2 indicates if S2 should be counted as S * @return the state of the pins after a ball was thrown */ public static int getFirstBallValue(boolean[] firstBall, boolean countH2, boolean countS2) { if (!firstBall[2]) { return -1; } int numberOfPinsKnockedDown = 0; for (boolean knockedDown : firstBall) { if (knockedDown) numberOfPinsKnockedDown++; } if (numberOfPinsKnockedDown == Constants.NUMBER_OF_PINS) return Constants.BALL_VALUE_STRIKE; else if (numberOfPinsKnockedDown == Constants.NUMBER_OF_PINS - 1) { if (!firstBall[Score.LEFT_2_PIN]) return Constants.BALL_VALUE_LEFT; else if (!firstBall[Score.RIGHT_2_PIN]) return Constants.BALL_VALUE_RIGHT; } else if (numberOfPinsKnockedDown == Constants.NUMBER_OF_PINS - 2) { if (firstBall[Score.LEFT_2_PIN] && firstBall[Score.LEFT_3_PIN]) return Constants.BALL_VALUE_LEFT_CHOP; else if (firstBall[Score.RIGHT_2_PIN] && firstBall[Score.RIGHT_3_PIN]) return Constants.BALL_VALUE_RIGHT_CHOP; else if (firstBall[Score.LEFT_3_PIN] && firstBall[Score.RIGHT_3_PIN]) return Constants.BALL_VALUE_ACE; else if (countS2) { if (firstBall[Score.LEFT_3_PIN] && firstBall[Score.RIGHT_2_PIN]) return Constants.BALL_VALUE_LEFT_SPLIT; else if (firstBall[Score.RIGHT_3_PIN] && firstBall[Score.LEFT_2_PIN]) return Constants.BALL_VALUE_RIGHT_SPLIT; } else { if (firstBall[Score.LEFT_2_PIN] && firstBall[Score.RIGHT_3_PIN] || (firstBall[Score.LEFT_3_PIN] && firstBall[Score.RIGHT_2_PIN])) return Constants.BALL_VALUE_HEAD_PIN_2_3; } } else if (numberOfPinsKnockedDown == 2) { if (firstBall[Score.LEFT_3_PIN]) return Constants.BALL_VALUE_LEFT_SPLIT; else if (firstBall[Score.RIGHT_3_PIN]) return Constants.BALL_VALUE_RIGHT_SPLIT; else if (firstBall[Score.LEFT_2_PIN] || firstBall[Score.RIGHT_2_PIN]) return (countH2) ? Constants.BALL_VALUE_HEAD_PIN : Constants.BALL_VALUE_HEAD_PIN_2; } else return Constants.BALL_VALUE_HEAD_PIN; // Returns a value which indicates the headpin was hit, but no other special ball was thrown //noinspection CheckStyle return -2; } /** * Default private constructor. */ private StatUtils() { // does nothing } }