package legion.muyue.best2048;

// Imports Android standard et UI
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.View;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.content.ContextCompat;
import androidx.gridlayout.widget.GridLayout;

// Imports Retrofit (pour les appels REST initiaux)
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response; // <--- IMPORT IMPORTANT POUR RETROFIT CALLBACK !

// Imports OkHttp (pour WebSocket)
import okhttp3.OkHttpClient;
import okhttp3.Request;
// import okhttp3.Response; // Aussi nécessaire pour WebSocketListener.onFailure, garder les deux imports
import okhttp3.WebSocket;
import okhttp3.WebSocketListener;
import okio.ByteString;

// Imports Gson (pour JSON)
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;

// Import Timeout pour OkHttpClient
import java.util.concurrent.TimeUnit;

// Imports des classes Data et Network (existantes)
import legion.muyue.best2048.data.GameInfo;
import legion.muyue.best2048.data.GameStateResponse;
import legion.muyue.best2048.data.MoveRequest; // Utilisé pour construire le JSON du move via WebSocket
import legion.muyue.best2048.data.PlayerIdRequest; // Pour createOrJoinGame
import legion.muyue.best2048.network.ApiClient;
import legion.muyue.best2048.network.ApiService;


public class MultiplayerActivity extends AppCompatActivity {

    private static final String TAG = "MultiplayerActivity";
    private static final int BOARD_SIZE = 4;

    // UI
    private GridLayout boardGridLayoutMulti;
    private TextView myScoreLabelMulti;
    private TextView opponentScoreLabelMulti;
    private TextView turnIndicatorMulti;
    private TextView statusTextMulti;
    private ProgressBar loadingIndicatorMulti;
    private TextView[][] tileViewsMulti = new TextView[BOARD_SIZE][BOARD_SIZE];

    // Network - REST (pour appels initiaux)
    private ApiService apiService;

    // Network - WebSocket (pour temps réel)
    private OkHttpClient wsClient;
    private WebSocket webSocket;
    private MyWebSocketListener webSocketListener;
    private static final String WEBSOCKET_URL = "wss://best2048.legion-muyue.fr/ws";
    private boolean isWebSocketConnected = false;

    // Game State
    private String currentGameId = null;
    private String myPlayerId = null;
    private String opponentPlayerId = null;
    private GameStateResponse currentGameState = null;

    // JSON Parser
    private Gson gson;

    // Handler pour UI Thread
    private Handler uiHandler;

