import smbus
import RPi.GPIO as GPIO
import socket
import threading
import time

I2C_ADDR = 0x27
LCD_WIDTH = 16
LCD_CHR = 1
LCD_CMD = 0
LCD_LINE_1 = 0x80
LCD_LINE_2 = 0xC0
LCD_BACKLIGHT = 0x08
ENABLE = 0b00000100
E_PULSE = 0.0002
E_DELAY = 0.0002

# left side
left_red_pin = 16
left_yellow_pin = 20
left_green_pin = 21

# right side
right_red_pin = 13
right_yellow_pin = 6
right_green_pin = 5

GPIO.setmode(GPIO.BCM)
GPIO.setup(left_red_pin, GPIO.OUT)
GPIO.setup(left_yellow_pin, GPIO.OUT)
GPIO.setup(left_green_pin, GPIO.OUT)
GPIO.setup(right_red_pin, GPIO.OUT)
GPIO.setup(right_yellow_pin, GPIO.OUT)
GPIO.setup(right_green_pin, GPIO.OUT)

bus = smbus.SMBus(1)


def lcd_init():
    try:
        lcd_byte(0x33, LCD_CMD)
        lcd_byte(0x32, LCD_CMD)
        lcd_byte(0x28, LCD_CMD)
        lcd_byte(0x0C, LCD_CMD)
        lcd_byte(0x06, LCD_CMD)
        lcd_byte(0x01, LCD_CMD)
        time.sleep(0.002)
    except Exception as e:
        print("LCD initialization failed:", e)

def lcd_byte(bits, mode):
    try:
        upper_nibble = (bits & 0xF0) | mode | LCD_BACKLIGHT
        lower_nibble = ((bits << 4) & 0xF0) | mode | LCD_BACKLIGHT
        bus.write_byte(I2C_ADDR, upper_nibble)
        lcd_toggle_enable(upper_nibble)
        bus.write_byte(I2C_ADDR, lower_nibble)
        lcd_toggle_enable(lower_nibble)
    except Exception as e:
        print("Error sending byte with E toggle:", e)

def lcd_toggle_enable(bits):
    try:
        bus.write_byte(I2C_ADDR, (bits | ENABLE))
        time.sleep(E_PULSE)
        bus.write_byte(I2C_ADDR, (bits & ~ENABLE))
        time.sleep(E_DELAY)
    except Exception as e:
        print("Error toggling enable:", e)

def lcd_string(message, line):
    try:
        message = message.ljust(LCD_WIDTH," ")
        lcd_byte(line, LCD_CMD)
        for i in range(LCD_WIDTH):
            lcd_byte(ord(message[i]),LCD_CHR)
    except Exception as e:
        print("Error sending string to LCD:", e)

# Function to control the traffic lights
def control_lights(left_count, right_count):
    if left_count > right_count:
        # Left side has more cars
        GPIO.output(left_red_pin, GPIO.LOW)
        GPIO.output(left_yellow_pin, GPIO.LOW)
        GPIO.output(left_green_pin, GPIO.HIGH)
        GPIO.output(right_red_pin, GPIO.HIGH)
        GPIO.output(right_yellow_pin, GPIO.LOW)
        GPIO.output(right_green_pin, GPIO.LOW)
    elif right_count > left_count:
        # Right side has more cars
        GPIO.output(left_red_pin, GPIO.HIGH)
        GPIO.output(left_yellow_pin, GPIO.LOW)
        GPIO.output(left_green_pin, GPIO.LOW)
        GPIO.output(right_red_pin, GPIO.LOW)
        GPIO.output(right_yellow_pin, GPIO.LOW)
        GPIO.output(right_green_pin, GPIO.HIGH)
    else:
        # Equal count on both sides
        GPIO.output(left_red_pin, GPIO.LOW)
        GPIO.output(left_yellow_pin, GPIO.LOW)
        GPIO.output(left_green_pin, GPIO.HIGH)
        GPIO.output(right_red_pin, GPIO.LOW)
        GPIO.output(right_yellow_pin, GPIO.LOW)
        GPIO.output(right_green_pin, GPIO.HIGH)      


server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', 8500))
server_socket.listen(1)

print('Waiting for connection...')
client_socket, addr = server_socket.accept()
print(f'Connected to {addr}')

try:
    while True:
        data = client_socket.recv(1024).decode('utf-8')
        if data:
            left_count, right_count = map(int, data.split(','))
            print(f'Received data - Left count: {left_count}, Right count: {right_count}')
            lcd_init()
            lcd_string(f'Left: {left_count}', LCD_LINE_1)
            lcd_string(f'Right: {right_count}', LCD_LINE_2)
            control_lights(left_count, right_count)
except KeyboardInterrupt:
    pass
finally:
    client_socket.close()
    server_socket.close()
    GPIO.cleanup()
