from pynput import keyboard
import easygui as ez
import serial as ser
import func
import sys
import time as t
import pycuber as pc
from pycuber.solver import CFOPSolver
import kociemba
import subprocess
import random

def clear_screen():
    subprocess.call("clear")

turn_off_usb = "echo '1-1' |sudo tee /sys/bus/usb/drivers/usb/unbind"
turn_on_usb = "echo '1-1' |sudo tee /sys/bus/usb/drivers/usb/bind"

subprocess.call(turn_on_usb, shell= True)
t.sleep(1)
clear_screen()

exit_program = False
connected = False


while connected == False:
    # connect to serial if no usb connected alert
    try:
        ser = serial.Serial('/dev/ttyACM0', 115200, timeout=60)
        print("\n     Connected")
        connected = True

    except serial.SerialException:
        notconnected = "***Not connected to Arduino board***"
        try_again = ez.buttonbox(notconnected, 'Cube Bot ', ["Try again", "Test Program"], default_choice="Try Again")

        if try_again == "Try Again":
            continue

        if try_again == None:
            exit_program = True
            sys.exit()

        if try_again == "Test Program":
            break

t.sleep(1)

ser.write(b'[')

solved = True
motors_on = True
speed = 1
cube = pc.Cube()
last_rand_move = ""
move_list = ["F B", "F2 B", "F' B", "F B2", "F2 B2", "F' B2", "F B'", "F2 B'", "F' B'", "L R", "L2 R",
             "L' R", "L R2", "L2 R2", "L' R2", "L R'", "L2 R'", "L' R'", "U D",  "U2 D",  "U' D",  "U D2",
             "U2 D2",  "U D2",  "U D'", "U2 D'", "U' D'", "U'", "D'", "F'", "B'", "R'", "L'", "U2", "D2", "R2",
             "L2", "F2", "B2", "U", "D", "F", "B", "R", "L"]


def change_speed():
    global speed
    if speed == 1:
        clear_screen()
        print("\n     Slow Speed")
        ser.write(b'5')
        
        
        speed = 2
    else:
        clear_screen()
        print("\n     Fast Speed")
        ser.write(b'3')
        
        speed = 1

def random_move(cube):
    
    global solved
    global last_rand_move
    global motors_on
    
    solved = False
    
    while True:
        rand_move = random.choice(move_list)

        if last_rand_move != rand_move:
            last_rand_move = rand_move
            break

    translated_rand_move = func.translate(rand_move)
    translated_rand_move = func.add_double_turns(translated_rand_move)

    ser.write(translated_rand_move.encode())

    cube(rand_move)



def random_scramble(cube):

    global solved
    solved = False
    alg = pc.Formula()
    random_alg = alg.random()
    cube(random_alg)

    # mixes the cube
    mixAlg = str(random_alg)  # this is the random scramble

    # runs algs through translate function
    translated_mixAlg = func.translate(mixAlg)
    translated_mixAlg = func.add_double_turns(translated_mixAlg)
    translated_mixAlg = f"[{translated_mixAlg}"

    ser.write(translated_mixAlg.encode())
    t.sleep(2.5)


def move (side):
    global solved
    solved = False
    ser.write(side.encode())
    if side == side.lower():
        side = side.upper() + "'"
        cube(side)
    else:
        cube(side)


def optimal_solve(cube):
    global solved

    if solved == False:
        
        solved = True
        clear_screen()
        print("\n     Solving...")
        defstring = func.get_def_string(cube)

        solution = kociemba.solve(defstring)

        translated_solution = func.translate(solution)
        translated_solution = func.add_double_turns(translated_solution)
        translated_solution = f"+{translated_solution}-"

        ser.write(translated_solution.encode())  # sends solution to arduino
        cube(solution)

        # gets time from arduino
        time = ser.readline()
        time_decoded = time.decode()
        time_decimal = (float(time_decoded) / 1000)
        clear_screen()
        print("\n     " + str(time_decimal) + "sec.")


def cfop_solve(cube):
    global solved

    if solved == False:
        solved = True
        clear_screen()
        print("\n     Loading...")
        solver = CFOPSolver(cube)
        solution = solver.solve(suppress_progress_messages=True) 

        solveAlg = str(solution)  # this is the solution

        # runs algs through translate function
        translated_solveAlg = func.translate(solveAlg)
        translated_solveAlg = func.add_double_turns(translated_solveAlg)

        algHalf1 = f"+{translated_solveAlg[0:40]}"
        algHalf2 = f"{translated_solveAlg[40:]}-"

        clear_screen()
        print("\n     Solving...")
        ser.write(algHalf1.encode())  # sends data to arduino in half because solution is too long
        t.sleep(1)
        ser.write(algHalf2.encode())

        # prints time from arduino
        time = ser.readline()
        time_decimal = (float(time.decode()) / 1000)
        clear_screen()
        print("\n     " + str(time_decimal) + "sec.")

        print(cube)

def cfop_solve(cube): #tutorial
    global solved

    if solved == False:
        solved = True
        clear_screen()
        print("\n     Loading...")
        solver = CFOPSolver(cube)
        solution = solver.solve(suppress_progress_messages=True) 

        solveAlg = str(solution)  # this is the solution

        # runs algs through translate function
        translated_solveAlg = func.translate(solveAlg)

        for item, i in enumerate(str(solution)):
            print(item)
            ser.write(f"+{translated_solveAlg[i]}")
            t.sleep(3)

        print(cube)

def on_press(key):
    pass

def on_release(key):
    global cube
    #print('{0} released'.format(key))
    if key == keyboard.Key.esc:
        # Stop listener
        return False
    elif key == keyboard.KeyCode.from_char('/'):
        move("U")
    elif key == keyboard.KeyCode.from_char('*'):
        move("u")
    elif key == keyboard.KeyCode.from_char('7') or key == keyboard.Key.home:
        move("L")
    elif key == keyboard.KeyCode.from_char('8') or key == keyboard.Key.up:
        move("F")
    elif key == keyboard.KeyCode.from_char('9') or key == keyboard.Key.page_up:
        move("f")
    elif key == keyboard.KeyCode.from_char('-'):
        move("R")
    elif key == keyboard.KeyCode.from_char('4') or key == keyboard.Key.left:
        move("l")
    elif str(key) == "<65437>":
        move("B")
    elif key == keyboard.KeyCode.from_char('6') or key == keyboard.Key.right:
        move("b")
    elif key == keyboard.KeyCode.from_char('+'):
        move("r")
    elif key == keyboard.KeyCode.from_char('2') or key == keyboard.Key.down:
        move("D")
    elif key == keyboard.KeyCode.from_char('3') or key == keyboard.Key.page_down:
        move("d")
    elif key == keyboard.Key.enter:
        optimal_solve(cube)
    elif key == keyboard.KeyCode.from_char(',') or key == keyboard.Key.delete:
        cube = pc.Cube()
        cfop_solve(cube)

    elif key == keyboard.Key.backspace:
        random_move(cube)
    elif key == keyboard.KeyCode.from_char('1') or key == keyboard.Key.end:
        change_speed()

    #elif key == keyboard.KeyCode.from_char('0'):
      #  random_scramble(cube)

#    elif key == keyboard.KeyCode.from_char('0'):





 #Collect events until released
with keyboard.Listener(on_press=on_press, on_release=on_release, suppress=True) as listener:
    listener.join()


try:
    ser.write(b']')
    ser.close()
    print("\n     disconnected")
    subprocess.call(turn_off_usb, shell=True)
    print("\n     USB OFF")
except NameError:
    print('End of program')
    sys.exit()