import tkinter as tk
import os
import math
import time


is_pi = True
try:
    import RPi.GPIO as GPIO
    print("Detected Raspberry Pi!")
except RuntimeError:
    print("Detected not a Raspberry Pi!")
    is_pi = False


def perform_movement_operation():
    if not is_pi:
        return
    in1 = 21
    in2 = 20
    in3 = 16
    in4 = 12
    GPIO.cleanup()
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    GPIO.setup(in1, GPIO.OUT)
    GPIO.setup(in2, GPIO.OUT)
    GPIO.setup(in3, GPIO.OUT)
    GPIO.setup(in4, GPIO.OUT)
    GPIO.output(in1, GPIO.LOW)
    GPIO.output(in2, GPIO.LOW)
    GPIO.output(in3, GPIO.LOW)
    GPIO.output(in4, GPIO.LOW)
    # Stepper motor parameters
    step_sleep = 0.001
    step_count = 2048  # 5.625*(1/64) per step, 4096 steps is 360°
    direction = True  # True for clockwise, False for counter-clockwise
    # defining stepper motor sequence (found in documentation http://www.4tronix.co.uk/arduino/Stepper-Motors.php)
    step_sequence = [[1, 0, 0, 1],
                    [1, 0, 0, 0],
                    [1, 1, 0, 0],
                    [0, 1, 0, 0],
                    [0, 1, 1, 0],
                    [0, 0, 1, 0],
                    [0, 0, 1, 1],
                    [0, 0, 0, 1]]
    motor_pins = [in1, in2, in3, in4]
    motor_step_counter = 0

    try:
        i = 0
        for i in range(step_count):
            for pin in range(0, len(motor_pins)):
                GPIO.output(motor_pins[pin],
                            step_sequence[motor_step_counter][pin])
            if direction == True:
                motor_step_counter = (motor_step_counter - 1) % 8
            elif direction == False:
                motor_step_counter = (motor_step_counter + 1) % 8
            time.sleep(step_sleep)

    except Exception as ex:
        print (ex)
        exit(1)
    time.sleep(0)

class PistonApp:
    def __init__(self):
        self.root = tk.Tk()
        self.canvas = tk.Canvas(self.root, width=480, height=370)
        self.canvas.pack()
        self.piston = self.canvas.create_rectangle(
            75, 50, 400, 150, fill='gray')
        self.crankshaft = self.canvas.create_oval(
            190, 230, 290, 330, fill='black')
        self.connecting_rod = self.canvas.create_line(0, 0, 0, 0, fill='black')
        self.stroke_length = 50
        self.stroke = 1
        self.theta = 0
        self.manual_mode = False
        self.delay = 400
        self.stroke_label=tk.Label(self.root, font=("Arial", 50),text="")
        self.stroke_label.pack()
        self.last_stroke='up'
        self.manual_mode_var = tk.BooleanVar()
        self.manual_mode_var.set(False)
        self.manual_mode_checkbox = tk.Checkbutton(
            self.root, text="Minecraft Mode", font=("Arial", 30), variable=self.manual_mode_var, command=self.toggle_manual_mode)
        self.manual_mode_checkbox.pack()

        self.next_button = tk.Button(
            self.root, text="Next", height=4, width=14, command=self.next_operation, font=("Arial", 20), state=tk.DISABLED)
        self.next_button.pack()

        self.root.after(1000, self.synchronize_engine)



    def toggle_manual_mode(self):
        self.manual_mode = self.manual_mode_var.get()
        if self.manual_mode:
            self.next_button.config(state=tk.NORMAL)
        else:
            self.next_button.config(state=tk.DISABLED)
            self.root.after(0, self.synchronize_engine)

    def next_operation(self):
        if self.manual_mode:
            if self.last_stroke == 'up':
                self.animate_downstroke()
                self.last_stroke = 'down'
            else:
                self.animate_upstroke()
                self.last_stroke = 'up'

    def synchronize_engine(self):
        if not self.manual_mode:
            if self.last_stroke=='up':
                self.animate_downstroke()
            self.root.after(self.delay, self.animate_upstroke)
            self.root.after(self.delay * 2, self.synchronize_engine)

    def update_connecting_rod(self):
        piston_coords = self.canvas.coords(self.piston)
        px = (piston_coords[0] + piston_coords[2]) / 2
        py = piston_coords[3]
        crankshaft_coords = self.canvas.coords(self.crankshaft)
        cx = (crankshaft_coords[0] + crankshaft_coords[2]) / 2
        cy = (crankshaft_coords[1] + crankshaft_coords[3]) / 2
        r = (crankshaft_coords[3] - crankshaft_coords[1]) / 2
        if self.theta < math.pi / 2:
            px2 = cx
            py2 = cy - math.sqrt(r**2 - (cx - px2)**2)
        else:
            px2 = cx
            py2 = cy + math.sqrt(r**2 - (cx - px2)**2)
        self.canvas.coords(self.connecting_rod, px, py, px2, py2)

    def animate_downstroke(self):
        self.canvas.move(self.piston, 0, self.stroke_length)
        if self.stroke == 1:
            self.canvas.itemconfig(self.piston, fill='blue')
            self.stroke_label.config(text="intake")
        elif self.stroke == 3:
            self.canvas.itemconfig(self.piston, fill='red')
            self.stroke_label.config(text="power")

        self.update_connecting_rod()
        self.stroke += 1
        self.rotate_crankshaft()
        self.last_stroke = 'down'
        if self.manual_mode:
            self.next_button.config(state='normal')
        self.canvas.update()
        perform_movement_operation()

    def animate_upstroke(self):
        self.canvas.update()
        self.stroke_label.update()
        self.canvas.move(self.piston, 0, -self.stroke_length)
        if self.stroke == 2:
            self.canvas.itemconfig(self.piston, fill='orange')
            self.stroke_label.config(text="compression")
        elif self.stroke == 4:
            self.canvas.itemconfig(self.piston, fill='brown')
            self.stroke_label.config(text="exhaust")
        self.update_connecting_rod()
        if self.stroke == 4:
            self.stroke = 1
        else:
            self.stroke += 1
        self.rotate_crankshaft()
        self.last_stroke = 'up'

        if self.manual_mode:
            self.next_button.config(state='normal')
        self.canvas.update()
        perform_movement_operation()
        self.canvas.update()
        self.stroke_label.update()

    def rotate_crankshaft(self):
        self.theta += math.pi / 12  # 30 degrees
        self.canvas.delete(self.connecting_rod)
        self.connecting_rod = self.canvas.create_line(0, 0, 0, 0, fill='black')
        self.update_connecting_rod()


if __name__ == '__main__':
    app = PistonApp()
    app.root.mainloop()