import network
import time
from machine import I2C, Pin 
from lcd_i2c import I2cLcd 
from umqtt.simple import MQTTClient
import json

MQTT_CLIENT_ID = "esp32_tic_tac_toe_7v_board"
MQTT_BROKER = "broker.mqttdashboard.com"
MQTT_TOPIC = "elsys/v/tic_tac_toe"

AddressOfLcd = 0x27
i2c = I2C(scl=Pin(22), sda=Pin(21), freq=400000)
lcd = I2cLcd(i2c, AddressOfLcd, 4, 20)

current_player = "Player1" 

board = [[" " for _ in range(3)] for _ in range(3)]

def process_msg(topic, msg):
    global current_player
    move = json.loads(msg)
    print(move)
    if move["type"] == "server":
        return
    if move["player"] == current_player and validate_move(move):
        process_move(move)
        current_player = "Player1" if current_player == "Player2" else "Player2"
        notify_next_player()
    elif move["player"] == current_player:
        message = {"type": "server", "player": move["player"], "status": "Invalid move!"}
        client.publish(MQTT_TOPIC, json.dumps(message))
    else:
        message = {"type": "server", "player": move["player"], "status": "Not your turn!"}
        client.publish(MQTT_TOPIC, json.dumps(message))

def validate_move(move):
    x, y = move["move"]
    if 0 <= x < 3 and 0 <= y < 3 and board[x][y] == " ":
        return True
    return False

def process_move(move):
    x, y = move["move"]
    symbol = "X" if move["player"] == "Player1" else "O"
    board[x][y] = symbol
    lcd.move_to(x, y)
    lcd.putstr(symbol)
    print(board)
    if check_win(symbol):
      message1 = {"type": "server", "player": "Player1", "status": move["player"] + " " + "won!"}
      message2 = {"type": "server", "player": "Player2", "status": move["player"] + " " + "won!"}
      client.publish(MQTT_TOPIC, json.dumps(message1))
      client.publish(MQTT_TOPIC, json.dumps(message2))
      reset_game()
    elif check_tie():
      message1 = {"type": "server", "player": "Player1", "status": "It's a tie!"}
      message2 = {"type": "server", "player": "Player2", "status": "It's a tie!"}
      client.publish(MQTT_TOPIC, json.dumps(message1))
      client.publish(MQTT_TOPIC, json.dumps(message2))
      reset_game()

def check_win(player):
    for i in range(3):
        if all(board[i][j] == player for j in range(3)) or all(board[j][i] == player for j in range(3)):
            return True
    if all(board[i][i] == player for i in range(3)) or all(board[i][2 - i] == player for i in range(3)):
        return True
    return False

def check_tie():
    return all(board[i][j] != " " for i in range(3) for j in range(3))

def notify_next_player():
    global current_player
    message = {"type": "server", "player": current_player, "status": "Your turn!"}
    client.publish(MQTT_TOPIC, json.dumps(message))

def reset_game():
    global board
    board = [[" " for _ in range(3)] for _ in range(3)]
    lcd.clear()

client = MQTTClient(MQTT_CLIENT_ID, MQTT_BROKER)
client.set_callback(process_msg)

def connect_to_wifi():
    print("Connecting to Wi-Fi", end="")
    sta_if = network.WLAN(network.STA_IF)
    sta_if.active(True)
    sta_if.connect('Wokwi-GUEST', '')
    while not sta_if.isconnected():
        print(".", end="")
        time.sleep(0.1)
    print(" Connected!")

def connect_to_mqtt_broker():
    connect_to_wifi()
    print("Connecting to MQTT server... ", end="")
    client.connect()
    print("Connected!")
    client.subscribe(MQTT_TOPIC)
    print("Subscribed to {}".format(MQTT_TOPIC))

def main():
    connect_to_mqtt_broker()

    while True:
        client.wait_msg()

if __name__ == "__main__":
    main()