package fr.insa.clavardator.client.chat;

import fr.insa.clavardator.client.db.DatabaseController;
import fr.insa.clavardator.client.users.CurrentUser;
import fr.insa.clavardator.client.users.PeerUser;
import fr.insa.clavardator.lib.message.Message;
import fr.insa.clavardator.lib.users.UserInformation;
import fr.insa.clavardator.lib.util.ErrorCallback;
import fr.insa.clavardator.lib.util.Log;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import java.util.ArrayList;
import java.util.Date;

public class ChatHistory {
	private final DatabaseController db;
	private final PeerUser user;
	private final ObservableList<Message> history;
	private HistoryLoadedCallback historyListener;
	private boolean historyLoaded;

	public ChatHistory(PeerUser user) {
		this.user = user;
		db = new DatabaseController();
		history = FXCollections.observableArrayList();
		historyLoaded = false;
	}

	/**
	 * Adds a listener for history loaded event
	 *
	 * @param listener The listener to add
	 */
	public void setHistoryLoadedListener(HistoryLoadedCallback listener) {
		historyListener = listener;
	}

	/**
	 * Notifies all listeners of a history loaded event
	 */
	private void notifyHistoryLoaded() {
		historyListener.onHistoryLoaded(user);
	}

	/**
	 * Loads history from database only if it has not previously been loaded
	 */
	public void load(ErrorCallback errorCallback) {
		if (!historyLoaded) {
			final Date from = new Date();
			// Load whole history
			from.setTime(0);
			db.getChatHistory(new UserInformation(user),
					new UserInformation(CurrentUser.getInstance()),
					from,
					new Date(),
					this::onLoaded,
					errorCallback);
		} else {
			notifyHistoryLoaded();
		}
	}

	/**
	 * Adds all fetched messages to the active history
	 *
	 * @param newHistory The fetched history
	 */
	private void onLoaded(ArrayList<Message> newHistory) {
		historyLoaded = true;
		Platform.runLater(() -> {
			history.addAll(newHistory);
			history.sort((message, t1) -> (int) (message.getDate().getTime() - t1.getDate().getTime()));
		});
		Log.v(getClass().getSimpleName(), "Message history loaded");
		notifyHistoryLoaded();
	}

	public ObservableList<Message> getHistory() {
		return history;
	}

	/**
	 * Saves the given message and adds it to the current history
	 *
	 * @param message The message to add
	 */
	public void addMessage(Message message, ErrorCallback errorCallback) {
		db.addMessage(message, () -> {
			// Add message to active history only if we already fetched it from db
			// This prevents duplicates when loading the db
			if (historyLoaded) {
				Platform.runLater(() -> history.add(message));
			}
		}, errorCallback);
	}

	public interface HistoryLoadedCallback {
		void onHistoryLoaded(PeerUser user);
	}

}
