# PingPongGame.py
# Implementation of a simple PingPong game on RPi Pico

import time, utime
from machine import Pin, I2C
from Buzzer import *
from Button import *
from LightStrip import *
from Displays import *
from Log import *

class PingPongGame:
    """
    Implements the logic for a 2-player PingPong game on RPi Pico.
    Uses Buzzer, Button, LightStrip, and LCDDisplay for hardware interaction.
    """

    def __init__(self, buzzerPin=16, buttonPin1=3, buttonPin2=4, neoPixelPin=2, \
                 lcdSdaPin=0, lcdSclPin=1):
        """Initializes game hardware and parameters."""
        Log.i("PingPongGame: constructor")
        self.buzzer = PassiveBuzzer(buzzerPin)
        self.button1 = Button(buttonPin1, "P1", handler=self)
        self.button2 = Button(buttonPin2, "P2", handler=self)
        self.lightStrip = LightStrip(pin=neoPixelPin, numleds=8)
        self.display = LCDDisplay(sda=lcdSdaPin, scl=lcdSclPin)
        self.resetGame()

    def resetGame(self):
        """Resets the game to the initial state."""
        Log.i("PingPongGame: resetGame")
        self.scoreP1 = 0
        self.scoreP2 = 0
        self.servingPlayer = "P1"
        self.ballPosition = 0
        self.ballDirection = 1
        self.gameOver = False
        self.updateDisplay()
        self.lightStrip.setPixel(self.ballPosition, RED)

    def updateDisplay(self):
        """Updates the LCD with current game information."""
        Log.i("PingPongGame: updateDisplay")
        self.display.showText(f"P1:{self.scoreP1} P2:{self.scoreP2}", row=0)
        if self.gameOver:
            winner = "P1" if self.scoreP1 >= 10 else "P2"
            self.display.showText(f"{winner} Wins!", row=1)
        else:
            server = "P1" if self.servingPlayer == "P1" else "P2"
            self.display.showText(f"{server} to serve", row=1)

    def buttonPressed(self, buttonName):
        """Handles button presses for serving and hitting."""
        Log.i(f"PingPongGame: buttonPressed by {buttonName}")

        if self.gameOver:
            self.resetGame()
            return

        if self.servingPlayer == buttonName:  # Serve
            self.ballDirection = 1 if buttonName == "P1" else -1
            self.servingPlayer = "P2" if buttonName == "P1" else "P1"  # Switch server
            self.updateDisplay()
            self.moveBall()  # Start automatic ball movement

        elif not self.gameOver:  # Hit attempt
            # This block is where the main change is:
            if buttonName == "P2" and self.ballDirection == 1:  # P2 hitting
                if self.ballPosition == 7:  # Valid hit
                    self.buzzer.beep(tone=DO, duration=10)
                    self.ballDirection = -1  # Reverse direction
                    self.moveBall() # Continue automatic movement in the opposite direction
                elif 0 <= self.ballPosition < 7: # Foul because it was hit before reaching 7
                    self.handleFoul("P2")

            elif buttonName == "P1" and self.ballDirection == -1: # P1 hitting
                if self.ballPosition == 0:  # Valid hit
                    self.buzzer.beep(tone=DO, duration=10)
                    self.ballDirection = 1 # Reverse direction
                    self.moveBall()  # Continue automatic movement
                elif 0 < self.ballPosition <= 7: # Foul because it was hit before reaching 0
                    self.handleFoul("P1")


    def handleFoul(self, player):
        """Handles foul logic for the given player."""
        Log.i(f"PingPongGame: Foul by {player}")
        self.buzzer.beep(tone=RE, duration=500)
        if player == "P1":
            self.scoreP2 += 1
            self.servingPlayer = "P2"
            self.ballPosition = 7  # Reset ball position
        else:  # player == "P2"
            self.scoreP1 += 1
            self.servingPlayer = "P1"
            self.ballPosition = 0  # Reset ball position
        self.updateDisplay()
        self.lightStrip.setPixel(self.ballPosition, RED)
        self.checkForGameOver()

    def buttonReleased(self, buttonName):
        pass  # No action on button release

    def moveBall(self):
        """Moves the ball automatically and checks for hits during movement."""

        while 0 <= self.ballPosition <= 7:
            self.lightStrip.setPixel(self.ballPosition, BLACK)  # Clear current position
            self.ballPosition += self.ballDirection
            if 0 <= self.ballPosition <= 7:  # Ball is still in bounds
                self.lightStrip.setPixel(self.ballPosition, RED)
                nextPos = self.ballPosition + self.ballDirection
                if 0 <= nextPos <= 7:
                    self.lightStrip.setPixel(nextPos, (50, 0, 0)) #Show next position dimly

            time.sleep_ms(200)

            # Check for hits *during* ball movement. Important change here!
            if self.ballDirection == 1 and self.button2.isPressed() and self.ballPosition == 7:
                self.buzzer.beep(tone=DO, duration=10)
                self.ballDirection = -1   #Reverse direction immediately
            elif self.ballDirection == -1 and self.button1.isPressed() and self.ballPosition == 0:
                self.buzzer.beep(tone=DO, duration=10)
                self.ballDirection = 1

            if not 0<=self.ballPosition<=7: # Check for out of bounds after the hit check
                break


        # Handle miss (out of bounds) - only if loop finishes without a return.
        if self.ballPosition < 0:
            self.scoreP2 += 1
            self.servingPlayer = "P2"
            self.ballPosition = 7  # Reset ball
        elif self.ballPosition > 7:
            self.scoreP1 += 1
            self.servingPlayer = "P1"
            self.ballPosition = 0  # Reset ball

        self.buzzer.beep(tone=RE, duration=500)
        self.updateDisplay()
        self.lightStrip.setPixel(self.ballPosition, RED)
        self.checkForGameOver()

    def checkForGameOver(self):
        """Checks if the game is over (score >= 10)."""
        if self.scoreP1 >= 10 or self.scoreP2 >= 10:
            self.gameOver = True
            self.updateDisplay()
            self.buzzer.beep(tone=DO2, duration=1000)  # Win sound
            self.lightStrip.run(runtype=LightStrip.RAINBOW)


# Initialize and start the game
game = PingPongGame()
BOOTSELLED1239USBRaspberryPiPico©2020RP2-8020/21P64M15.00TTT