org.sofun.core.kup.KupServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.sofun.core.kup.KupServiceImpl.java

Source

/*
 * Copyright (c)  Sofun Gaming SAS.
 * Copyright (c)  Julien Anguenot <julien@anguenot.org>
 * Copyright (c)  Julien De Preaumont <juliendepreaumont@gmail.com>
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Julien Anguenot <julien@anguenot.org> - initial API and implementation
*/

package org.sofun.core.kup;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sofun.core.CoreConstants;
import org.sofun.core.api.community.table.MemberRankingTableEntry;
import org.sofun.core.api.exception.CoreException;
import org.sofun.core.api.kup.Kup;
import org.sofun.core.api.kup.KupRankingTable;
import org.sofun.core.api.kup.KupRoles;
import org.sofun.core.api.kup.KupSearchResults;
import org.sofun.core.api.kup.KupService;
import org.sofun.core.api.kup.KupStatus;
import org.sofun.core.api.kup.KupType;
import org.sofun.core.api.kup.bet.KupMemberBet;
import org.sofun.core.api.kup.bet.KupWinningsRepartitionRuleType;
import org.sofun.core.api.kup.prediction.KupPredictionPointsRule;
import org.sofun.core.api.local.KupServiceLocal;
import org.sofun.core.api.local.NotificationServiceLocal;
import org.sofun.core.api.local.PredictionServiceLocal;
import org.sofun.core.api.member.Member;
import org.sofun.core.api.member.MemberAccountStatus;
import org.sofun.core.api.member.MemberCredit;
import org.sofun.core.api.member.MemberService;
import org.sofun.core.api.member.MemberTransaction;
import org.sofun.core.api.member.MemberTransactionStatus;
import org.sofun.core.api.member.MemberTransactionType;
import org.sofun.core.api.member.ejb.MemberServiceLocal;
import org.sofun.core.api.notification.NotificationService;
import org.sofun.core.api.prediction.Prediction;
import org.sofun.core.api.prediction.PredictionService;
import org.sofun.core.api.question.Question;
import org.sofun.core.api.question.QuestionKupTiebreaker;
import org.sofun.core.api.remote.KupServiceRemote;
import org.sofun.core.api.sport.SportContestant;
import org.sofun.core.api.sport.tournament.Tournament;
import org.sofun.core.api.sport.tournament.TournamentGame;
import org.sofun.core.api.sport.tournament.TournamentGameStatus;
import org.sofun.core.api.sport.tournament.TournamentRound;
import org.sofun.core.api.sport.tournament.TournamentRoundStatus;
import org.sofun.core.api.sport.tournament.TournamentSeason;
import org.sofun.core.api.sport.tournament.TournamentSeasonStatus;
import org.sofun.core.api.sport.tournament.TournamentStage;
import org.sofun.core.api.sport.tournament.TournamentStageStatus;
import org.sofun.core.api.team.Team;
import org.sofun.core.api.team.TeamPrivacy;
import org.sofun.core.api.team.TeamRoles;
import org.sofun.core.kup.bet.KupMemberBetImpl;
import org.sofun.core.kup.points.rule.GenericRule;
import org.sofun.core.kup.table.KupRankingTableImpl;
import org.sofun.core.member.MemberTransactionImpl;

/**
 * Kup Service Implementation.
 * 
 * @author <a href="mailto:julien@anguenot.org">Julien Anguenot</a>
 * 
 */
@Stateless
@Local(KupServiceLocal.class)
@Remote(KupServiceRemote.class)
public class KupServiceImpl implements KupService {

    private static final long serialVersionUID = 1008880715501446028L;

    private static final Log log = LogFactory.getLog(KupServiceImpl.class);

    @PersistenceContext(unitName = CoreConstants.PERSISTENCE_UNIT)
    private transient EntityManager em;

    @EJB(beanName = "PredictionServiceImpl", beanInterface = PredictionServiceLocal.class)
    private PredictionService predictions;

    @EJB(beanName = "NotificationServiceImpl", beanInterface = NotificationServiceLocal.class)
    private NotificationService notifications;

    @EJB(beanName = "MemberServiceImpl", beanInterface = MemberServiceLocal.class)
    private MemberService members;

    protected static final Map<String, KupPredictionPointsRule> rulePlugins = new HashMap<String, KupPredictionPointsRule>();

    public KupServiceImpl() {
        super();
    }

    public KupServiceImpl(EntityManager em) {
        this();
        this.em = em;
    }

    protected Query createQuery(String queryStr) {
        Query query = em.createQuery(queryStr);
        return query;
    }

