import java.awt.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class GameState {
    private static final int WIDTH = 800, HEIGHT = 600;
    private static final int BUNNY_SIZE = 40, CAR_WIDTH = 80, CAR_HEIGHT = 40;
    private static final int LANE_HEIGHT = 60, GRASS_HEIGHT = 80, NUM_LANES = 5;
    private static final int HOUSE_Y = 50, HOUSE_SIZE = 60;

    private Map<String, Player> players = new ConcurrentHashMap<>();
    private List<Car> cars = new ArrayList<>();
    private boolean gameOver = false;
    private String winner = null;
    private int score = 0;
    private int level = 1;

    public void startGame(boolean isSinglePlayer) {
        players.clear();
        cars.clear();
        gameOver = false;
        winner = null;
        score = 0;
        level = 1;

        players.put("PLAYER1", new Player(200, HEIGHT - BUNNY_SIZE - 10, Color.RED));
        if (!isSinglePlayer) {
            players.put("PLAYER2", new Player(600, HEIGHT - BUNNY_SIZE - 10, Color.BLUE));
        }
        initializeCars();
    }

    private void initializeCars() {
        Random rand = new Random();
        for (int i = 0; i < NUM_LANES; i++) {
            int y = HOUSE_Y + HOUSE_SIZE + i * (LANE_HEIGHT + GRASS_HEIGHT) + (LANE_HEIGHT - CAR_HEIGHT) / 2;
            int speed = (rand.nextInt(3) + 2) * (rand.nextBoolean() ? 1 : -1);
            cars.add(new Car(rand.nextInt(WIDTH), y, speed));
        }
    }

    public void update() {
        moveCars();
        checkCollisions();
        checkWinCondition();
        updateScore();
    }

    private void moveCars() {
        for (Car car : cars) {
            car.move();
            if (car.getX() > WIDTH) car.setX(-CAR_WIDTH);
            if (car.getX() < -CAR_WIDTH) car.setX(WIDTH);
        }
    }

    private void checkCollisions() {
        for (Player player : players.values()) {
            for (Car car : cars) {
                if (car.checkCollision(player.getX(), player.getY(), BUNNY_SIZE)) {
                    player.reset();
                    score = Math.max(0, score - 50);
                }
            }
        }
    }

    private void checkWinCondition() {
        for (Map.Entry<String, Player> entry : players.entrySet()) {
            Player player = entry.getValue();
            if (player.getY() <= HOUSE_Y + HOUSE_SIZE) {
                gameOver = true;
                winner = entry.getKey();
                break;
            }
        }
    }

    private void updateScore() {
        score++;
        if (score % 1000 == 0) {
            level++;
            for (Car car : cars) {
                car.increaseSpeed();
            }
        }
    }

    public void movePlayer(String playerId, String direction) {
        Player player = players.get(playerId);
        if (player != null) {
            switch (direction) {
                case "UP" -> player.moveUp();
                case "DOWN" -> player.moveDown();
                case "LEFT" -> player.moveLeft();
                case "RIGHT" -> player.moveRight();
            }
        }
    }

    public void render(Graphics g) {
        drawBackground(g);
        drawHouses(g);
        drawCars(g);
        drawPlayers(g);
        drawScore(g);
        if (gameOver) {
            drawGameOver(g);
        }
    }

    private void drawBackground(Graphics g) {
        g.setColor(Color.GREEN);
        g.fillRect(0, 0, WIDTH, HEIGHT);
        g.setColor(Color.GRAY);
        for (int i = 0; i < NUM_LANES; i++) {
            int laneY = HOUSE_Y + HOUSE_SIZE + i * (LANE_HEIGHT + GRASS_HEIGHT);
            g.fillRect(0, laneY, WIDTH, LANE_HEIGHT);
        }
    }

    private void drawHouses(Graphics g) {
        g.setColor(Color.ORANGE);
        int houseWidth = WIDTH / NUM_LANES;
        for (int i = 0; i < NUM_LANES; i++) {
            g.fillRect(i * houseWidth, HOUSE_Y, houseWidth - 10, HOUSE_SIZE);
        }
    }

    private void drawPlayers(Graphics g) {
        for (Player player : players.values()) {
            player.draw(g);
        }
    }

    private void drawCars(Graphics g) {
        for (Car car : cars) {
            car.draw(g);
        }
    }

    private void drawScore(Graphics g) {
        g.setColor(Color.BLACK);
        g.setFont(new Font("Arial", Font.BOLD, 20));
        g.drawString("Score: " + score, 10, 25);
        g.drawString("Level: " + level, WIDTH - 100, 25);
    }

    private void drawGameOver(Graphics g) {
        g.setColor(new Color(0, 0, 0, 150));
        g.fillRect(0, 0, WIDTH, HEIGHT);
        g.setColor(Color.WHITE);
        g.setFont(new Font("Arial", Font.BOLD, 40));
        g.drawString("Game Over!", WIDTH / 2 - 100, HEIGHT / 2 - 20);
        g.setFont(new Font("Arial", Font.BOLD, 30));
        g.drawString(winner + " wins!", WIDTH / 2 - 70, HEIGHT / 2 + 20);
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (Map.Entry<String, Player> entry : players.entrySet()) {
            Player p = entry.getValue();
            sb.append(entry.getKey()).append(":").append(p.getX()).append(",").append(p.getY()).append(";");
        }
        for (Car car : cars) {
            sb.append("CAR:").append(car.getX()).append(",").append(car.getY()).append(",").append(car.getSpeed()).append(";");
        }
        sb.append("GAME:").append(gameOver).append(",").append(winner).append(",").append(score).append(",").append(level);
        return sb.toString();
    }

    public void fromString(String state) {
        String[] parts = state.split(";");
        players.clear();
        cars.clear();
        for (String part : parts) {
            String[] data = part.split(":");
            switch (data[0]) {
                case "PLAYER1", "PLAYER2" -> {
                    String[] coords = data[1].split(",");
                    players.put(data[0], new Player(Integer.parseInt(coords[0]), Integer.parseInt(coords[1]), data[0].equals("PLAYER1") ? Color.RED : Color.BLUE));
                }
                case "CAR" -> {
                    String[] coords = data[1].split(",");
                    cars.add(new Car(Integer.parseInt(coords[0]), Integer.parseInt(coords[1]), Integer.parseInt(coords[2])));
                }
                case "GAME" -> {
                    String[] gameData = data[1].split(",");
                    gameOver = Boolean.parseBoolean(gameData[0]);
                    winner = gameData[1].equals("null") ? null : gameData[1];
                    score = Integer.parseInt(gameData[2]);
                    level = Integer.parseInt(gameData[3]);
                }
            }
        }
    }

    private static class Player {
        private int x, y;
        private Color color;

        public Player(int x, int y, Color color) {
            this.x = x;
            this.y = y;
            this.color = color;
        }

        public void moveUp() {
            y = Math.max(HOUSE_Y + HOUSE_SIZE, y - 5);
        }

        public void moveDown() {
            y = Math.min(HEIGHT - BUNNY_SIZE, y + 5);
        }

        public void moveLeft() {
            x = Math.max(0, x - 5);
        }

        public void moveRight() {
            x = Math.min(WIDTH - BUNNY_SIZE, x + 5);
        }

        public void reset() {
            x = color == Color.RED ? 200 : 600;
            y = HEIGHT - BUNNY_SIZE - 10;
        }

        public int getX() {
            return x;
        }

        public int getY() {
            return y;
        }

        public void draw(Graphics g) {
            g.setColor(color);
            g.fillOval(x, y, BUNNY_SIZE, BUNNY_SIZE);
        }
    }
}