package com.square.WeakLinkServerSide.logic.game;
import com.square.WeakLinkServerSide.logic.players.Bot;
import com.square.WeakLinkServerSide.logic.players.Player;
import com.square.WeakLinkServerSide.logic.utils.*;
import com.square.WeakLinkServerSide.utils.Preferences;
import com.square.WeakLinkServerSide.utils.Request;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.TreeMap;
public class Game {
//
// Два режима игры: с ботами или с людьми
//
public static final int MODE_BOTS = 0;
public static final int MODE_HUMANS = 1;
//
// Состояния игры:
// NONE - неопределённое состояние
// WAITING - состояние ожидания игроков
// Попадаем сюда из NONE
// Отсюда переходим в STARTED
// STARTED - состояние на протяжении игры
// Попадаем сюда из WAITING
// Отсюда переходим в FINISHED
// FINISHED - состояние после окончания игры
// Попадаем сюда из STARTED
//
public static final int STATUS_NONE = 0;
public static final int STATUS_WAITING = 1;
public static final int STATUS_STARTED = 2;
public static final int STATUS_FINISHED = 3;
private int status = STATUS_NONE;
//
// Текущий этап игры:
// NONE -> BETWEEN -> ROUND -> BETWEEN -> RATE -> BETWEEN -> ADVERT -> BETWEEN -> [NONE -> ...] -> FINAL -> BETWEEN
//
// NONE - этап перед началом раунда
// BETWEEN - этап перехода
// ROUND - этап раунда
// RATE - этап голосования
// ADVERT - этап показа рекламы
// FINAL - этап финального раунда
//
public static final int NOW_NONE = -1;
public static final int NOW_BETWEEN = 0;
public static final int NOW_ROUND = 1;
public static final int NOW_RATE = 2;
public static final int NOW_ADVERT = 3;
public static final int NOW_FINAL = 4;
private int now = NOW_NONE;
//
// Задержки соответствующие фразам между этапами
//
final GameWaiter BEFORE_ROUND_WAITER = new WaitersSet.BeforeRoundWaiter(this);
final GameWaiter AFTER_ROUND_WAITER = new WaitersSet.AfterRoundWaiter(this);
final GameWaiter AFTER_RATE_WAITER = new WaitersSet.AfterRateWaiter(this);
final GameWaiter AFTER_ADVERT_WAITER = new WaitersSet.AfterAdvertWaiter(this);
//
// Все, используемые игрой, задержки
//
final GameWaiter WAIT_PLAYERS_WAITER = new WaitersSet.WaitPlayersWaiter(this);
final GameWaiter BANK_WAITER = new WaitersSet.BankWaiter(this);
final GameWaiter ANSWER_WAITER = new WaitersSet.AnswerWaiter(this);
final GameWaiter CHECK_WAITER = new WaitersSet.CheckWaiter(this);
final GameWaiter QUESTION_WAITER = new WaitersSet.QuestionWaiter(this);
final GameWaiter RATES_WAITER = new WaitersSet.RatesWaiter(this);
final GameWaiter ADVERT_WAITER = new WaitersSet.AdvertWaiter(this);
final GameWaiter SECONDS_COUNTER_WAITER = new WaitersSet.SecondsCounterWaiter(this);
final LinkedList<String> availableBotsNames;
final GameData gameData = new GameData() {
@Override
public void onCommit() {
for (Player p : this.getPlayers()) {
p.onGameDataCommitted(this.getChanges());
}
}
};
final Alarm alarm;
private final int ID;
private final int mode;
public Game(int mode, int id, final Alarm alarm) {
assert mode == MODE_BOTS || mode == MODE_HUMANS : "Wrong mode in game constructor";
ID = id;
this.mode = mode;
this.alarm = alarm;
status = STATUS_WAITING;
gameData.setGameStatus("waiting");
if (mode == Game.MODE_BOTS) {
availableBotsNames = new LinkedList<String>(Preferences.getStringList("Game", "BotsNames"));
} else {
availableBotsNames = new LinkedList<String>(Preferences.getStringList("Game", "HumansBotsNames"));
}
Collections.shuffle(availableBotsNames);
if (mode == MODE_BOTS) {
fillByBots();
start();
} else {
alarm.registerWaiter(WAIT_PLAYERS_WAITER);
}
}
public int getID() {
return ID;
}
public int getMode() {
return mode;
}
public Alarm getAlarm() {
return alarm;
}
private void fillByBots() {
while (!isFully()) {
addPlayer(new Bot(this, availableBotsNames.poll()));
}
}
public boolean isFully() {
System.out.println(gameData.getPlayers().size() + " :: " + gameData.getNumPlayers());
return gameData.getPlayers().size() == gameData.getNumPlayers();
}
public boolean addPlayer(Player player) {
if (gameData.addPlayer(player)) {
gameData.commit();
return true;
}
return false;
}
public boolean removePlayer(Player player) {
return gameData.removePlayer(player);
}
public boolean isFinal() {
return now == NOW_FINAL;
}
void turn() {
gameData.setBankEnabled(true);
alarm.registerWaiter(BANK_WAITER);
alarm.registerWaiter(QUESTION_WAITER);
}
//
// Функции, вызывающие переход в новый этап
//
void start() {
status = STATUS_STARTED;
gameData.setGameStatus("started");
none();
}
void between() {
int prev = now;
now = NOW_BETWEEN;
gameData.setNow("between");
switch (prev) {
//
// Если мы перешли в промежуточный этап из неопределённого, значит следующим этапом будет раунд или финал,
// ставим задержку перед началом раунда (переход к раунду[финалу] прописан в задержке)
//
case NOW_NONE:
gameData.setPhrase("Before Round");
alarm.registerWaiter(BEFORE_ROUND_WAITER);
break;
//
// Если мы перешли в промежуточный этап из этапа раунда, значит следующим этапом будет голосование,
// ставим задержку после окончания раунда (переход к голосованию прописан в задержке)
//
case NOW_ROUND:
gameData.setPhrase("After Round");
alarm.registerWaiter(AFTER_ROUND_WAITER);
break;
//
// Если мы перешли в промежуточный этап из этапа голосования, значит следующим этапом будет реклама,
// ставим задержку после окончания голосования (переход к рекламе прописан в задержке)
//
case NOW_RATE:
gameData.setPhrase("After Rate");
alarm.registerWaiter(AFTER_RATE_WAITER);
break;
//
// Если мы перешли в промежуточный этап из этапа рекламы, значит следующим этапом будет неопределённый,
// ставим задержку после окончания рекламы (переход к неопределённому этапу прописан в задержке)
//
case NOW_ADVERT:
alarm.registerWaiter(AFTER_ADVERT_WAITER);
break;
//
// Если мы перешли в промежуточный этап из этапа финала, значит далее мы должны завершить игру,
// вызываем соответствующий метод
//
case NOW_FINAL:
stop();
break;
default:
assert false : "Wrong now in between";
}
}
void round() {
if (!gameData.isNextFinal()) {
now = NOW_ROUND;
gameData.setNow("round");
Integer currentRound = gameData.getCurrentRound();
gameData.set("currentRound", ++currentRound);
int duration = Preferences.getInt("Game", "TimeRounds", "" + currentRound);
gameData.setRemainingTime(duration);
alarm.registerWaiter(SECONDS_COUNTER_WAITER);
turn();
} else {
now = NOW_FINAL;
gameData.setNow("final");
Integer currentRound = gameData.getCurrentRound();
gameData.set("currentRound", ++currentRound);
// TODO
}
}
void rate() {
now = NOW_RATE;
gameData.setNow("rate");
alarm.registerWaiter(RATES_WAITER);
}
void advert() {
now = NOW_ADVERT;
gameData.setNow("advert");
gameData.commit();//TODO:
alarm.registerWaiter(ADVERT_WAITER);
}
void none() {
now = NOW_NONE;
gameData.setNow("none");
between();
}
void stop() {}
void swapPlayer(Player newPlayer, Player oldPlayer) {
gameData.removePlayer(oldPlayer);
gameData.addPlayer(newPlayer);
}
public void assign(Player p, Request r) {
if (r.code == Preferences.getInt("Requests", "ANSWER")) {
if (ANSWER_WAITER.isRunning() && gameData.getCurrentPlayerID() == p.getID()) {
ANSWER_WAITER.setData(r.data);
ANSWER_WAITER.close();
}
} else if (r.code == Preferences.getInt("Requests", "RATE") && p.isEnabled()) {
if (RATES_WAITER.isRunning()) {
gameData.setVote(p, (Integer) r.data);
if (gameData.isAllRated()) {
RATES_WAITER.close();
}
}
} else if (r.code == Preferences.getInt("Requests", "C_FLUSH_TO_BANK")) {
if (BANK_WAITER.isRunning()) {
gameData.flushToBank();
BANK_WAITER.close();
}
}
}
public boolean isFinished() {
return status == STATUS_FINISHED;
}
public boolean isIn(Player p) {
return gameData.getPlayers().contains(p);
}
}