    @Override
    public KupSearchResults search(Map<String, String> params) throws CoreException {

        List<Kup> results = new ArrayList<Kup>();
        long count = 0;
        List<KupImpl> kups = null;

        int offset = 0;
        final String offsetStr = params.get("offset");
        if (offsetStr != null) {
            offset = Integer.valueOf(offsetStr);
        }
        int batchSize = 10; // default batch size
        final String batchSizeStr = params.get("batchSize");
        if (batchSizeStr != null) {
            batchSize = Integer.valueOf(batchSizeStr);
        }

        String queryStr = "";
        TypedQuery<KupImpl> query = null;
        Query countQuery = null;

        List<Byte> kupStatus = new ArrayList<Byte>();
        final String kupStatusParam = params.get("status");
        if (kupStatusParam == null) {
            kupStatus = null;
        } else if (kupStatusParam.equals("ALL")) {
            kupStatus = null;
        } else if (kupStatusParam.equals("OPENED")) {
            kupStatus.add(Integer.valueOf(1).byteValue()); // CREATED
        } else if (kupStatusParam.equals("ON_GOING")) {
            kupStatus.add(Integer.valueOf(2).byteValue()); // ON_GOING
        } else if (kupStatusParam.equals("ALL_OPENED")) {
            kupStatus.add(Integer.valueOf(1).byteValue()); // CREATED
            kupStatus.add(Integer.valueOf(2).byteValue()); // ON_GOING
        } else if (kupStatusParam.equals("ALL_CLOSED")) {
            kupStatus.add(Integer.valueOf(3).byteValue()); // CLOSED
            kupStatus.add(Integer.valueOf(4).byteValue()); // SETTLED
            kupStatus.add(Integer.valueOf(5).byteValue()); // PAID OUT
            kupStatus.add(Integer.valueOf(4).byteValue()); // SETTLED
            kupStatus.add(Integer.valueOf(-1).byteValue()); // CANCELED
        }

        final String email = params.get("email");
        if (email != null) {

            Member member = members.getMember(email);
            if (member != null) {

                String template = params.get("template");
                if (template != null && template.equals("all")) {

                    queryStr = "SELECT k FROM " + KupImpl.class.getSimpleName()
                            + " k JOIN k.members m WHERE m.id=:member_id";
                    if (kupStatus != null) {
                        queryStr += " AND k.status IN (:status)";
                    }
                    queryStr += " ORDER BY k.created";

                    query = em.createQuery(queryStr, KupImpl.class);
                    countQuery = em.createQuery(
                            "SELECT count(*) "
                                    + queryStr.substring(queryStr.indexOf("FROM"), queryStr.indexOf("ORDER BY")),
                            Long.class);
                    query.setParameter("member_id", member.getId());
                    countQuery.setParameter("member_id", member.getId());
                    if (kupStatus != null) {
                        query.setParameter("status", kupStatus);
                        countQuery.setParameter("status", kupStatus);
                    }

                } else {

                    queryStr = "SELECT k FROM " + KupImpl.class.getSimpleName()
                            + " k JOIN k.members m WHERE m.id=:member_id AND k.isTemplate=:isTemplate";
                    if (kupStatus != null && kupStatus.size() > 0) {
                        queryStr += " AND k.status IN (:status)";
                    }
                    queryStr += " ORDER BY k.created";

                    query = em.createQuery(queryStr, KupImpl.class);
                    countQuery = em.createQuery(
                            "SELECT count(*) "
                                    + queryStr.substring(queryStr.indexOf("FROM"), queryStr.indexOf("ORDER BY")),
                            Long.class);

                    query.setParameter("member_id", member.getId());
                    countQuery.setParameter("member_id", member.getId());

                    query.setParameter("isTemplate", true);
                    countQuery.setParameter("isTemplate", true);
                    if (kupStatus != null && kupStatus.size() > 0) {
                        query.setParameter("status", kupStatus);
                        countQuery.setParameter("status", kupStatus);
                    }

                }

                // pagination
                query.setFirstResult(offset);
                query.setMaxResults(batchSize);

                kups = query.getResultList();
                count = (Long) countQuery.getSingleResult();
                if (kups != null) {
                    results.addAll(kups);
                }

            }

        } else {

            if (kupStatusParam == null || kupStatusParam.isEmpty()) {
                return null;
            }

            boolean includeRoomKups = false;
            final String withRoomKups = params.get("withRoomKups");
            if (withRoomKups != null && "1".equals(withRoomKups)) {
                includeRoomKups = true;
            }

            if (!includeRoomKups) {
                queryStr = "from " + KupImpl.class.getSimpleName() + " k where k.isTemplate=:isTemplate";
            } else {
                queryStr = "from " + KupImpl.class.getSimpleName() + " k where k.team.privacy IN (:teamPrivacy)";
            }
            if (kupStatus != null) {
                queryStr += " AND k.status IN (:status)";
            }
            final String name = params.get("name");
            if (name != null && !"".equals(name)) {
                queryStr += " AND k.name IN (:name)";
            }

            boolean isTemplateParam = true;
            final String isTemplate = params.get("isTemplate");
            if (isTemplate != null && !"".equals(isTemplate)) {
                if (!isTemplate.equals("1")) {
                    isTemplateParam = false;
                }
            }

            final String kupStakeParam = params.get("stake");
            if (kupStakeParam != null) {
                if ("FREE_FREEROLL".equals(kupStakeParam)) {
                    queryStr += " AND k.stake=0";
                } else if ("FREEROLL".equals(kupStakeParam)) {
                    queryStr += " AND k.stake=0 AND k.type='GAMBLING_FR'";
                } else if ("GAMBLING".equals(kupStakeParam)) {
                    queryStr += " AND k.type='GAMBLING_FR' AND k.stake>0";
                } else if ("FREE".equals(kupStakeParam)) {
                    queryStr += " AND k.type='FREE'";
                } else if ("ALL_GAMBLING".equals(kupStakeParam)) {
                    queryStr += " AND k.type='GAMBLING_FR'";
                } else if (kupStakeParam.isEmpty()) {
                    return null;
                }
            }

            final String kupSportsParam = params.get("sports");
            List<String> sportsNameParams = null;
            if (kupSportsParam != null) {
                if (kupSportsParam.isEmpty()) {
                    return null;
                }
                List<String> sparams = Arrays.asList(kupSportsParam.split("#"));
                if (!sparams.contains("ALL")) {
                    queryStr += " AND UPPER(k.sport.name) IN (:sports)";
                    sportsNameParams = sparams;
                }

            }

            // Remove kups where player is a participant
            final String removeValidatedFor = params.get("removeValidatedfor");
            Member removeValidatedMember = null;
            if (removeValidatedFor != null && !removeValidatedFor.isEmpty()) {
                queryStr += " AND :member NOT MEMBER OF k.participants";
                removeValidatedMember = members.getMember(removeValidatedFor);
            }

            // Do not show up with no participants
            if (kupStatusParam != null && "ALL_CLOSED".equals(kupStatusParam)) {
                queryStr += " AND k.nbParticipants > 0";
            }

            // Sorting
            final String sortParams = params.get("sort");
            if (sortParams != null) {
                if (sortParams.isEmpty()) {
                    return null;
                }
                queryStr += " ORDER BY";
                List<String> sparams = Arrays.asList(sortParams.split("#"));
                boolean initialized = false;
                if (sparams.contains("START_DATE")) {
                    if ("ALL_CLOSED".equals(kupStatusParam)) {
                        queryStr += " k.endDate DESC";
                    } else {
                        queryStr += " k.status ASC, k.startDate ASC";
                    }
                    initialized = true;
                }
                if (sparams.contains("JACKPOT")) {
                    if (initialized) {
                        queryStr += " ,";
                    }
                    queryStr += " k.guaranteedPrice DESC";
                }
                if (sparams.contains("PARTICIPANTS")) {
                    if (initialized) {
                        queryStr += " ,";
                    }
                    queryStr += " k.nbParticipants DESC";
                }
                if (sparams.contains("KUP_DURATION")) {
                    if (initialized) {
                        queryStr += " ,";
                    }
                    queryStr += " k.duration DESC";
                }
            } else {
                if (kupStatusParam != null && "ALL_CLOSED".equals(kupStatusParam)) {
                    queryStr += " ORDER BY k.endDate DESC";
                } else {
                    queryStr += " ORDER BY k.status ASC, k.startDate ASC";
                }
            }

            query = em.createQuery(queryStr, KupImpl.class);
            countQuery = em.createQuery("SELECT count(*) " + queryStr.substring(0, queryStr.indexOf("ORDER BY")),
                    Long.class);

            if (!includeRoomKups) {
                query.setParameter("isTemplate", isTemplateParam);
                countQuery.setParameter("isTemplate", isTemplateParam);
            } else {
                String[] teamPrivacy = new String[] { TeamPrivacy.PUBLIC, TeamPrivacy.PUBLIC_GAMBLING_FR };
                query.setParameter("teamPrivacy", Arrays.asList(teamPrivacy));
                countQuery.setParameter("teamPrivacy", Arrays.asList(teamPrivacy));
            }
            if (kupStatus != null) {
                query.setParameter("status", kupStatus);
                countQuery.setParameter("status", kupStatus);
            }
            if (sportsNameParams != null) {
                query.setParameter("sports", sportsNameParams);
                countQuery.setParameter("sports", sportsNameParams);
            }
            if (name != null && !"".equals(name)) {
                final String[] names = name.split(",");
                query.setParameter("name", Arrays.asList(names));
                countQuery.setParameter("name", Arrays.asList(names));
            }
            if (removeValidatedFor != null && !removeValidatedFor.isEmpty()) {
                query.setParameter("member", removeValidatedMember);
                countQuery.setParameter("member", removeValidatedMember);
            }

            // pagination
            query.setFirstResult(offset);
            query.setMaxResults(batchSize);

            kups = query.getResultList();
            count = (Long) countQuery.getSingleResult();
            if (kups != null) {
                results.addAll(kups);
            }

        }

        return new KupSearchResultsImpl(offset, batchSize, count, results);

    }

