View Javadoc
1   /*******************************************************************************
2    * jhunters: Pool League
3    * Copyright 2015 Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   ********************************************************************************/
17  package net.sourceforge.jhunters.pool.data;
18  
19  import java.security.SecureRandom;
20  import java.time.DayOfWeek;
21  import java.time.LocalDate;
22  import java.util.ArrayList;
23  import java.util.Collections;
24  import java.util.HashMap;
25  import java.util.Iterator;
26  import java.util.List;
27  import java.util.Map;
28  
29  import javafx.beans.property.IntegerProperty;
30  import javafx.beans.property.SimpleIntegerProperty;
31  import javafx.collections.ObservableList;
32  import net.sourceforge.jhunters.pool.PoolStatus;
33  
34  /**
35   * Set of fixtures for a particular week.
36   */
37  public class FixtureSet {
38      /**
39       * FixtureSet Name.
40       */
41      protected static final String OBJECT_NAME = DataResource.FIXTURESET_NAME.getValue();
42  
43      /**
44       * WeekNo field.
45       */
46      private static final String FIELD_WEEKNO = DataResource.FIXTURESET_WEEKNO.getValue();
47  
48      /**
49       * List of fixtures.
50       */
51      private final List<Fixture> theFixtures;
52  
53      /**
54       * Week number.
55       */
56      private final IntegerProperty theWeekNo;
57  
58      /**
59       * The status of the set.
60       */
61      private PoolStatus theStatus;
62  
63      /**
64       * Constructor.
65       */
66      protected FixtureSet() {
67          theFixtures = new ArrayList<Fixture>();
68          theWeekNo = new SimpleIntegerProperty(this, FIELD_WEEKNO);
69      }
70  
71      /**
72       * Set named property.
73       * @param pName the name of the property.
74       * @param pValue the value of the property.
75       */
76      protected void setNamedProperty(final String pName,
77                                      final String pValue) {
78          if (pName.equals(FIELD_WEEKNO)) {
79              setWeekNo(DataParser.parseInteger(pValue));
80          }
81      }
82  
83      @Override
84      public String toString() {
85          /* build display string */
86          StringBuilder myBuilder = new StringBuilder();
87  
88          /* Loop through the fixtures */
89          Iterator<Fixture> myIterator = fixtureIterator();
90          while (myIterator.hasNext()) {
91              Fixture myFixture = myIterator.next();
92  
93              myBuilder.append(myFixture.toString());
94              myBuilder.append('\n');
95          }
96          return myBuilder.toString();
97      }
98  
99      /**
100      * Add fixture.
101      * @param pHome the home player.
102      * @param pAway the away player
103      */
104     protected void addFixture(final Player pHome,
105                               final Player pAway) {
106         /* Create the fixture */
107         Fixture myFixture = new Fixture();
108         myFixture.setHomePlayer(pHome);
109         myFixture.setAwayPlayer(pAway);
110 
111         /* Add to the fixture list */
112         addFixture(myFixture);
113     }
114 
115     /**
116      * Add fixture.
117      * @param pFixture the fixture to add.
118      */
119     protected void addFixture(final Fixture pFixture) {
120         theFixtures.add(pFixture);
121     }
122 
123     /**
124      * Obtain weekNo property.
125      * @return the weekNo property
126      */
127     public IntegerProperty weekNoProperty() {
128         return theWeekNo;
129     }
130 
131     /**
132      * Obtain weekNo.
133      * @return the weekNo
134      */
135     public Integer getWeekNo() {
136         return theWeekNo.getValue();
137     }
138 
139     /**
140      * Obtain the status.
141      * @return the status.
142      */
143     public PoolStatus getStatus() {
144         return theStatus;
145     }
146 
147     /**
148      * Set the weekNo.
149      * @param pWeekNo the weekNo
150      */
151     private void setWeekNo(final Integer pWeekNo) {
152         theWeekNo.setValue(pWeekNo);
153     }
154 
155     /**
156      * Obtain fixture list.
157      * @return the fixtures
158      */
159     public List<Fixture> getFixtures() {
160         return theFixtures;
161     }
162 
163     /**
164      * Add fixtures to the list.
165      * @param pList the list to add to
166      * @param pNow the limiting date
167      */
168     protected void getFixtures(final ObservableList<Fixture> pList,
169                                final LocalDate pNow) {
170         /* Loop through the fixtures */
171         Iterator<Fixture> myIterator = fixtureIterator();
172         while (myIterator.hasNext()) {
173             Fixture myCurr = myIterator.next();
174 
175             /* If this is not a bye and date is in the past */
176             if (!myCurr.isBye()
177                 && pNow.compareTo(myCurr.getDate()) >= 0) {
178                 pList.add(myCurr);
179             }
180         }
181     }
182 
183     /**
184      * Obtain fixtureIterator.
185      * @return the iterator
186      */
187     public Iterator<Fixture> fixtureIterator() {
188         return theFixtures.iterator();
189     }
190 
191     /**
192      * Set Bye player.
193      * @param pPlayer the final player
194      */
195     protected void setByePlayer(final Player pPlayer) {
196         /* Loop through the fixtures */
197         Iterator<Fixture> myIterator = fixtureIterator();
198         while (myIterator.hasNext()) {
199             Fixture myCurr = myIterator.next();
200 
201             /* If this is the bye */
202             if (myCurr.isBye()) {
203                 myCurr.setByePlayer(pPlayer);
204                 break;
205             }
206         }
207     }
208 
209     /**
210      * Randomise the fixtures.
211      * @param pRandom the random generator
212      */
213     protected void randomise(final SecureRandom pRandom) {
214         Collections.shuffle(theFixtures, pRandom);
215     }
216 
217     /**
218      * Set the dates of the fixture.
219      * @param pWeekNo the Week Number
220      * @param pDate the base date.
221      */
222     protected void setDates(final Integer pWeekNo,
223                             final LocalDate pDate) {
224         /* Store the week number */
225         setWeekNo(pWeekNo);
226 
227         /* Determine the First Date */
228         LocalDate myDate = pDate.with(DayOfWeek.TUESDAY);
229         int mySplit = theFixtures.size() >> 1;
230 
231         /* Loop through the fixtures */
232         int i = 1;
233         Iterator<Fixture> myIterator = fixtureIterator();
234         while (myIterator.hasNext()) {
235             Fixture myCurr = myIterator.next();
236 
237             /* Set Date and time */
238             myCurr.setDate(myDate);
239 
240             /* If we need to reset */
241             if (i++ == mySplit) {
242                 /* Shift date */
243                 myDate = myDate.plusDays(1);
244             }
245         }
246     }
247 
248     @Override
249     public boolean equals(final Object pThat) {
250         /* Handle trivial cases */
251         if (this == pThat) {
252             return true;
253         }
254         if (pThat == null) {
255             return false;
256         }
257 
258         /* Handle wrong class */
259         if (!(pThat instanceof FixtureSet)) {
260             return false;
261         }
262 
263         /* Access as fixtureSet */
264         FixtureSet myThat = (FixtureSet) pThat;
265 
266         /* Test on weekNo */
267         if (!DataSet.isEqual(getWeekNo(), myThat.getWeekNo())) {
268             return false;
269         }
270 
271         /* Test on fixtures */
272         return theFixtures.equals(myThat.getFixtures());
273     }
274 
275     @Override
276     public int hashCode() {
277         return (theFixtures.hashCode() * DataSet.HASH_PRIME)
278                + theWeekNo.hashCode();
279     }
280 
281     /**
282      * validate the fixtureSet.
283      * @param pParticipants the participants
284      * @return the status
285      */
286     protected PoolStatus validate(final PlayerList pParticipants) {
287         /* Create name map */
288         Map<String, PoolStatus> myMap = new HashMap<String, PoolStatus>();
289         theStatus = PoolStatus.VALID;
290         int myByeCount = 0;
291 
292         /* Loop through the fixtures */
293         Iterator<Fixture> myIterator = fixtureIterator();
294         while (myIterator.hasNext()) {
295             Fixture myCurr = myIterator.next();
296 
297             /* Validate the fixture */
298             PoolStatus myStatus = myCurr.validate(pParticipants);
299             if (!myStatus.isValid()) {
300                 theStatus = PoolStatus.INVALID;
301             } else {
302                 /* Access players */
303                 Player myHome = myCurr.getHomePlayer();
304                 Player myAway = myCurr.getAwayPlayer();
305 
306                 /* Check for preExistence */
307                 if (myHome != null) {
308                     String myName = myHome.getName();
309                     PoolStatus myExisting = myMap.get(myName);
310                     myMap.put(myName, myExisting == null
311                                                          ? PoolStatus.VALID
312                                                          : PoolStatus.DUPLICATE);
313                 }
314                 if (myAway != null) {
315                     String myName = myAway.getName();
316                     PoolStatus myExisting = myMap.get(myName);
317                     myMap.put(myName, myExisting == null
318                                                          ? PoolStatus.VALID
319                                                          : PoolStatus.DUPLICATE);
320                 }
321 
322                 /* increment bye count if required */
323                 if (myCurr.isBye()) {
324                     myByeCount++;
325                 }
326             }
327         }
328 
329         /* If we are valid */
330         if (theStatus.isValid()) {
331             /* Check for bad bye count */
332             boolean badBye = myByeCount > 1;
333 
334             /* Loop through the fixtures again */
335             myIterator = fixtureIterator();
336             while (myIterator.hasNext()) {
337                 Fixture myCurr = myIterator.next();
338 
339                 /* Ignore invalid fixtures */
340                 if (!myCurr.getStatus().isValid()) {
341                     continue;
342                 }
343 
344                 /* Access players */
345                 Player myHome = myCurr.getHomePlayer();
346                 Player myAway = myCurr.getAwayPlayer();
347 
348                 /* Check for duplicate on home player */
349                 if (myHome != null) {
350                     PoolStatus myStatus = myMap.get(myHome.getName());
351                     if (!myStatus.isValid()) {
352                         /* Set as duplicate */
353                         theStatus = PoolStatus.DUPLICATE;
354                         myCurr.setStatus(theStatus);
355                     }
356                 } else if (badBye) {
357                     /* Set as duplicate */
358                     theStatus = PoolStatus.DUPLICATE;
359                     myCurr.setStatus(theStatus);
360                 }
361 
362                 /* Check for duplicate on away player */
363                 if (myAway != null) {
364                     PoolStatus myStatus = myMap.get(myAway.getName());
365                     if (!myStatus.isValid()) {
366                         /* Set as duplicate */
367                         theStatus = PoolStatus.DUPLICATE;
368                         myCurr.setStatus(theStatus);
369                     }
370                 } else if (badBye) {
371                     /* Set as duplicate */
372                     theStatus = PoolStatus.DUPLICATE;
373                     myCurr.setStatus(theStatus);
374                 }
375             }
376         }
377 
378         /* If we are valid */
379         if (theStatus.isValid()) {
380             /* Check expected fixture count */
381             int myNumFixtures = 1 + ((pParticipants.size() - 1) / 2);
382             if (myNumFixtures != theFixtures.size()) {
383                 theStatus = PoolStatus.INVALID;
384             }
385         }
386 
387         /* return the status */
388         return theStatus;
389     }
390 }