import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

/**
 * Dient dem Aufbau der Matrix (Spielfeld) und füllt diese mit Funktion
 * @author Lucas Bronson, Luca Conte, Joshua Kuklok
 */
public class BoardDisplay extends JPanel {
    private JButton[][] fields;
    private int gridSize;
    private Ship currentShip;
    private Player player;
    private boolean horizontal = false;
    private List<ShipButton> shipButtonList;
    private boolean enemyBoard;
    private Point mousePosition;

    /**
     * Fügt Buttons zu Liste hinzu und aktualisiert Feld durch Aufruf von paintFields
     * @param button
     * @author Joshua Kuklok
     */
    public void addShipButton(ShipButton button) {
        shipButtonList.add(button);
        paintFields();
    }

    /**
     * Gibt currentShip zurück
     * @return currentShip
     * @author Lucas Bronson, Luca Conte, Joshua Kuklok
     */
    public Ship getCurrentShip() {
        return currentShip;
    }

    /**
     * Konstruktor der startLocalGame.
     * @param gridSize Die Größe des Spielfelds
     * @param player Der Spieler
     * @author Lucas Bronson
     */
    public BoardDisplay(int gridSize, Player player, boolean enemyBoard) {
        super(new GridLayout(gridSize + 1, gridSize + 1)); // +1 wegen extra Zeile/Spalte
        this.fields = new JButton[gridSize][gridSize];
        this.shipButtonList = new ArrayList<>();
        this.player = player;
        this.gridSize = gridSize;
        this.enemyBoard = enemyBoard;

        // Erstellung vom Spielfeld
        for (int i = 0; i <= gridSize; i++) {
            for (int j = 0; j <= gridSize; j++) {
                final int x = j - 1; // Temporäre Variable
                final int y = i - 1; // Temporäre Variable
                if (i == 0 && j == 0) {
                    add(new JLabel(" "));
                } else if (i == 0) {
                    JLabel colLabel = new JLabel(String.valueOf(j));
                    colLabel.setHorizontalAlignment(SwingConstants.CENTER);
                    colLabel.setFont(new Font("Roboto", Font.BOLD, 14));
                    add(colLabel);
                } else if (j == 0) {
                    JLabel rowLabel = new JLabel(String.valueOf((char) ('A' + i - 1)));
                    rowLabel.setHorizontalAlignment(SwingConstants.CENTER);
                    rowLabel.setFont(new Font("Arial", Font.BOLD, 14));
                    add(rowLabel);
                } else {
                    // Spielfeld (interaktive Zellen)
                    JButton field = new JButton("");
                    field.setBackground(Color.BLUE);
                    field.setOpaque(true);
                    field.setBorderPainted(true);
                    fields[x][y] = field;
                    add(field);

                    // Um Mausinteraktionen zu ermöglichen (Rechts/- Linksklick, Hover)
                    field.addMouseListener(new MouseAdapter() {

                        // Um beim "Hovern" Position zu setzten und weiterzugeben.
                        @Override
                        public void mouseEntered(MouseEvent e) {
                            mousePosition = new Point(x, y);
                            paintFields();
                        }

                        // Um nach "wegbewegen" der Maus wieder zu entfärben
                        @Override
                        public void mouseExited(MouseEvent e) {
                            paintFields();
                        }

                        // Um Schiffe zu rotieren/platzieren
                        @Override
                        public void mouseClicked(MouseEvent e) {
                            if (SwingUtilities.isRightMouseButton(e)) {
                                togglePlacementDirection();
                            } else if (SwingUtilities.isLeftMouseButton(e)) {
                                Point o = new Point(x, y);
                                handleFieldClick(o);
                                SoundHandler.playSound("plop");
                            }
                        }
                    });
                }
            }
        }

    }

    /**
     * Aktuelles Schiff auswählen
     * @param ship Schiff zum Auswählen
     * @author Lucas Bronson, Joshua Kuklok
     */
    public void selectCurrentShip(Ship ship) {
        this.currentShip = ship;
        paintFields();
    }

    /**
     * Zurücksetzen von aktuellem Schiff und allen Schiffen des Spielers.
     * Danach blau färben des Spielfeldes
     * @author Lucas Bronson, Joshua Kuklok
     */
    public void resetAllShips() {
        this.currentShip = null;
        for (Ship ship : player.getBoard().getShips()) {
            ship.resetPosition();
        }
        paintFields();
    }