    @Override
    public Kup getKupById(long kupId) {

        String queryStr = "from " + KupImpl.class.getSimpleName() + " k where k.id=:kupId";
        Query query = createQuery(queryStr);
        query.setParameter("kupId", kupId);

        try {
            return (Kup) query.getSingleResult();
        } catch (NoResultException nre) {
            return null;
        }

    }

    @Override
    public Map<Integer, Float> getWinningsRepartitionRulesFor(Kup kup) {
        final Map<Integer, Float> rule = new HashMap<Integer, Float>();

        final byte type = kup.getRepartitionRuleType();
        if (type == KupWinningsRepartitionRuleType.TYPE_1) {

            // Type 1: One (1) winner. The {@link Member} ranking #1 in the
            // corresponding {@link KupRankingtable} gets 100% of {@link Kup}
            // jackpot.
            rule.put(1, 100f);

        } else if (type == KupWinningsRepartitionRuleType.TYPE_2) {

            // Type 2: Two (2) winners. The 2 {@link Member} ranking #1 and #2
            // in the corresponding {@link KupRankingtable} ge 50% each of the
            // {@link Kup} jackpot.

            rule.put(1, 70f);
            rule.put(2, 30f);

        } else if (type == KupWinningsRepartitionRuleType.TYPE_3) {

            // Type 3: Three (3) winners. The 3 {@link Member} ranking #1, #2
            // and #3 in the corresponding {@link KupRankingtable} get
            // respectively 70%,
            // 20% and 10% of the {@link Kup} jackpot.

            rule.put(1, 50f);
            rule.put(2, 30f);
            rule.put(3, 20f);

        } else if (type == KupWinningsRepartitionRuleType.TYPE_4) {

            // Type4: Ten (10) winners. The 10 {@link Member} ranking from #1 to
            // #10 in the corresponding {@link KupRankingTable} get the
            // following: 25%, 20%, 15%, 10%, 5%, 5%, 5%, 5%, 5%, 5%

            rule.put(1, 25f);
            rule.put(2, 20f);
            rule.put(3, 15f);
            rule.put(4, 10f);
            rule.put(5, 5f);
            rule.put(6, 5f);
            rule.put(7, 5f);
            rule.put(8, 5f);
            rule.put(9, 5f);
            rule.put(10, 5f);

        } else if (type == KupWinningsRepartitionRuleType.TYPE_5) {

            //
            // Type5: Thirteen (13) winners. The 13 {@link Member} ranking from
            // #1 to #13 in the corresponding {@link KupRankingTable} get the
            // following: 23%, 15%, 13%, 10%, 7%, 4%, 4%, 4%, 4%, 4%, 4%, 4%,
            // 4%,
            //

            rule.put(1, 23f);
            rule.put(2, 15f);
            rule.put(3, 13f);
            rule.put(4, 10f);
            rule.put(5, 7f);
            rule.put(6, 4f);
            rule.put(7, 4f);
            rule.put(8, 4f);
            rule.put(9, 4f);
            rule.put(10, 4f);
            rule.put(11, 4f);
            rule.put(12, 4f);
            rule.put(13, 4f);

        } else if (type == KupWinningsRepartitionRuleType.TYPE_6) {

            //
            // Type6: Twenty (20) winners. The 20 {@link Member} ranking from #1
            // to #20
            // in the corresponding {@link KupRankingTable} get the following:
            // 20%, 12%,
            // 10%, 8%, 5%, 5%, 5%, 5%, 5%, 5%, 2%, 2%, 2%, 2%, 2%, 2%, 2%, 2%,
            // 2%, 2%
            //

            rule.put(1, 20f);
            rule.put(2, 12f);
            rule.put(3, 10f);
            rule.put(4, 8f);
            rule.put(5, 5f);
            rule.put(6, 5f);
            rule.put(7, 5f);
            rule.put(8, 5f);
            rule.put(9, 5f);
            rule.put(10, 5f);
            rule.put(11, 2f);
            rule.put(12, 2f);
            rule.put(13, 2f);
            rule.put(14, 2f);
            rule.put(15, 2f);
            rule.put(16, 2f);
            rule.put(17, 2f);
            rule.put(18, 2f);
            rule.put(19, 2f);
            rule.put(20, 2f);

        } else if (type == KupWinningsRepartitionRuleType.TYPE_55) {

            //
            // Type55: Five (5) winners. The 5 {@link Member} ranking from #1 to
            // #5 in
            // the corresponding {@link KupRankingTable} get the following: 30%,
            // 25%,
            // 20%, 15%, 10%.
            //

            rule.put(1, 30f);
            rule.put(2, 25f);
            rule.put(3, 20f);
            rule.put(4, 15f);
            rule.put(5, 10f);

        } else if (type == KupWinningsRepartitionRuleType.TYPE_30) {

            //
            // Type30: Thirty (30) winners. The 30 {@link Member} ranking from
            // #1 to #30
            // in the corresponding {@link KupRankingTable} get the following:
            // <ul>
            // <li>1er : 15%</li>
            // <li>2e : 10 %</li>
            // <li>3e : 7%</li>
            // <li>4e up to 10e : 4%</li>
            // <li>11e up to 30e : 2%</li>
            // </ul>
            //

            rule.put(1, 15f);
            rule.put(2, 10f);
            rule.put(3, 7f);
            rule.put(4, 4f);
            rule.put(5, 4f);
            rule.put(6, 4f);
            rule.put(7, 4f);
            rule.put(8, 4f);
            rule.put(9, 4f);
            rule.put(10, 4f);
            rule.put(11, 2f);
            rule.put(12, 2f);
            rule.put(13, 2f);
            rule.put(14, 2f);
            rule.put(15, 2f);
            rule.put(16, 2f);
            rule.put(17, 2f);
            rule.put(18, 2f);
            rule.put(19, 2f);
            rule.put(20, 2f);
            rule.put(21, 5f);
            rule.put(22, 2f);
            rule.put(23, 2f);
            rule.put(24, 2f);
            rule.put(25, 2f);
            rule.put(26, 2f);
            rule.put(27, 2f);
            rule.put(28, 2f);
            rule.put(29, 2f);
            rule.put(30, 2f);

        } else {
            log.warn("Kup Winnings Type=" + String.valueOf(type) + " does not exist.");
        }

        return rule;
    }

