Coverage Report - com.liquidatom.derbyscore.domain.Bout
 
Classes in this File Line Coverage Branch Coverage Complexity
Bout
0%
0/163
0%
0/40
1.73
 
 1  
 package com.liquidatom.derbyscore.domain;
 2  
 
 3  
 import java.util.LinkedHashSet;
 4  
 import java.util.Set;
 5  
 import java.util.concurrent.ExecutorService;
 6  
 import java.util.concurrent.Executors;
 7  
 import java.util.concurrent.TimeUnit;
 8  
 import java.util.concurrent.locks.Lock;
 9  
 import java.util.concurrent.locks.ReadWriteLock;
 10  
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 11  
 import javax.annotation.concurrent.GuardedBy;
 12  
 import javax.annotation.concurrent.ThreadSafe;
 13  
 
 14  
 /**
 15  
  * A domain object which represents a bout between two teams.
 16  
  *
 17  
  * @author Russell Francis (russ@metro-six.com)
 18  
  */
 19  
 @ThreadSafe
 20  
 public class Bout implements ClockListener, TeamListener, ReadWriteLock {
 21  
 
 22  0
     static public final Duration PERIOD_DURATION = new Duration(30, TimeUnit.MINUTES);
 23  0
     static public final Duration JAM_DURATION = new Duration(2, TimeUnit.MINUTES);
 24  0
     static public final Duration TEAM_TIMEOUT_DURATION = new Duration(1, TimeUnit.MINUTES);
 25  0
     static public final Duration LINEUP_DURATION = new Duration(30, TimeUnit.SECONDS);
 26  0
     static public final Duration INTERMISSION_DURATION = new Duration(10, TimeUnit.MINUTES);
 27  0
     static public final Duration OVERTIME_LINEUP_DURATION = new Duration(1, TimeUnit.MINUTES);
 28  
 
 29  0
     private final ExecutorService executorService = Executors.newCachedThreadPool();
 30  
 
 31  0
     private final ReadWriteLock lock = new ReentrantReadWriteLock();
 32  0
     private final ClockExecutor clockExecutor = new ClockExecutor();
 33  0
     private final Clock periodClock = new Clock(PERIOD_DURATION, true);
 34  0
     private final Clock jamClock = new Clock(JAM_DURATION, false);
 35  
     private final Team teamA;
 36  
     private final Team teamB;
 37  
 
 38  0
     @GuardedBy("itself")
 39  
     private final Set<BoutListener> boutListeners = new LinkedHashSet<BoutListener>();
 40  
 
 41  0
     @GuardedBy("lock")
 42  
     private int period = 1;
 43  0
     @GuardedBy("lock")
 44  
     private boolean overtime = false;
 45  
     
 46  
     // The team which currently has lead jammer.
 47  
     @GuardedBy("lock")
 48  
     private Team lead;
 49  
 
 50  0
     @GuardedBy("lock")
 51  
     private BoutState boutState = BoutState.UNCONFIGURED;
 52  
 
 53  
     public Bout(final Team teamA, final Team teamB) {
 54  0
         super();
 55  
 
 56  0
         this.teamA = teamA;
 57  0
         this.teamA.addListener(this);
 58  
 
 59  0
         this.teamB = teamB;
 60  0
         this.teamB.addListener(this);
 61  
 
 62  0
         clockExecutor.addClock(periodClock);
 63  0
         periodClock.addListener(this);
 64  
 
 65  0
         clockExecutor.addClock(jamClock);
 66  0
         jamClock.addListener(this);
 67  
 
 68  0
         executorService.submit(clockExecutor);
 69  0
     }
 70  
 
 71  
     public boolean addListener(BoutListener listener) {
 72  0
         synchronized (boutListeners) {
 73  0
             return boutListeners.add(listener);
 74  0
         }
 75  
     }
 76  
 
 77  
     public boolean removeListener(BoutListener listener) {
 78  0
         synchronized (boutListeners) {
 79  0
             return boutListeners.remove(listener);
 80  0
         }
 81  
     }
 82  
 
 83  
     private void fireOnChanged() {
 84  0
         synchronized (boutListeners) {
 85  0
             for (BoutListener listener : boutListeners) {
 86  0
                 listener.onChanged();
 87  
             }
 88  0
         }
 89  0
     }
 90  
 
 91  
     /**
 92  
      * Returns the team which currently has lead or null if the lead jammer status has not been assigned to any
 93  
      * particular team for this jam.
 94  
      *
 95  
      * @return The team which has the lead jammer status or null if it has not been assigned to any team.
 96  
      */
 97  
     public Team getLead() {
 98  0
         readLock().lock();
 99  
         try {
 100  0
             return lead;
 101  
         }
 102  
         finally {
 103  0
             readLock().unlock();
 104  
         }
 105  
     }
 106  
 
 107  
     public boolean isTeamALead() {
 108  0
         readLock().lock();
 109  
         try {
 110  0
             return lead != null && lead.equals(teamA);
 111  
         }
 112  
         finally {
 113  0
             readLock().unlock();
 114  
         }
 115  
     }
 116  
 
 117  
     public boolean isTeamBLead() {
 118  0
         readLock().lock();
 119  
         try {
 120  0
             return lead != null && lead.equals(teamB);
 121  
         }
 122  
         finally {
 123  0
             readLock().unlock();
 124  
         }
 125  
     }
 126  
 
 127  
     /**
 128  
      * Set the team which has lead jammer status.
 129  
      *
 130  
      * @param lead The team which has lead jammer status or null if the status should be cleared from both teams.
 131  
      */
 132  
     public void setLead(final Team lead) {
 133  0
         boolean fireOnChanged = false;
 134  
         
 135  0
         writeLock().lock();
 136  
         try {
 137  0
             if (lead != null && !lead.equals(teamA) && !lead.equals(teamB)) {
 138  0
                 throw new IllegalStateException("Unable to assign lead status to a team which isn't in this bout.");
 139  
             }
 140  
             
 141  0
             if (this.lead != lead) {
 142  0
                 fireOnChanged = true;
 143  0
                 this.lead = lead;
 144  
             }
 145  
         }
 146  
         finally {
 147  0
             writeLock().unlock();
 148  0
         }
 149  
         
 150  0
         if (fireOnChanged) {
 151  0
             fireOnChanged();
 152  
         }
 153  0
     }
 154  
 
 155  
     public int getPeriod() {
 156  0
         readLock().lock();
 157  
         try {
 158  0
             return period;
 159  
         }
 160  
         finally {
 161  0
             readLock().unlock();
 162  
         }
 163  
     }
 164  
 
 165  
     public void setPeriod(final int period) {
 166  0
         if (period != 1 && period != 2) {
 167  0
             throw new IllegalArgumentException("The period must be 1 or 2.");
 168  
         }
 169  
 
 170  0
         writeLock().lock();
 171  
         try {
 172  0
             this.period = period;
 173  
         }
 174  
         finally {
 175  0
             writeLock().unlock();
 176  0
         }
 177  0
     }
 178  
 
 179  
     public boolean isOvertime() {
 180  0
         readLock().lock();
 181  
         try {
 182  0
             return overtime;
 183  
         }
 184  
         finally {
 185  0
             readLock().unlock();
 186  
         }
 187  
     }
 188  
 
 189  
     public void setOvertime(final boolean overtime) {
 190  0
         writeLock().lock();
 191  
         try {
 192  0
             this.overtime = overtime;
 193  
         }
 194  
         finally {
 195  0
             writeLock().unlock();
 196  0
         }
 197  0
     }
 198  
 
 199  
     public BoutState getBoutState() {
 200  0
         readLock().lock();
 201  
         try {
 202  0
             return boutState;
 203  
         }
 204  
         finally {
 205  0
             readLock().unlock();
 206  
         }
 207  
     }
 208  
 
 209  
     protected void setBoutState(final BoutState boutState) {
 210  0
         if (boutState == null) {
 211  0
             throw new IllegalArgumentException("The parameter boutState must be non-null.");
 212  
         }
 213  
 
 214  0
         boolean fireOnChanged = false;
 215  
 
 216  0
         writeLock().lock();
 217  
         try {
 218  0
             if (!boutState.equals(this.boutState)) {
 219  0
                 fireOnChanged = true;
 220  0
                 this.boutState = boutState;
 221  
             }
 222  
         }
 223  
         finally {
 224  0
             writeLock().unlock();
 225  0
         }
 226  
 
 227  0
         if (fireOnChanged) {
 228  0
             fireOnChanged();
 229  
         }
 230  0
     }
 231  
     
 232  
     public int getAdDigit() {
 233  
         // Toggle the ads every 130 seconds  130 * 1000.
 234  0
         return (int) ((System.currentTimeMillis() / (130 * 1000)) % 2);
 235  
     }
 236  
 
 237  
     public boolean isTimeout() {
 238  0
         return BoutState.TEAM_TIMEOUT.equals(getBoutState());
 239  
     }
 240  
 
 241  
     public Clock getPeriodClock() {
 242  0
         return periodClock;
 243  
     }
 244  
 
 245  
     public Clock getJamClock() {
 246  0
         return jamClock;
 247  
     }
 248  
 
 249  
     public Team getTeamA() {
 250  0
         return teamA;
 251  
     }
 252  
 
 253  
     public Team getTeamB() {
 254  0
         return teamB;
 255  
     }
 256  
 
 257  
     public void markConfigured() {
 258  0
         setBoutState(BoutState.CONFIGURED);
 259  0
     }
 260  
 
 261  
     /**
 262  
      * Call a timeout for the given team, in order for a timeout to be successfully called, the bout must be in the
 263  
      * LINEUP state and the team must have a positive number of timeouts remaining.
 264  
      *
 265  
      * @return true if the timeout was called successfully, false otherwise.
 266  
      */
 267  
     public void timeout() {
 268  0
         writeLock().lock();
 269  
         try {
 270  0
             getPeriodClock().pause();
 271  0
             getJamClock().reset(TEAM_TIMEOUT_DURATION);
 272  0
             getJamClock().beginAndSynchronizeTo(periodClock);
 273  
 
 274  0
             setBoutState(BoutState.TEAM_TIMEOUT);
 275  
         }
 276  
         finally {
 277  0
             writeLock().unlock();
 278  0
         }
 279  0
     }
 280  
 
 281  
     public void officialTimeout() {
 282  0
         writeLock().lock();
 283  
         try {
 284  0
             getPeriodClock().pause();
 285  0
             getJamClock().pause();
 286  0
             setBoutState(BoutState.OFFICIAL_TIMEOUT);
 287  
         }
 288  
         finally {
 289  0
             writeLock().unlock();
 290  0
         }
 291  0
     }
 292  
 
 293  
     public void beginJam() {
 294  0
         writeLock().lock();
 295  
         try {
 296  
 //            if (getBoutState().equals(BoutState.INTERMISSION)) {
 297  
 //                getPeriodClock().end();
 298  
 //                getPeriodClock().reset(PERIOD_DURATION);
 299  
 //            }
 300  
 
 301  0
             getJamClock().end();
 302  0
             getJamClock().reset(JAM_DURATION);
 303  
 
 304  0
             setLead(null);
 305  0
             getTeamA().applyJamPoints();
 306  0
             getTeamB().applyJamPoints();
 307  
 
 308  0
             getPeriodClock().begin();
 309  0
             getPeriodClock().resume();
 310  0
             getJamClock().beginAndSynchronizeTo(periodClock);
 311  
 
 312  0
             setBoutState(BoutState.JAMMING);
 313  
         }
 314  
         finally {
 315  0
             writeLock().unlock();
 316  0
         }
 317  0
     }
 318  
 
 319  
     public void lineup() {
 320  0
         writeLock().lock();
 321  
         try {
 322  0
             getJamClock().end();
 323  0
             getJamClock().reset(LINEUP_DURATION);
 324  0
             getJamClock().beginAndSynchronizeTo(getPeriodClock());
 325  0
             setBoutState(BoutState.LINEUP);
 326  
         }
 327  
         finally {
 328  0
             writeLock().unlock();
 329  0
         }
 330  0
     }
 331  
 
 332  
     public void endJam() {
 333  0
         lineup();
 334  0
     }
 335  
 
 336  
     public void onPause(Clock clock) {
 337  0
     }
 338  
 
 339  
     public void onResume(Clock clock) {
 340  0
     }
 341  
 
 342  
     public void onChanged(Clock clock) {
 343  0
         fireOnChanged();
 344  0
     }
 345  
 
 346  
     public void onBegin(Clock clock) {
 347  0
         fireOnChanged();
 348  0
     }
 349  
 
 350  
     public void onEnd(Clock clock) {
 351  0
         if (clock == getPeriodClock()) {
 352  0
             onPeriodClockEnd();
 353  
         }
 354  0
         else if (clock == getJamClock()) {
 355  0
             onJamClockEnd();
 356  
         }
 357  
 
 358  0
         fireOnChanged();
 359  0
     }
 360  
 
 361  
     public void onTerminate(Clock clock) {
 362  0
         fireOnChanged();
 363  0
     }
 364  
 
 365  
     public void onChanged(Team team) {
 366  0
         fireOnChanged();
 367  0
     }
 368  
 
 369  
     private void onPeriodClockEnd() {
 370  
 //        writeLock().lock();
 371  
 //        try {
 372  
 //            // period ended.
 373  
 //            if (getBoutState().equals(BoutState.INTERMISSION)) {
 374  
 //                getPeriodClock().reset(PERIOD_DURATION);
 375  
 //                getJamClock().reset(JAM_DURATION);
 376  
 //            }
 377  
 //            // if state is jamming let it finish.
 378  
 //            else if (!getBoutState().equals(BoutState.JAMMING)) {
 379  
 //                getJamClock().reset(JAM_DURATION);
 380  
 //                if (getPeriod() == 1) {
 381  
 //                    setPeriod(getPeriod() + 1);
 382  
 //                    getPeriodClock().reset(INTERMISSION_DURATION);
 383  
 //                    getPeriodClock().begin();
 384  
 //                    setBoutState(BoutState.INTERMISSION);
 385  
 //                }
 386  
 //                else {
 387  
 //                    // overtime (?)
 388  
 //                }
 389  
 //            }
 390  
 //        }
 391  
 //        finally {
 392  
 //            writeLock().unlock();
 393  
 //        }
 394  0
     }
 395  
 
 396  
     private void onJamClockEnd() {
 397  0
         writeLock().lock();
 398  
         try {
 399  0
             if (getBoutState().equals(BoutState.LINEUP)) {
 400  
                 // Don't automatically start the next jam.  require a user click to start.
 401  
                 // beginJam();
 402  
             }
 403  0
             else if (getBoutState().equals(BoutState.JAMMING) || isTimeout()) {
 404  0
                 endJam();
 405  
             }
 406  
 //
 407  
 //            if (getPeriodClock().isEnded()) {
 408  
 //                if (getPeriod() == 1) {
 409  
 //                    setPeriod(getPeriod() + 1);
 410  
 //                    getJamClock().reset(JAM_DURATION);
 411  
 //                    getPeriodClock().reset(INTERMISSION_DURATION);
 412  
 //                    getPeriodClock().begin();
 413  
 //                    setBoutState(BoutState.INTERMISSION);
 414  
 //                }
 415  
 //                else if (getPeriod() == 2 && !isOvertime()) {
 416  
 //                    setOvertime(true);
 417  
 //                    getJamClock().reset(OVERTIME_LINEUP_DURATION);
 418  
 //                    getJamClock().beginAndSynchronizeTo(getPeriodClock());
 419  
 //                    setBoutState(BoutState.OVERTIME_LINEUP);
 420  
 //                }
 421  
 //            }
 422  
         }
 423  
         finally {
 424  0
             writeLock().unlock();
 425  0
         }
 426  0
     }
 427  
 
 428  
     public Lock readLock() {
 429  0
         return lock.readLock();
 430  
     }
 431  
 
 432  
     public Lock writeLock() {
 433  0
         return lock.writeLock();
 434  
     }
 435  
 }