    /**
     * Wechselt die Platzierungsrichtung zwischen horizontal und vertikal.
     * @author Lucas Bronson, Joshua Kuklok
     */
    private void togglePlacementDirection() {
        horizontal = !horizontal;
        String direction = horizontal ? "horizontal" : "vertikal";
        System.out.println("Platzierungsrichtung geändert zu: " + direction);
        paintFields();
    }

    /**
     * Handhabt das Platzieren eines Schiffs auf dem Spielfeld.
     * @param o Die Koordinaten des Klicks.
     * @author Lucas Bronson, Luca Conte
     */
    private void handleFieldClick(Point o) {
        System.out.println("CLICK " + o);
        if (!this.enemyBoard && !this.player.isGameRunning() && !this.player.isReady()) {
            this.currentShip.setPosition(o, horizontal, player.getBoard().getShips(),this.gridSize);
        }
        if (this.enemyBoard && this.player.isGameRunning() && this.player.enemy.myTurn) {
            this.player.enemy.shoot(o);
        }
        paintFields();
    }

    /**
     * Färbt das Spielfeld blau, überprüft, ob das aktuelle Schiff null ist.
     * Färbt eine Preview in das Spielfeld ein, ob Schiff setzen möglich ist
     * Schiffe die gesetzt wurden, werden grau gefärbt
     * Aufrufen von refreshButtonState um zu zeigen, ob Button drückbar ist
     * @author Lucas Bronson, Luca Conte, Joshua Kuklok
     */
    public void paintFields() {
        List<Point> test=new ArrayList<>();
        if(currentShip != null) {
            test = currentShip.getVirtualOccupiedPoints(mousePosition, horizontal);
        }
        for(int i = 0; i < gridSize; i++) {
            for(int j = 0; j < gridSize; j++) {
                if(fields[i][j] == null) {
                    continue;
                }
                if (this.enemyBoard) {
                    // enemy board only accessible if game is running AND it's "enemy's" so local player's turn
                    if (!this.player.isGameRunning() || !this.player.enemy.myTurn) {
                        fields[i][j].setEnabled(false);
                    } else {
                        fields[i][j].setEnabled(true);
                    }
                } else {
                    if (this.player.isGameRunning() || this.player.isReady()) {
                        fields[i][j].setEnabled(false);
                    } else {
                        fields[i][j].setEnabled(true);
                    }
                }
                if (this.enemyBoard) {
                    fields[i][j].setBackground(Color.WHITE);
                } else {
                    fields[i][j].setBackground(Color.BLUE);
                }
                if (!this.player.isReady()) {
                    for(Point p : test) {
                        if(i==p.getX() && j==p.getY()) {
                            if (currentShip.checkValidPlacement(mousePosition,horizontal,player.getBoard().getShips(),gridSize)) {
                                fields[i][j].setBackground(Color.GREEN);
                            } else {
                                fields[i][j].setBackground(Color.RED);
                            }
                        }
                    }
                }
                for(Ship ship: player.getBoard().getShips()) {
                    if(ship.isShipOnPos(new Point(i,j))) {
                        fields[i][j].setBackground(Color.LIGHT_GRAY);
                    }
                }
                HitResponse hit = this.player.getBoard().getHitResponseOnPoint(new Point(i, j));
                if (hit != null) {
                    switch (hit.getType()) {
                        case HIT:
                            fields[i][j].setBackground(Color.ORANGE);
                            //SoundHandler.playSound("hit");
                            break;
                        case SUNK:
                            //SoundHandler.playSound("destroyed");
                        case VICTORY:
                            fields[i][j].setBackground(Color.RED);
                            break;
                        case MISS:
                            if (this.enemyBoard) {
                                //SoundHandler.playSound("miss");
                                fields[i][j].setBackground(Color.BLUE);
                            } else {
                                fields[i][j].setBackground(Color.CYAN);
                            }
                            break;
                    }
                }
            }
        }
        for( ShipButton shipButton: shipButtonList) {
            shipButton.refreshButtonState();
        }
    }

    /**
     * Ruft paintFields auf, um Felder zu aktualisieren
     * @author Luca Conte
     */
    public void refresh() {
        paintFields();
    }
}
