package network.server;

import java.io.IOException;
import java.net.ServerSocket;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

import game.Game;
import game.Player;
import game.Game.GameState;
import network.protocol.Packet;
import network.protocol.packets.EndGamePacket;
import network.protocol.packets.StartGamePacket;
import sudoku.io.SudokuSerializer;
import sudoku.structure.MultiDoku;

public class Server {

	final ServerSocket serverSocket;
	final List<ServerConnexion> connexions;
	private final ServerAcceptThread acceptThread;
	private final ServerLogicThread logicThread;
	private final Game game;
	private int nextPlayerId = 0;

	public Server(short port) throws IOException {
		this.serverSocket = new ServerSocket(port);
		this.connexions = new ArrayList<>();
		this.acceptThread = new ServerAcceptThread(this);
		this.acceptThread.start();
		this.logicThread = new ServerLogicThread(this);
		this.logicThread.start();
		this.game = new Game();
	}

	public void broadcastPacket(Packet packet) {
		for (ServerConnexion connexion : this.connexions) {
			connexion.sendPacket(packet);
		}
	}

	private void checkTimer() {
		if (getGame() == null || getGame().getGameState() != GameState.GameGoing)
			return;
		long now = Instant.now().getEpochSecond();
		long end = getGame().getStartTime().getEpochSecond() + getGame().getGameDuration();
		if (now > end)
			stopGame();
	}

	private void checkConnexions() {
		for (var it = connexions.iterator(); it.hasNext();) {
			ServerConnexion connexion = it.next();
			if (!connexion.update()) {
				connexion.close();
				connexion.nukeConnection();
				it.remove();
			}
		}
	}

	public void update() {
		checkTimer();
		checkConnexions();
	}

	public void stop() {
		this.acceptThread.cancel();
		this.logicThread.cancel();
		for (ServerConnexion connexion : this.connexions) {
			connexion.nukeConnection();
			connexion.close();
		}
	}

	public Player addPlayer(String pseudo) {
		Player p = new Player(nextPlayerId, pseudo);
		this.game.addPlayer(p);
		nextPlayerId++;
		return p;
	}

	public Game getGame() {
		return game;
	}

	public void startGame(MultiDoku doku, long gameDuration) {
		Instant now = Instant.now();
		this.game.startGame(doku, now, gameDuration);
		for (ServerConnexion connexion : this.connexions) {
			connexion.setSudoku(doku.clone());
		}
		broadcastPacket(new StartGamePacket(SudokuSerializer.serializeSudoku(doku).toString(), now, gameDuration));
	}

	public void stopGame() {
		// we don't need to specify the winner since it has to be the first
		broadcastPacket(new EndGamePacket());
		getGame().stopGame();
	}

}