    @Override
    public void placeKupBet(Member member, Kup kup) throws CoreException {

        //
        // Verify that the member account status.
        //

        final String memberStatus = member.getAccountStatus();
        if (!MemberAccountStatus.getActiveStatus().contains(memberStatus)) {
            throw new CoreException("Member account is not active.");
        }

        //
        // Bets not allowed in Free Kups.
        //

        final String kupType = kup.getType();
        if (KupType.FREE.equals(kupType)) {
            throw new CoreException("Bets not allowed in free kups.");
        }

        //
        // Verify that the member does not have a bet for this kup already.
        //

        if (hasBet(member, kup)) {
            throw new CoreException("Member already placed a bet on this Kup.");
        }

        //
        // Verify the user already has prediction
        //

        if (!hasPrediction(member, kup)) {
            throw new CoreException("Member does not have any predictions in this Kup yet.");
        }

        //
        // Verify that member belongs to the team in which the kup is setup
        //

        /*
         * Team team = kup.getTeam(); if
         * (!member.getMemberTeams().contains(team)) { throw new
         * CoreException("Member does not belong to team."); }
         */

        //
        // Check if kup is opened but not started. This is a Sofun Gaming
        // policy.
        //

        if (kup.getStatus() != KupStatus.OPENED && kup.getStatus() != KupStatus.ON_GOING) {
            throw new CoreException("Kup is not opened for bets.");
        }

        if (KupType.GAMBLING_FR.equals(kupType)) {

            if (!MemberAccountStatus.getActiveGamblingFrStatus().contains(memberStatus)) {
                throw new CoreException("Member not allowed to gamble.");
            }

            final float stake = kup.getStake();
            MemberCredit credit = member.getMemberCredit();
            if (stake > 0 && credit.getCredit() < stake) {
                throw new CoreException("Credit too low to place a bet.");
            }

            // Create transaction
            final Date now = new Date();
            MemberTransaction txn = new MemberTransactionImpl("0", "0", now, kup.getStake(), kup.getStakeCurrency(),
                    MemberTransactionType.BET_DEBIT);
            txn.setDebit(true);
            txn.setStatusCode("00000");
            txn.setStatus(MemberTransactionStatus.INTERNAL);
            member.addTransaction(txn);
            txn.setMember(member);

            // Record
            KupMemberBet bet = new KupMemberBetImpl(member, kup, txn);
            bet.setEffectiveDate(now);
            em.persist(bet);

            if (!kup.getMembers().contains(member)) {
                kup.addMember(member);
            }

            // Validate
            if (hasPrediction(member, kup)) {
                kup.addParticipant(member);
                bet.setEffectiveDate(txn.getDate());
            }

            // Increase Jackpot
            kup.setJackpot(kup.getJackpot() + kup.getStake());

        } else {
            throw new CoreException("Kup type not recognized. Cancelling...");
        }

    }

