package uk.mgrove.ac.soton.comp1206.component;

import javafx.application.Platform;
import javafx.event.Event;
import javafx.geometry.Pos;
import javafx.scene.control.ScrollPane;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import uk.mgrove.ac.soton.comp1206.network.Communicator;
import uk.mgrove.ac.soton.comp1206.util.Multimedia;

public class Chat extends VBox {

    /**
     * Logger
     */
    private static final Logger logger = LogManager.getLogger(Chat.class);

    /**
    * Scroll pane to hold messages
    */
    private final ScrollPane messagesContainer = new ScrollPane();

    /**
     * Container for messages in channel
     */
    private final VBox messages = new VBox();

    /**
     * Text field for messages to send to channels
     */
    private final TextField newMessageText = new TextField();

    /**
     * Button to send message
     */
    private final Text sendMessage = new Text("Send");

    /**
     * The communicator to use
     */
    private final Communicator communicator;

    /**
     * Whether chat should scroll to bottom next time layout updates
     */
    private boolean scrollToBottom;

    /**
     * Whether the last chat message is from the local user
     */
    private boolean lastMessageFromMe;

    /**
     * Initialise the chat container with a communicator
     * @param communicator the communicator to use
     */
    public Chat(Communicator communicator, boolean compact) {
        this.communicator = communicator;

        newMessageText.setPromptText("Type a message...");
        newMessageText.setOnAction(this::sendMessage);

        messages.setSpacing(4);
        messagesContainer.setContent(messages);
        messagesContainer.setFitToWidth(true);
        messagesContainer.setFitToHeight(true);
        messagesContainer.setPrefHeight(320);
        messagesContainer.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
        messagesContainer.setVvalue(1.0);
        messagesContainer.getStyleClass().add("scroller");

        var newMessageContainer = new HBox();
        newMessageContainer.setAlignment(Pos.CENTER_LEFT);
        newMessageContainer.setSpacing(8);
        sendMessage.getStyleClass().add("channelItem");
        sendMessage.setOnMouseClicked(this::sendMessage);

        if (compact) newMessageText.setPrefWidth(140);
        else newMessageText.setPrefWidth(400);
        logger.info("New message text has pref width of: {}", newMessageText.getPrefWidth());
        newMessageContainer.getChildren().addAll(newMessageText, sendMessage);

        getChildren().addAll(messagesContainer, newMessageContainer);

        communicator.addListener((message) -> {
            if (message.matches("^MSG .+:.+$")) {
                var data = message.replaceFirst("MSG ", "").split(":");
                logger.info("Received message: {} from: {}", data[1], data[0]);
                if (messagesContainer.getVvalue() == 1.0f) scrollToBottom = true;

                Platform.runLater(() -> {
                    var user = new Text(data[0] + " ");
                    user.getStyleClass().add("myname");
                    var messageText = new Text(data[1]);
                    var messageContainer = new TextFlow(user, messageText);
                    messages.getChildren().add(messageContainer);
                });

                if (!lastMessageFromMe) Multimedia.playAudio("sounds/message.wav");
                else lastMessageFromMe = false;
            }
        });
    }

    /**
     * Send a message to a channel
     *
     * @param event triggering action event
     */
    private void sendMessage(Event event) {
        if (!newMessageText.getText().isBlank()) {
            if (newMessageText.getText().startsWith("/nick ")) {
                var newNickname = newMessageText.getText().replaceFirst("/nick ", "");
                logger.info("Setting new nickname: {}", newNickname);
                communicator.send("NICK " + newNickname);
            } else {
                logger.info("Sending message: {}", newMessageText.getText());
                communicator.send("MSG " + newMessageText.getText());
                lastMessageFromMe = true;
            }
            newMessageText.clear();
        }
    }

    /**
     * Scroll to bottom of messages list.
     */
    public void scrollToBottom() {
        if (!scrollToBottom) return;
        logger.info("Scrolling to bottom of chat");
        messagesContainer.setVvalue(1.0);
        scrollToBottom = false;
    }

    /**
     * Focus the message input field
     */
    public void focusInputField() {
        newMessageText.requestFocus();
    }

    /**
     * Set whether chat elements should be focus traversable
     * @param traversable whether the elements should be focus traversable
     */
    public void setChatFocusTraversable(boolean traversable) {
        newMessageText.setFocusTraversable(traversable);
        sendMessage.setFocusTraversable(traversable);
    }

    /**
     * Clear the message history
     */
    public void clearMessages() {
        messages.getChildren().clear();
    }

}