    // --- Activity Lifecycle ---

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_multiplayer);
        Log.d(TAG, "onCreate");

        findViewsMulti();
        apiService = ApiClient.getApiService(); // Instance Retrofit
        wsClient = new OkHttpClient.Builder()
                .pingInterval(30, TimeUnit.SECONDS) // Maintenir connexion WS active
                .build();
        webSocketListener = new MyWebSocketListener();
        gson = new Gson();
        uiHandler = new Handler(Looper.getMainLooper());

        setupSwipeListenerMulti();
        initMultiplayerGame(); // Démarrer le processus
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause");
        closeWebSocket(); // Fermer WS en quittant l'écran
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume");
        // Tenter de reconnecter si besoin
        if (currentGameId != null && !isWebSocketConnected && currentGameState != null && !currentGameState.isGameOver()) {
            Log.i(TAG, "Tentative de reconnexion WebSocket...");
            connectWebSocket();
        } else if (currentGameId != null && isWebSocketConnected) {
            Log.d(TAG, "WebSocket déjà connecté, rafraîchissement état via REST...");
            fetchGameState(); // Resynchroniser via REST au cas où
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy");
        closeWebSocket(); // Assurer la fermeture
        if (uiHandler != null) {
            uiHandler.removeCallbacksAndMessages(null);
        }
    }

    // --- UI Setup ---
    private void findViewsMulti() {
        boardGridLayoutMulti = findViewById(R.id.gameBoardMulti);
        myScoreLabelMulti = findViewById(R.id.myScoreLabelMulti);
        opponentScoreLabelMulti = findViewById(R.id.opponentScoreLabelMulti);
        turnIndicatorMulti = findViewById(R.id.turnIndicatorMulti);
        statusTextMulti = findViewById(R.id.multiplayerStatusText);
        loadingIndicatorMulti = findViewById(R.id.loadingIndicatorMulti);
    }

    // --- Game Initialization & REST Call ---

    /** Lance l'appel REST pour créer ou rejoindre une partie. */
    private void initMultiplayerGame() {
        showLoading(true);
        statusTextMulti.setText(R.string.multiplayer_status_searching);

        // Générer/Récupérer Player ID (UUID temporaire pour test)
        myPlayerId = java.util.UUID.randomUUID().toString();
        Log.i(TAG, "initMultiplayerGame: Utilisation du Player ID: " + myPlayerId);

        PlayerIdRequest requestBody = new PlayerIdRequest(myPlayerId);

        // Appel Retrofit
        apiService.createOrJoinGame(requestBody).enqueue(new Callback<GameInfo>() {
            @Override
            // Utilise bien retrofit2.Response ici
            public void onResponse(@NonNull Call<GameInfo> call, @NonNull Response<GameInfo> response) {
                if (response.isSuccessful() && response.body() != null) {
                    GameInfo gameInfo = response.body();
                    currentGameId = gameInfo.getGameId();

                    // Détermine opponentPlayerId
                    if (myPlayerId.equals(gameInfo.getPlayer1Id())) {
                        opponentPlayerId = gameInfo.getPlayer2Id();
                    } else {
                        opponentPlayerId = gameInfo.getPlayer1Id();
                    }

                    Log.i(TAG, "Partie rejointe/créée via REST: ID=" + currentGameId + ", Moi=" + myPlayerId + ", Adversaire=" + opponentPlayerId);
                    statusTextMulti.setText(getString(R.string.multiplayer_status_found, currentGameId.substring(0, 8)));

                    // Succès de l'appel REST -> On lance la connexion WebSocket
                    connectWebSocket();

                } else {
                    Log.e(TAG, "Erreur création/rejoindre partie: " + response.code());
                    handleNetworkError(getString(R.string.error_join_create_game, response.code()));
                    showLoading(false);
                }
            }

            @Override
            public void onFailure(@NonNull Call<GameInfo> call, @NonNull Throwable t) {
                Log.e(TAG, "Échec création/rejoindre partie", t);
                handleNetworkError(getString(R.string.error_server_connection));
                showLoading(false);
            }
        });
    }

    /** Récupère l'état actuel via REST (pour synchro). */
    private void fetchGameState() {
        if (currentGameId == null) return;
        Log.d(TAG, "fetchGameState via REST pour Game ID: " + currentGameId);

        apiService.getGameState(currentGameId).enqueue(new Callback<GameStateResponse>() {
            @Override
            // Utilise bien retrofit2.Response ici
            public void onResponse(@NonNull Call<GameStateResponse> call, @NonNull Response<GameStateResponse> response) {
                if (response.isSuccessful() && response.body() != null) {
                    Log.d(TAG, "État du jeu récupéré via REST.");
                    currentGameState = response.body();
                    // Mettre à jour l'UI sur le thread principal
                    runOnUiThread(() -> updateMultiplayerUI(currentGameState));
                } else {
                    Log.e(TAG, "Erreur récupération état partie via REST: " + response.code());
                }
            }

            @Override
            public void onFailure(@NonNull Call<GameStateResponse> call, @NonNull Throwable t) {
                Log.e(TAG, "Échec récupération état partie via REST", t);
            }
        });
    }

    // --- WebSocket Management ---

    /** Initialise et lance la connexion WebSocket. */
    private void connectWebSocket() {
        if (currentGameId == null || myPlayerId == null) {
            Log.e(TAG, "connectWebSocket: Impossible de connecter, gameId ou playerId est null.");
            return;
        }
        if (webSocket != null) {
            Log.w(TAG, "connectWebSocket: Tentative de connexion alors qu'un WebSocket existe déjà. Fermeture de l'ancien.");
            closeWebSocket();
        }

        Log.i(TAG, "Tentative de connexion WebSocket à: " + WEBSOCKET_URL);
        statusTextMulti.setText(R.string.multiplayer_status_connecting);
        showLoading(true);

        Request request = new Request.Builder().url(WEBSOCKET_URL).build();
        webSocket = wsClient.newWebSocket(request, webSocketListener); // Lance la connexion
    }

    /** Ferme la connexion WebSocket proprement. */
    private void closeWebSocket() {
        if (webSocket != null) {
            Log.i(TAG, "Fermeture de la connexion WebSocket.");
            webSocket.close(1000, "Activity closed");
            webSocket = null;
            isWebSocketConnected = false;
        }
    }

    /** Méthode pour envoyer un message texte via le WebSocket. */
    private void sendWebSocketMessage(String jsonMessage) {
        if (webSocket != null && isWebSocketConnected) {
            Log.d(TAG, "Envoi WS message: " + jsonMessage);
            webSocket.send(jsonMessage);
        } else {
            Log.e(TAG, "sendWebSocketMessage: Tentative d'envoi alors que le WebSocket est fermé ou non connecté.");
            Toast.makeText(this, R.string.error_websocket_disconnected, Toast.LENGTH_SHORT).show();
            // On pourrait tenter une reconnexion ici si désiré
            // connectWebSocket();
        }
    }


    // --- WebSocket Listener Implementation ---
    private class MyWebSocketListener extends WebSocketListener {

        @Override
        public void onOpen(@NonNull WebSocket ws, @NonNull okhttp3.Response response) { // okhttp3.Response ici !
            super.onOpen(ws, response);
            isWebSocketConnected = true;
            Log.i(TAG, "WebSocket onOpen: Connexion établie !");

            // Envoie le message d'enregistrement
            RegisterMessage registerMsg = new RegisterMessage("register", myPlayerId, currentGameId);
            String registerJson = gson.toJson(registerMsg);
            ws.send(registerJson); // Envoie l'enregistrement

            // Met à jour l'UI sur le thread principal
            runOnUiThread(() -> {
                showLoading(false);
                statusTextMulti.setText(R.string.multiplayer_status_waiting_state);
                Toast.makeText(MultiplayerActivity.this, R.string.websocket_connected, Toast.LENGTH_SHORT).show();
            });
        }

        @Override
        public void onMessage(@NonNull WebSocket ws, @NonNull String text) {
            super.onMessage(ws, text);
            Log.d(TAG, "WebSocket onMessage: Reçu texte: " + text);

            try {
                BaseMessage baseMessage = gson.fromJson(text, BaseMessage.class);

                if ("gameStateUpdate".equals(baseMessage.type)) {
                    currentGameState = gson.fromJson(text, GameStateResponse.class);
                    Log.i(TAG, "WebSocket onMessage: GameStateUpdate reçu. Tour: " + currentGameState.getCurrentPlayerId());
                    runOnUiThread(() -> {
                        showLoading(false);
                        updateMultiplayerUI(currentGameState);
                        if (currentGameState.isGameOver()) {
                            handleGameOverUI(currentGameState);
                        }
                    });
                } else if ("error".equals(baseMessage.type)) {
                    ErrorMessage errorMsg = gson.fromJson(text, ErrorMessage.class);
                    Log.e(TAG, "WebSocket onMessage: Erreur serveur reçue: " + errorMsg.message);
                    runOnUiThread(() -> {
                        Toast.makeText(MultiplayerActivity.this, getString(R.string.server_error_prefix, errorMsg.message), Toast.LENGTH_LONG).show();
                        statusTextMulti.setText(getString(R.string.server_error_prefix, errorMsg.message));
                    });
                } else if ("info".equals(baseMessage.type)) {
                    InfoMessage infoMsg = gson.fromJson(text, InfoMessage.class);
                    Log.i(TAG, "WebSocket onMessage: Info serveur reçue: " + infoMsg.message);
                    runOnUiThread(() -> Toast.makeText(MultiplayerActivity.this, "Info: " + infoMsg.message, Toast.LENGTH_SHORT).show());
                } else {
                    Log.w(TAG, "WebSocket onMessage: Type de message inconnu reçu: " + baseMessage.type);
                }

            } catch (JsonSyntaxException e) {
                Log.e(TAG, "WebSocket onMessage: Erreur parsing JSON", e);
            }
        }

        @Override
        public void onMessage(@NonNull WebSocket ws, @NonNull ByteString bytes) {
            super.onMessage(ws, bytes);
            Log.w(TAG, "WebSocket onMessage: Reçu message binaire (non géré)");
        }

        @Override
        public void onClosing(@NonNull WebSocket ws, int code, @NonNull String reason) {
            super.onClosing(ws, code, reason);
            Log.i(TAG, "WebSocket onClosing: Fermeture demandée par le serveur. Code=" + code + ", Raison=" + reason);
            isWebSocketConnected = false;
            ws.close(1000, null);
            runOnUiThread(() -> {
                statusTextMulti.setText(getString(R.string.websocket_closing));
                showLoading(false);
            });
        }

        @Override
        public void onClosed(@NonNull WebSocket ws, int code, @NonNull String reason) {
            super.onClosed(ws, code, reason);
            Log.i(TAG, "WebSocket onClosed: Connexion fermée. Code=" + code + ", Raison=" + reason);
            isWebSocketConnected = false;
            webSocket = null;
            runOnUiThread(() -> {
                statusTextMulti.setText(getString(R.string.websocket_closed));
                showLoading(false);
                // Gérer la reconnexion si nécessaire
            });
        }

        @Override
        public void onFailure(@NonNull WebSocket ws, @NonNull Throwable t, @Nullable okhttp3.Response response) { // okhttp3.Response ici !
            super.onFailure(ws, t, response);
            Log.e(TAG, "WebSocket onFailure: Erreur de connexion.", t);
            isWebSocketConnected = false;
            webSocket = null;
            runOnUiThread(() -> {
                handleNetworkError(getString(R.string.error_websocket_connection));
                showLoading(false);
                // Gérer la reconnexion si nécessaire
            });
        }
    }

    // --- Data Classes internes pour WebSocket Messages ---
    private static class BaseMessage { String type; }
    private static class ErrorMessage extends BaseMessage { String message; }
    private static class InfoMessage extends BaseMessage { String message; }
    private static class RegisterMessage extends BaseMessage { String playerId; String gameId;
        RegisterMessage(String type, String pId, String gId){ this.type=type; this.playerId=pId; this.gameId=gId; }
    }
    private static class MoveMessage extends BaseMessage { String direction; String playerId;
        MoveMessage(String type, String dir, String pId) {this.type=type; this.direction=dir; this.playerId=pId;}
    }

    // --- UI Update ---
    /** Met à jour l'interface multijoueur. DOIT être appelée sur le thread UI. */
    private void updateMultiplayerUI(GameStateResponse state) {
        if (state == null) return;
        if (Looper.myLooper() != Looper.getMainLooper()) { runOnUiThread(() -> updateMultiplayerUI(state)); return; }
        Log.d(TAG, "updateMultiplayerUI: Mise à jour sur thread UI.");

        myScoreLabelMulti.setText(getString(R.string.multiplayer_my_score, state.getMyScore(myPlayerId)));
        opponentScoreLabelMulti.setText(getString(R.string.multiplayer_opponent_score, state.getOpponentScore(myPlayerId)));

        boolean myTurn = myPlayerId != null && myPlayerId.equals(state.getCurrentPlayerId());
        turnIndicatorMulti.setText(myTurn ? R.string.multiplayer_turn_yours : R.string.multiplayer_turn_opponent);
        turnIndicatorMulti.setTextColor(ContextCompat.getColor(this, myTurn ? R.color.tile_16 : R.color.text_tile_low));

        syncBoardViewMulti(state.getBoard());

        if (!state.isGameOver()) {
            if (!myTurn) { statusTextMulti.setText(R.string.multiplayer_status_waiting_opponent); }
            else { statusTextMulti.setText(""); }
        }
        // Le message de fin est géré par handleGameOverUI
    }

    /** Gère l'affichage de fin de partie. DOIT être appelée sur le thread UI. */
    private void handleGameOverUI(GameStateResponse finalState) {
        if (finalState == null || !finalState.isGameOver()) return;
        if (Looper.myLooper() != Looper.getMainLooper()) { runOnUiThread(() -> handleGameOverUI(finalState)); return; }

        Log.i(TAG, "handleGameOverUI: Partie terminée. Winner: " + finalState.getWinnerId());
        String endMessage;
        if ("DRAW".equals(finalState.getWinnerId())) { endMessage = getString(R.string.game_over_draw); }
        else if (myPlayerId != null && myPlayerId.equals(finalState.getWinnerId())) { endMessage = getString(R.string.game_over_won); }
        else if (opponentPlayerId != null && opponentPlayerId.equals(finalState.getWinnerId())) { endMessage = getString(R.string.game_over_lost); }
        else { endMessage = getString(R.string.game_over_generic); }

        statusTextMulti.setText(endMessage);
        turnIndicatorMulti.setText("");
        closeWebSocket(); // Fermer le WebSocket à la fin de la partie
    }


    // --- Board Sync (inchangé) ---
    /** Synchronise la vue de la grille multijoueur avec un état de plateau donné. */
    private void syncBoardViewMulti(int[][] boardState) {
        if (boardState == null || boardState.length != BOARD_SIZE || boardState[0].length != BOARD_SIZE) { Log.e(TAG, "syncBoardViewMulti: État du plateau invalide."); return; }
        boardGridLayoutMulti.removeAllViews();
        tileViewsMulti = new TextView[BOARD_SIZE][BOARD_SIZE];
        for (int r = 0; r < BOARD_SIZE; r++) {
            for (int c = 0; c < BOARD_SIZE; c++) {
                View backgroundCell = new View(this);
                backgroundCell.setBackgroundResource(R.drawable.tile_background);
                backgroundCell.getBackground().setTintList(ContextCompat.getColorStateList(this, R.color.tile_empty));
                GridLayout.LayoutParams bgParams = new GridLayout.LayoutParams(GridLayout.spec(r, 1f), GridLayout.spec(c, 1f));
                bgParams.width = 0; bgParams.height = 0;
                int margin = (int) getResources().getDimension(R.dimen.tile_margin);
                bgParams.setMargins(margin, margin, margin, margin);
                backgroundCell.setLayoutParams(bgParams);
                boardGridLayoutMulti.addView(backgroundCell);
                int value = boardState[r][c];
                if (value > 0) {
                    TextView tileView = createTileTextViewMulti(value, r, c);
                    tileViewsMulti[r][c] = tileView;
                    boardGridLayoutMulti.addView(tileView);
                } else { tileViewsMulti[r][c] = null; }
            }
        }
    }
    /** Crée une TextView pour une tuile multijoueur. */
    private TextView createTileTextViewMulti(int value, int row, int col) {
        TextView tileTextView = new TextView(this);
        setTileStyleMulti(tileTextView, value);
        GridLayout.LayoutParams params = new GridLayout.LayoutParams(GridLayout.spec(row, 1f), GridLayout.spec(col, 1f));
        params.width = 0; params.height = 0;
        int margin = (int) getResources().getDimension(R.dimen.tile_margin);
        params.setMargins(margin, margin, margin, margin);
        tileTextView.setLayoutParams(params);
        return tileTextView;
    }
    /** Applique le style à une tuile. */
    private void setTileStyleMulti(TextView tileTextView, int value) {
        tileTextView.setText(value > 0 ? String.valueOf(value) : "");
        tileTextView.setGravity(Gravity.CENTER);
        tileTextView.setTypeface(null, android.graphics.Typeface.BOLD);
        int backgroundColorId; int textColorId; int textSizeId;
        switch (value) {
            case 0:    backgroundColorId = R.color.tile_empty; textColorId = android.R.color.transparent; textSizeId = R.dimen.text_size_tile_small; break;
            case 2:    backgroundColorId = R.color.tile_2;     textColorId = R.color.text_tile_low;      textSizeId = R.dimen.text_size_tile_small; break;
            case 4:    backgroundColorId = R.color.tile_4;     textColorId = R.color.text_tile_low;      textSizeId = R.dimen.text_size_tile_small; break;
            case 8:    backgroundColorId = R.color.tile_8;     textColorId = R.color.text_tile_high;     textSizeId = R.dimen.text_size_tile_small; break;
            case 16:   backgroundColorId = R.color.tile_16;    textColorId = R.color.text_tile_high;     textSizeId = R.dimen.text_size_tile_small; break;
            case 32:   backgroundColorId = R.color.tile_32;    textColorId = R.color.text_tile_high;     textSizeId = R.dimen.text_size_tile_small; break;
            case 64:   backgroundColorId = R.color.tile_64;    textColorId = R.color.text_tile_high;     textSizeId = R.dimen.text_size_tile_small; break;
            case 128:  backgroundColorId = R.color.tile_128;   textColorId = R.color.text_tile_high;     textSizeId = R.dimen.text_size_tile_medium; break;
            case 256:  backgroundColorId = R.color.tile_256;   textColorId = R.color.text_tile_high;     textSizeId = R.dimen.text_size_tile_medium; break;
            case 512:  backgroundColorId = R.color.tile_512;   textColorId = R.color.text_tile_high;     textSizeId = R.dimen.text_size_tile_medium; break;
            case 1024: backgroundColorId = R.color.tile_1024;  textColorId = R.color.text_tile_high;     textSizeId = R.dimen.text_size_tile_large; break;
            case 2048: backgroundColorId = R.color.tile_2048;  textColorId = R.color.text_tile_high;     textSizeId = R.dimen.text_size_tile_large; break;
            default:   backgroundColorId = R.color.tile_super;   textColorId = R.color.text_tile_high;     textSizeId = R.dimen.text_size_tile_large; break;
        }
        tileTextView.setBackgroundResource(R.drawable.tile_background);
        tileTextView.getBackground().setTint(ContextCompat.getColor(this, backgroundColorId));
        tileTextView.setTextColor(ContextCompat.getColor(this, textColorId));
        tileTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX, getResources().getDimension(textSizeId));
    }

    // --- Swipe Handling (Modifié pour envoyer via WebSocket) ---

    /** Configure le listener de swipe pour le plateau multijoueur. */
    @SuppressLint("ClickableViewAccessibility")
    private void setupSwipeListenerMulti() {
        boardGridLayoutMulti.setOnTouchListener(new OnSwipeTouchListener(this, new OnSwipeTouchListener.SwipeListener() {
            @Override public void onSwipeTop() { handleMultiplayerSwipe(Direction.UP); }
            @Override public void onSwipeBottom() { handleMultiplayerSwipe(Direction.DOWN); }
            @Override public void onSwipeLeft() { handleMultiplayerSwipe(Direction.LEFT); }
            @Override public void onSwipeRight() { handleMultiplayerSwipe(Direction.RIGHT); }
        }));
    }

    /** Gère un swipe : envoie un message WebSocket 'move'. */
    private void handleMultiplayerSwipe(Direction direction) {
        if (currentGameState == null || currentGameId == null || currentGameState.isGameOver() || myPlayerId == null) { Log.d(TAG, "Swipe ignoré (état invalide)."); return; }
        if (!myPlayerId.equals(currentGameState.getCurrentPlayerId())) { Toast.makeText(this, R.string.error_not_your_turn, Toast.LENGTH_SHORT).show(); return; }
        if (webSocket == null || !isWebSocketConnected) { Toast.makeText(this, R.string.error_websocket_disconnected, Toast.LENGTH_SHORT).show(); connectWebSocket(); return; }

        Log.d(TAG, "Swipe détecté: " + direction + ". Envoi du mouvement via WebSocket...");
        statusTextMulti.setText(R.string.multiplayer_status_sending_move);

        MoveMessage moveMsg = new MoveMessage("move", direction.name(), myPlayerId);
        String moveJson = gson.toJson(moveMsg);
        sendWebSocketMessage(moveJson);
        // L'UI sera mise à jour à la réception du gameStateUpdate
    }


    // --- Utility Methods ---
    private void showLoading(boolean show) {
        if (loadingIndicatorMulti != null) { loadingIndicatorMulti.setVisibility(show ? View.VISIBLE : View.GONE); }
    }
    private void handleNetworkError(String message) {
        runOnUiThread(() -> {
            if (statusTextMulti != null) { statusTextMulti.setText(message); }
            Toast.makeText(MultiplayerActivity.this, message, Toast.LENGTH_LONG).show();
            showLoading(false);
        });
    }
    private enum Direction { UP, DOWN, LEFT, RIGHT }

} // Fin MultiplayerActivity