import os.path

print("Starting...")
print("Snake by Erik Oellers")
import time
try:
    print("Importing Packages...")
    import random
    import screeninfo
    import keyboard
    import threading
    import tkinter as tk
    import sys
    import ctypes
    print("Loaded!")
    '''
    pip install screeninfo
    pip install keyboard
     '''

    print("Creating Shared Class...")
    class shared:
        def __init__(self):
            self.Frame = None
            self.blocks = []
            self.pointcoords = [0,0]
            self.length = 2
            self.topcoords = [0, 0] #x,y
            self.topblock = None
            self.dir = "r"
            self.fpsms = 0.08
            self.point_placed = False
            self.bodypos = []
            self.visiblebodypos = []
            self.minusfps = 0.00005
            self.starttime = 0
            self.usableblocks = []
            self.version = "1.2"
            self.updatecheck = True
            self.endall = False


    s = shared()

    if os.path.exists("snakedebug.txt"):
        with open("snakedebug.txt", "r") as f:
            lines = f.readlines()
            f.close()

        for raw_line in lines:
            try:
                line = raw_line.replace("\n", "")
                print("DEBUGLINE: " + line)
                time.sleep(0.5)
                key = "length ="
                if line.startswith(key):
                    usenum = line.replace(key, "")
                    s.length = int(usenum)

                key = "fpsms"
                if line.startswith(key):
                    usenum = line.replace(key, "")
                    s.fpsms = int(usenum)


                key = "minusfps ="
                if line.startswith(key):
                    usenum = line.replace(key, "")
                    s.minusfps = int(usenum)

            except:
                continue


    print("Creating Frame...")
    width = screeninfo.get_monitors()[0].width
    height = screeninfo.get_monitors()[0].height

    frame_width = round(width / 2.3)
    frame_height = round(height / 2)

    frame = tk.Tk()
    frame.geometry(f"{frame_width - 10}x{frame_height}")
    frame.configure(background="#000000")
    frame.title("Snake - Erik Oellers")

    version = tk.Label(frame, text=s.version, foreground="#ffffff", background="#000000")
    version.place(relx=0.01, rely=0.01)

    frame.update()

    s.Frame = frame
    if s.Frame.winfo_exists():
        print("Frame Verification Successful!")
    else:
        raise Exception("FRAME NOT VERIFIED")
    print("prepare Snake Body")
    for g in range(round(frame_width / 30)):
        for f in range(round(frame_height / 30)):
            newuseableblock = tk.Frame(frame, background="#ffffff", width=30, height=30)
            newuseableblock.place(x=frame_width + 500, y=frame_height + 500)
            s.usableblocks.append(newuseableblock)

    print("Loading Start Settings...")
    def placehead():
        x_start = round(frame_height / 2)
        while x_start % 30 != 0:
            x_start += 1
        s.topcoords = [30, x_start]
        for l in range(s.length):
            s.bodypos.append([30, x_start])
    placehead()

    block = tk.Frame(frame, background="#ffffff", width=30, height=30)
    block.place(x=s.topcoords[0], y=s.topcoords[1])

    point = tk.Frame(frame, background="#ffffff", width=15, height=15)
    point.place(x=s.topcoords[0] + 500, y=s.topcoords[0] + 500)
    s.starttime = time.time()

    print("prepare lose function...")
    def lost():
        endtime = time.time()
        bettime = endtime - s.starttime
        #print("DONE: " + str(round(bettime, 5)))
        block.place_configure(x=width + 500, y=height + 500)

        point.place_configure(x=width + 500, y=height + 500)
        frame.update()
        time.sleep(1.5)


        s.pointcoords = [0, 0]
        s.length = 2
        s.topcoords = [0, 0]  # x,y
        s.dir = "r"
        s.fpsms = 0.08
        s.point_placed = False
        s.bodypos = []

        for block_3 in s.blocks:
            block_3.place_forget()
            s.blocks.remove(block_3)

        placehead()
        frame.update()
        s.starttime = time.time()

    print("Adding Mechanics....")
    def move(add_x, add_y):
        for z in range(1):
            #print("REACHED MOVE: " + str(z))
            if z == 0:
                #print("MOVING: " + str(z))
                #print(str(s.topcoords[0]) + "---" + str(s.topcoords[1]))
                if s.topcoords[0] + add_x > 1 and s.topcoords[0] + add_x < frame_width - 29:
                    new_x = s.topcoords[0] + add_x
                else:
                    lost()
                    break

                if s.topcoords[1] + add_y > 1 and s.topcoords[1] + add_y < frame_height - 29:
                    new_y = s.topcoords[1] + add_y
                else:
                    lost()
                    break
                s.topcoords = [new_x, new_y]
                if add_x != 0 or add_y != 0:
                    s.bodypos.append([new_x, new_y])
    def controls():
        while True:
            if keyboard.is_pressed("w") or keyboard.is_pressed("up"):
                if s.dir != "d":
                    s.dir = "u"
            elif keyboard.is_pressed("s") or keyboard.is_pressed("down"):
                if s.dir != "u":
                    s.dir = "d"
            elif keyboard.is_pressed("a") or keyboard.is_pressed("left"):
                if s.dir != "r":
                    s.dir = "l"
            elif keyboard.is_pressed("d") or keyboard.is_pressed("right"):
                if s.dir != "l":
                    s.dir = "r"
            if s.endall:
                raise SystemExit
            time.sleep(0.0333333)

    def points():
        while True:
            time.sleep(0.0333333)
            if not s.point_placed:
                xplace = random.randint(60, frame_width - 60)
                yplace = random.randint(60, frame_height - 60)
                while xplace % 30 != 0:
                    xplace += 1
                while yplace % 30 != 0:
                    yplace += 1
                s.pointcoords = [xplace, yplace]
                #print(s.pointcoords)
                s.point_placed = True
            if s.endall:
                raise SystemExit

    def snake_body_logic():
        for block_2 in s.blocks:
            block_2.place_forget()
            s.blocks.remove(block_2)
        s.visiblebodypos = []
        for u in range(1, s.length + 1):
            i = len(s.bodypos) - u
            tail_block = s.usableblocks[u]
            xplacetail = s.bodypos[i][0]
            yplacetail = s.bodypos[i][1]
            tail_block.place(x=xplacetail, y=yplacetail)
            s.blocks.append(tail_block)
            s.visiblebodypos.append([xplacetail, yplacetail])
        #print(s.visiblebodypos)


    def check_exist():
        while True:
            starttime = time.time()
            while not s.updatecheck:
                time.sleep(1)
                endtime = time.time()
                if endtime - starttime > 10:
                    s.endall = True
                    raise SystemExit
            s.updatecheck = False
            time.sleep(1)






    print("Starting Threads...")
    control_thread = threading.Thread(target=controls)
    control_thread.start()

    point_thread = threading.Thread(target=points)
    point_thread.start()

    frame_exist_thread = threading.Thread(target=check_exist)
    frame_exist_thread.start()
    print("Threads Startet!")
    def check_coin():
        if s.topcoords[0] == s.pointcoords[0] and s.topcoords[1] == s.pointcoords[1]:
            point.place_configure(x=width+500, y=height + 500)
            s.point_placed = False
            s.length += 1
    print("Starting Mainloop...")
    count = 0
    while True:
        if s.endall:
            raise SystemExit
        if count == 0:
            print("Mainloop Startet!\nClosing Terminal...")
            count += 1
            if sys.platform == "win32":
                ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)
        try:
            timerstart = time.time()
            if s.dir == "u":
                movedirs = [0, -15]
            elif s.dir == "d":
                movedirs = [0, 15]
            elif s.dir == "l":
                movedirs = [-15, 0]
            elif s.dir == "r":
                movedirs = [15, 0]
            else:
                movedirs = [0, 0]


            snake_body_logic()

            point.place_configure(x=s.pointcoords[0] + 7, y=s.pointcoords[1] + 7)

            for p in range(2):
                #print("PLAYED----------------------- " + str(p))
                move(movedirs[0], movedirs[1])
                #print("MOVED: " + str(movedirs[0]) + " ; " + str(movedirs[1]))
                block.place_configure(x=s.topcoords[0], y=s.topcoords[1])
                #print("BLOCKPLACES: " + str(s.topcoords[0]) + " ; " + str(s.topcoords[1]))
                check_coin()
                #print("CHECKED COIN")
                frame.update() #FRAMEUPDATE---FRAMEUPDATE---FRAMEUPDATE---FRAMEUPDATE---FRAMEUPDATE---FRAMEUPDATE---FRAMEUPDATE---FRAMEUPDATE---
                #print("FRAME UPDATE")
                time.sleep(s.fpsms)
                #print("SLEEPDONE")

            if s.topcoords in s.visiblebodypos:
                lost()
                continue
            if s.fpsms > 0.008:
                s.fpsms -= s.minusfps
                #print(s.fpsms)

            timerend = time.time()
            elapsedtime = timerend - timerstart
            s.updatecheck = True
            #print("UPDATED TO TRUE")

            #print(str(round(elapsedtime, 5)) + " TIME")
        except Exception as e:
            if 'invoke "place" command: application has been destroyed' in str(e):
                raise SystemExit
            else:
                print(str(e))


except Exception as e:
    if sys.platform == "win32":
        ctypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 1)
    print("\n\n")
    print("---" * 500)
    print("---" * 500)
    print("---" * 500)
    print(f"FATAL ERROR:\n\n{str(e)}")
    time.sleep(100)