    @Override
    public boolean hasPrediction(Member member, Kup kup) {
        return predictions.hasPredictions(member, kup);
    }

    @Override
    public boolean hasBet(Member member, Kup kup) throws CoreException {

        if (member == null || kup == null) {
            return false;
        }

        String queryStr = "from " + KupMemberBetImpl.class.getSimpleName()
                + " m where m.member.email=:email AND m.kup.id=:kup_id";

        Query query = createQuery(queryStr);
        query.setParameter("email", member.getEmail());
        query.setParameter("kup_id", kup.getId());

        @SuppressWarnings("unchecked")
        List<Prediction> results = query.getResultList();
        return results.size() > 0 ? true : false;
    }

    protected void checkForPredicton(Member member, Kup kup) throws CoreException {

        //
        // Verify that the member account status.
        //

        final String memberStatus = member.getAccountStatus();
        if (!MemberAccountStatus.getActiveStatus().contains(memberStatus)) {
            throw new CoreException("Member account is not active.");
        }

        //
        // Verify that member belongs to the team in which the kup is setup
        //

        Team team = kup.getTeam();
        if (!team.getMembers().contains(member)) {
            throw new CoreException("Member does not belong to team.");
        }

        //
        // Check if kup is active.
        //

        if (!(kup.getStatus() == KupStatus.OPENED || kup.getStatus() == KupStatus.ON_GOING)) {
            throw new CoreException("Kup is not opened for predictions.");
        }
    }

    @Override
    public void addPrediction(String type, Member member, Kup kup, TournamentGame game,
            List<SportContestant> contestants) throws CoreException {

        if (predictions.isPredictionAllowedOn(game)) {
            predictions.createPredictionFor(member, game, type, contestants, kup);
        } else {
            throw new CoreException("Prediction not allowed on game with uuid=" + game.getUUID());
        }

    }

    @Override
    public void addPredictionScore(String type, Member member, Kup kup, TournamentGame game, List<Integer> score)
            throws CoreException {

        if (predictions.isPredictionAllowedOn(game)) {
            predictions.createPredictionScoreFor(member, game, type, score, kup);
        } else {
            throw new CoreException("Prediction not allowed on game with uuid=" + game.getUUID());
        }

    }

