#This code was modified from this github page, which has more info :
# https://justinbois.github.io/bootcamp/2020/lessons/l39_serial.html

#cell for relevant imports
import time
import serial
import serial.tools.list_ports
import panel as pn
pn.extension()

"""
Explanation: This Lab python notebook is designed for the control of the cloud chamber servo motor which controls the flow of mist. This notebook should be run on a device physically connected to that arduino board.
End of explanation
"""


#The meaning of these bytes sent to the board could be changed as needed
#In general: wait -> There is a wait of some set time before taking the next command
#airbrush_on-> moves servo to angle to press the airbrush trigger
#airbrush_off-> similar to the last action, but in reverse

WAIT= 1;
AIRBRUSH_ON = 2;
AIRBRUSH_OFF = 3;

#combining a command for a one second wait, with the airbrush on and off commands
#allows us to create loops and patterns of moisture addition 

def find_arduino(port=None):
    """Get the name of the port that is connected to Arduino."""
    if port is None:
        ports = serial.tools.list_ports.comports()
        for p in ports:
            
            #I found this definition to find a port on github, I'm not sure if our board has different
            #manufacturing, but that can be edited here
            
            if p.manufacturer is not None and "Arduino" in p.manufacturer:
                port = p.device
    return port

port = find_arduino()
#this sets the port to the arduino

# Open port
arduino = serial.Serial(port, baudrate=115200, timeout=1)

# Close and reopen
arduino.close()
arduino.open()


def handshake_arduino(arduino, sleep_time=1):
    """Make sure connection is established by sending
    and receiving bytes."""
    time.sleep(sleep_time);

    # Set a long timeout to complete handshake
    timeout = arduino.timeout
    arduino.timeout = 2

    # Send request to Arduino
    arduino.write(bytes([1]))

    # Wait for Arduino to respond
    while (arduino.in_waiting < 0):
        pass

    # Read in which Arduino sent and ignore it
    _ = arduino.read_until()

    # Reset the timeout
    arduino.timeout = timeout


handshake_arduino(arduino)



def open_arduino(port, baudrate=115200, timeout=1):
    """Open a connection with an Arduino device and
    handshake to get ready for use."""
    # Open port
    arduino = serial.Serial(port, baudrate=baudrate, timeout=timeout)

    # Close and reopen
    arduino.close()
    arduino.open()

    handshake_arduino(arduino)

    return arduino

arduino = open_arduino(port)

"""
Explanation: All commands to be sent to the board need to have an integer associated with them, so we can send the command as a byte.
End of explanation
"""


def toggle_airbrush_live(event):
    if event.new:
        arduino.write(bytes([AIRBRUSH_ON]))
    else:
        arduino.write(bytes([AIRBRUSH_OFF]))
        
airbrush_toggle_live = pn.widgets.Toggle(
    name="ADD CLOUD NOW", value=False, button_type="primary", width=100,
)

def toggle_airbrush_set(event):
    if event.new:
        arduino.write(bytes([AIRBRUSH_ON]))
        #turns airbrush on
        
        for i in range(ab_time):
            arduino.write(bytes([WAIT]))
        #waits for set amount of time
    
        arduino.write(bytes([AIRBRUSH_OFF]))
        #turns airbrush off after
        
airbrush_toggle_set = pn.widgets.Toggle(
    name="ADD CLOUD FOR SET TIME".format(ab_time), value=False, button_type="default", width=100,
)
        
def toggle_wait(event):
    if event.new:
        for i in range(wait_time):
            arduino.write(bytes([WAIT]))
        
wait_toggle = pn.widgets.Toggle(
    name="WAIT", value=False, button_type="default", width=100,
)

#this sets an amount of time to wait before initializing next action
wait_time =   pn.widgets.Spinner(value=0, width=75)
ab_time =   pn.widgets.Spinner(value=0, width=75)

airbrush_watch = airbrush_toggle.param.watch(toggle_airbrush, 'value')
wait_watch = wait_toggle.param.watch(toggle_wait, 'value')

#watchers need to be defined so that it is clear if an event is new for the toggle definitions.


#in the panel module we get these live toggle buttons

pn.Row(airbrush_toggle_live,pn.Spacer(width=25),airbrush_toggle_set, pn.Spacer(width=65), ab_time, pn.Spacer(width=0),wait_toggle,wait_time)

"""
Explanation: In order to create a toggle button for the values sent to the board, each toggle needs to be defined, (see examples below,) as well as a widget made in panels
End of explanation
"""


def pattern_add(on_time,off_time,rounds):
    """pattern add controls the airbrush in a pattern
    it takes the parameters of the on_time, the off_time,
    and how many times that it should cycle between the two"""
        for i in range(rounds):
            for j in range(off_time):
                arduino.write(bytes([WAIT])
                              
            arduino.write(bytes([AIRBRUSH_ON]))
            for m in range(on_time):
                arduino.write(bytes([WAIT])
            arduino.write(bytes([AIRBRUSH_OFF]))
    

pattern_add(3,2,5)
#this should cycle through a pattern w/ 3 seconds on, 2 seconds off, for 5 rounds

#always close serial connection when finished
arduino.close()

## TODO
# Add a way for the program to take brightness data and use that information
#to determine how long of a pulse is needed to bring the cloud to a stable state

"""
Explanation: Here is an example of a function to add the cloud incrementally into the chamber
End of explanation
"""


