import socket
import threading

HEADER = 10
PORT = 5050
SERVER = socket.gethostbyname(socket.gethostname())
ADDR = (SERVER, PORT)
FORMAT = 'utf-8'

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(ADDR)

# only valid usernames
users = ['ROBYN', 'NIRMAL', 'AMAAR', 'PATRICK']

# the menu the client can see and order off of
menu = {'starter': ['garlic bread', 'dough balls', 'chicken wings'],
        'main': ['pizza', 'burger', 'pasta'],
        'side': ['fries', 'salad', 'rice']}

# users orders stored here
orders = {}

chef_present = True
global user
global state  # one of: LOGIN, CUSTOMER, NEW, STARTER, MAIN, SIDE, OLD, DONE


def handle_message(msg):
    if chef_present:
        global state
        if state == 'LOGIN':
            if AUTH(msg.upper()):
                update_user(msg)
                state = "CUSTOMER"
                return "NEW or OLD customer?"
            else:
                return "Enter username: "

        if state == 'CUSTOMER':
            if msg.upper() == "NEW":
                state = 'NEW'
                return "MENU or ORDR?"
            elif msg.upper() == "OLD":
                state = 'OLD'
                return 'PKUP?'
            else:
                return "NEW or OLD?"

        if state == "NEW":
            if msg.upper() == "MENU":
                return menu_to_string() + "\nMENU or ORDR?"
            elif msg.upper() == "ORDR":
                state = "STARTER"
                return "Select starter: "
            else:
                return "MENU or ORDR?"

        if state == "STARTER":
            if is_starter(msg.lower()):
                orders[user] = []
                orders[user].append(msg.lower())
                state = "MAIN"
                return "Select main: "
            else:
                return "Select starter: "

        if state == "MAIN":
            if is_main(msg.lower()):
                orders[user].append(msg.lower())
                state = "SIDE"
                return "Select side: "
            else:
                return "Select main: "

        if state == "SIDE":
            if is_side(msg.lower()):
                orders[user].append(msg.lower())
                state = "DONE"
                return "DONE"
            else:
                return "Select side: "

        if state == "DONE":
            return "DONE"

        if state == "OLD":
            if msg.upper() == "PKUP":
                if user in orders:
                    order = orders_to_string(user)
                    orders.pop(user)
                    return order + "\nOrder picked up, you may close the connection."
                else:
                    return "You don't have any orders to pick up. Please close the connection."
            else:
                return "PKUP?"

        else:
            return 'ERR'
    else:
        return 'Sorry, the chef is currently unavailable'


def AUTH(usr):
    if usr in users:
        return True
    else:
        return False


def update_user(name):
    global user
    user = name.upper()


def is_starter(item):
    if item in menu["starter"]:
        return True
    else:
        return False


def is_side(item):
    if item in menu["side"]:
        return True
    else:
        return False


def is_main(item):
    if item in menu["main"]:
        return True
    else:
        return False


def menu_to_string():
    menu_string = "Starters: "
    for item in menu['starter']:
        menu_string += item + ", "
    menu_string += "\nMains: "
    for item in menu['main']:
        menu_string += item + ", "
    menu_string += "\nSides: "
    for item in menu['side']:
        menu_string += item + ", "
    menu_string += "\n"
    return menu_string


def orders_to_string(usr):
    order_string = usr + ":\n"
    for item in orders[usr]:
        order_string += item + "\n"
    return order_string


def handle_client(conn, addr):
    print(f"[NEW CONNECTION] {addr} connected.")
    global state
    state = 'LOGIN'
    connected = True
    while connected:
        msg_length = conn.recv(HEADER).decode(FORMAT)
        if msg_length:
            msg_length = int(msg_length)
            msg = conn.recv(msg_length).decode(FORMAT)
            if msg == "BYE":
                connected = False
                print(f"[{addr}] {msg}")
                conn.send("END".encode(FORMAT))
            else:
                print(f"[{addr}] {msg}")
                conn.send(handle_message(msg).encode(FORMAT))

    conn.close()


def start():
    server.listen()
    print(f"[LISTENING] Server is listening on {SERVER}")
    while True:
        conn, addr = server.accept()
        thread = threading.Thread(target=handle_client, args=(conn, addr))
        thread.start()
        print(f"[ACTIVE CONNECTIONS] {threading.activeCount() - 1}")


print("[STARTING] server is starting...")
start()