    @Override
    public void addGameQuestionPrediction(String type, Member member, Kup kup, TournamentGame game,
            Question question, String choice) throws CoreException {

        if (predictions.isPredictionAllowedOn(game)) {
            predictions.createQuestionPredictionFor(member, game, type, question, kup, choice);
        } else {
            throw new CoreException("Prediction not allowed on game with uuid=" + game.getUUID());
        }

    }

    @Override
    public void addPrediction(String type, Member member, Kup kup, TournamentRound round,
            List<SportContestant> contestants) throws CoreException {

        if (predictions.isPredictionAllowedOn(round)) {
            predictions.createPredictionFor(member, round, type, contestants, kup);
        } else {
            throw new CoreException("Prediction not allowed on game with uuid=" + round.getUUID());
        }

    }

    @Override
    public void addPrediction(String type, Member member, Kup kup, TournamentStage stage,
            List<SportContestant> contestants) throws CoreException {

        if (predictions.isPredictionAllowedOn(stage)) {
            predictions.createPredictionFor(member, stage, type, contestants, kup);
        } else {
            throw new CoreException("Prediction not allowed on game with uuid=" + stage.getUUID());
        }

    }

    @Override
    public void addPrediction(String type, Member member, Kup kup, TournamentSeason season,
            List<SportContestant> contestants) throws CoreException {

        if (predictions.isPredictionAllowedOn(season)) {
            predictions.createPredictionFor(member, season, type, contestants, kup);
        } else {
            throw new CoreException("Prediction not allowed on game with uuid=" + season.getUUID());
        }

    }

    private static final List<Byte> toByteList(byte[] a) {
        final List<Byte> r = new ArrayList<Byte>();
        for (int i = 0; i < a.length; i++) {
            r.add(Byte.valueOf(a[i]));
        }
        return r;
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Kup> getKupsByStatus(byte[] status, String[] types) throws CoreException {

        final List<Byte> statusParm = toByteList(status);

        String queryStr = "from " + KupImpl.class.getSimpleName() + " k where k.status IN (:status)";

        if (types != null) {
            queryStr += " AND k.type IN (:types)";
        }

        Query query = createQuery(queryStr);
        query.setParameter("status", statusParm);
        if (types != null) {
            query.setParameter("types", Arrays.asList(types));
        }

        try {
            return query.getResultList();
        } catch (NoResultException nre) {
            return new ArrayList<Kup>();
        }

    }

    @Override
    public List<Kup> getActiveKups() throws CoreException {
        final byte[] status = KupStatus.getActiveStatus();
        return getKupsByStatus(status, null);
    }

    @Override
    public int getPointsPredictionFor(Kup kup, Prediction prediction) throws CoreException {
        // final KupPredictionPointsRule rule =
        // rulePlugins.get(kup.getMetaType());
        // FIXME use kup meta type to deduce points computing rules.
        KupPredictionPointsRule rule = new GenericRule();
        return rule.getPointsFor(kup, prediction, this);
    }

    @Override
    public void cancelKup(Kup kup) {

        final Date now = new Date();

        kup.setStatus(KupStatus.CANCELED);

        final float stake = kup.getStake();
        // Free roll nothing to reimbursed
        if (stake == 0) {
            return;
        }

        for (Member member : kup.getParticipants()) {
            MemberTransaction txn = new MemberTransactionImpl(now, stake, kup.getStakeCurrency(),
                    MemberTransactionType.BET_CREDIT);
            txn.setLabel(MemberTransactionType.BET_CREDIT);
            txn.setCredit(true);
            txn.setStatusCode("00000");
            txn.setStatus(MemberTransactionStatus.INTERNAL);
            member.addTransaction(txn);
            txn.setMember(member);
        }

    }

    @Override
    public Set<String> getCredentials(Kup kup, Member member) {
        Set<String> credentials = new HashSet<String>();

        if (kup.isAdmin(member)) {
            credentials.add(KupRoles.ADMINISTRATOR);
        }

        if (kup.isMember(member)) {
            credentials.add(KupRoles.MEMBER);
        }

        if (kup.isParticipant(member)) {
            credentials.add(KupRoles.PARTICIPANT);
        }

        if (credentials.size() == 0) {
            credentials.add(TeamRoles.ANONYMOUS);
        }

        return credentials;
    }

    @Override
    public Kup createKupFromMeta(Kup kup, Team team, float stake, byte repartitionType) throws CoreException {

        Kup newKup = new KupImpl(kup, team, stake, repartitionType);
        team.addKup(newKup);
        em.persist(newKup);
        return newKup;
    }

    @Override
    public List<Prediction> getPredictionsFor(Member member, Kup kup) throws CoreException {
        return predictions.getPredictionsFor(kup, member);
    }

    @SuppressWarnings("unchecked")
    @Override
    public List<Kup> getKupsByName(String name) {

        String queryStr = "from " + KupImpl.class.getSimpleName() + " k where k.name=:name";
        Query query = createQuery(queryStr);
        query.setParameter("name", name);

        try {
            return query.getResultList();
        } catch (NoResultException nre) {
            return new ArrayList<Kup>();
        }

    }

    @Override
    public Kup getTemplateFor(Kup kup) {
        if (kup == null) {
            return null;
        }
        if (kup.isTemplate()) {
            return null;
        }
        String queryStr = "from " + KupImpl.class.getSimpleName()
                + " k where k.name=:kupName AND k.isTemplate=:isTemplate";
        Query query = createQuery(queryStr);
        query.setParameter("kupName", kup.getName());
        query.setParameter("isTemplate", true);
        try {
            return (Kup) query.getSingleResult();
        } catch (NoResultException nre) {
            return null;
        }
    }

    @Override
    public void syncKupsWithTemplate() throws CoreException {
        for (Kup kup : getActiveKups()) {

            // Update start date / end date (depends on events)
            kup.setStartDate(kup.getEffectiveStartDate());
            kup.setEndDate(kup.getEffectiveEndDate());
            // Update Kup search indexes.
            kup.setNbParticipants(kup.getParticipants().size());
            // Update Kup duration search index
            kup.setDuration(kup.computeDuration());

            Kup template = getTemplateFor(kup);
            if (template == null) {
                continue;
            }

            // Set sport
            kup.setSport(template.getSport());
            for (Tournament tournament : template.getSportTournaments()) {
                kup.addSportTournament(tournament);
            }

            log.debug("Syncing Kup w/ id=" + String.valueOf(kup.getId()) + " and template w/ name="
                    + template.getName());

            for (TournamentGame game : template.getBettableGames()) {
                if (TournamentGameStatus.SCHEDULED.equals(game.getGameStatus())
                        && !kup.getBettableGames().contains(game)) {
                    log.info("Adding game with uuid=" + game.getUUID() + " to kup with id=" + kup.getId());
                    kup.addBettableGame(game);
                }
            }
            for (TournamentRound round : template.getBettableRounds()) {
                if (TournamentRoundStatus.SCHEDULED.equals(round.getStatus())
                        && !kup.getBettableRounds().contains(round)) {
                    log.info("Adding round with uuid=" + round.getUUID() + " to kup with id=" + kup.getId());
                    kup.addBettableRound(round);
                }
            }
            for (TournamentStage stage : template.getBettableStages()) {
                if (TournamentStageStatus.SCHEDULED.equals(stage.getStatus())
                        && !kup.getBettableStages().contains(stage)) {
                    log.info("Adding stage with uuid=" + stage.getUUID() + " to kup with id=" + kup.getId());
                    kup.addBettableStage(stage);
                }
            }
            for (TournamentSeason season : template.getBettableTournaments()) {
                if (TournamentSeasonStatus.SCHEDULED.equals(season.getStatus())
                        && !kup.getBettableTournaments().contains(season)) {
                    log.info("Adding season with uuid=" + season.getUUID() + " to kup with id=" + kup.getId());
                    kup.addBettableTournament(season);
                }
            }
            for (Question question : template.getBettableQuestion()) {
                if (!kup.getBettableQuestion().contains(question)) {
                    log.info("Adding question with uuid=" + question.getId() + " to kup with id=" + kup.getId());
                    kup.addBettableQuestion(question);
                }
            }
            for (QuestionKupTiebreaker question : template.getQuestionsTiebreaker()) {
                if (!kup.getQuestionsTiebreaker().contains(question)) {
                    log.info("Adding question tie breaker with uuid=" + question.getId() + " to kup with id="
                            + kup.getId());
                    kup.addQuestionTiebreaker(question);
                }
            }

        }

    }

    @Override
    public void syncTemplates() throws CoreException {

        // We only interested in opened or to be opened kups.
        final List<Byte> kupStatus = new ArrayList<Byte>();
        kupStatus.add(Integer.valueOf(0).byteValue()); // CREATED
        kupStatus.add(Integer.valueOf(1).byteValue()); // OPENED
        kupStatus.add(Integer.valueOf(2).byteValue()); // ON_GOING

        final String queryStr = "from " + KupImpl.class.getSimpleName()
                + " k where k.isTemplate=:isTemplate AND k.status IN (:status) AND k.isFinal=:isFinal";

        final Query query = createQuery(queryStr);
        query.setParameter("isTemplate", true);
        query.setParameter("isFinal", false); // Only non final Kups should be
                                              // upgraded
        query.setParameter("status", kupStatus);

        // Code below will be applied only to non-final Kup's templates.
        @SuppressWarnings("unchecked")
        final List<Kup> kups = query.getResultList();
        for (Kup kup : kups) {
            // Check Round's games
            for (TournamentRound round : kup.getBettableRounds()) {
                for (TournamentGame game : round.getGames()) {
                    if (!kup.getBettableGames().contains(game)) {
                        kup.addBettableGame(game);
                        log.info("Adding game with uuid=" + game.getUUID() + " to kup with id=" + kup.getId());
                    }
                }
            }
            // Check Round's stages
            for (TournamentStage stage : kup.getBettableStages()) {
                for (TournamentRound round : stage.getRounds()) {
                    if (!kup.getBettableRounds().contains(round)) {
                        kup.addBettableRound(round);
                        log.info("Adding round with uuid=" + round.getUUID() + " to kup with id=" + kup.getId());
                    }
                }
            }
            // Check Stage's seasons
            for (TournamentSeason season : kup.getBettableTournaments()) {
                for (TournamentStage stage : season.getStages()) {
                    if (!kup.getBettableStages().contains(stage)) {
                        kup.addBettableStage(stage);
                        log.info("Adding stage with uuid=" + stage.getUUID() + " to kup with id=" + kup.getId());
                    }
                }
            }
            // Update start / end date (depends on events)
            kup.setStartDate(kup.getEffectiveStartDate());
            kup.setEndDate(kup.getEffectiveEndDate());
            // Update Kup search indexes.
            kup.setNbParticipants(kup.getParticipants().size());
            // Update Kup duration search index
            kup.setDuration(kup.computeDuration());
            // Set Sports if not set
            // Lazy init.
            kup.getSport();
        }

    }

    @Override
    public Date getPredictionsLastModifiedFor(Member member, Kup kup) throws CoreException {
        return predictions.getPredictionsLastModifiedFor(member, kup);
    }

    @Override
    public void invite(Kup kup, Member inviter, Map<String, String> params) throws CoreException {
        params.put("templateId", "kup-invite_fr");
        params.put("format", "text/plain");
        notifications.sendEmail(inviter, params);
    }

    @Override
    public int countCorrectPredictionsFor(Kup kup, Member member) throws CoreException {
        int nb = 0;
        for (Prediction p : predictions.getPredictionsFor(kup, member)) {
            if (p.getPoints() > 0) {
                nb += 1;
            }
        }
        // Lazy update
        MemberRankingTableEntry entry = kup.getRankingTable().getEntryForMember(member);
        if (entry != null && (entry.getCorrectPredictions() == null || entry.getCorrectPredictions() != nb)) {
            entry.setCorrectPredictions(nb);
        }
        return nb;
    }

    @Override
    public KupRankingTable getKupRanking(Kup kup, int offset, int batchSize) throws CoreException {
        return kup.getRankingTable();
    }

    @Override
    public Date getFirstPredictionDateFor(Member member, Kup kup) throws CoreException {
        Date earliest = null;
        for (Prediction p : predictions.getPredictionsFor(kup, member)) {
            if (earliest == null) {
                earliest = p.getCreated();
            } else if (p.getCreated().compareTo(earliest) < 0) {
                earliest = p.getCreated();
            }
        }
        // Lazy update
        MemberRankingTableEntry entry = kup.getRankingTable().getEntryForMember(member);
        if (entry != null && entry.getFirstPredictions() != null && earliest != null) {
            entry.setFirstPredictions(earliest);
        }
        return earliest;
    }

    @Override
    public void updateKupStats(Kup kup) throws CoreException {
        if (kup == null) {
            return;
        }
        log.info("Updating statistics of Kup w/ uuid=" + kup.getId());
        KupRankingTable table = kup.getRankingTable();
        for (Iterator<MemberRankingTableEntry> i = table.getEntries().iterator(); i.hasNext();) {
            MemberRankingTableEntry entry = i.next();
            entry.setCorrectPredictions(countCorrectPredictionsFor(kup, entry.getMember()));
            if (entry.getFirstPredictions() == null) {
                entry.setFirstPredictions(getFirstPredictionDateFor(entry.getMember(), kup));
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Override
    public void updateKupsStats() throws CoreException {

        // Only kup ranking tables not marked as final should be recomputed.
        final String queryStr = "from " + KupRankingTableImpl.class.getSimpleName() + " k where k.fin=:final";
        final Query query = createQuery(queryStr);
        query.setParameter("final", false);

        List<KupRankingTable> tables;
        try {
            tables = query.getResultList();
        } catch (NoResultException nre) {
            // This is case should never be happening unless the shop is
            // closed...
            tables = new ArrayList<KupRankingTable>();
        }
        for (Iterator<KupRankingTable> i = tables.listIterator(); i.hasNext();) {
            KupRankingTable table = i.next();
            Kup kup = table.getKup();
            updateKupStats(kup);
            byte kupStatus = kup.getStatus();
            if (KupType.FREE.equals(kup.getType())) {
                if (KupStatus.CANCELED == kupStatus || KupStatus.SETTLED == kupStatus) {
                    table.setFinal(true);
                }
            } else {
                if (KupStatus.CANCELED == kupStatus || KupStatus.PAID_OUT == kupStatus) {
                    table.setFinal(true);
                }
            }
        }

    }

    @Override
    public void addQuestionKupTiebreaker(Member member, Kup kup, QuestionKupTiebreaker question, String choice)
            throws CoreException {

        if (predictions.isPredictionAllowedOn(kup)) {
            predictions.createQuestionTiebreakerPredictionFor(member, question, kup, choice);
        } else {
            throw new CoreException("Prediction not allowed on Kup with uuid=" + kup.getId());
        }

    }

    @Override
    public Kup getKupByTransactionId(long txnId) {

        String queryStr = "from " + KupMemberBetImpl.class.getSimpleName() + " b where b.transaction.id=:txnId";
        Query query = createQuery(queryStr);
        query.setParameter("txnId", txnId);

        KupMemberBet bet;
        try {
            bet = (KupMemberBet) query.getSingleResult();
        } catch (NoResultException nre) {
            bet = null;
        }

        Kup kup = null;
        if (bet != null) {
            kup = bet.getKup();
        }
        return kup;

    }

}