from machine import Pin, I2C
import time
from time import sleep
from ssd1306 import SSD1306_I2C

#SSD DISPLAY
import ssd1306

# ESP32 Pin assignment 
i2c = I2C(0, scl=Pin(21), sda=Pin(5))

# Screen Variables
width = 128
height = 64
line = 1 
highlight = 1
shift = 0
list_length = 0
total_lines = 6

# create the display
oled = SSD1306_I2C(width, height, i2c)
oled.init_display()

# Setup the Rotary Encoder
button_pin = Pin(32, Pin.IN, Pin.PULL_UP)
direction_pin = Pin(34, Pin.IN, Pin.PULL_UP)
step_pin  = Pin(35, Pin.IN, Pin.PULL_UP)

#splash screen
oled.text('Lub Control', 1, 1)
oled.show()
sleep(1)
#splash screen end

#set lub class
class Lub:
    def __init__(self, lub_type, relayPin, prSwitchPin, timeDelay, lastRun, timeOut, fault, startTime, isRunning, savePos1, savePos2, savePos3):
        self.lub_type = lub_type
        self.relayPin = Pin(relayPin, Pin.OUT) #solenoid which starts the air supply to particular pump
        self.prSwitchPin = Pin(prSwitchPin, Pin.IN, Pin.PULL_DOWN) #switch which trips the above solenoid
        self.timeDelay = timeDelay #time gap between successive run of a particular pump
        self.lastRun = lastRun #last time the pump ran
        self.timeOut = timeOut #maximum time for which the pump can run
        self.fault = fault #flag: if the pump runs till the time out time, a fault will be generated 
        self.startTime = startTime
        self.isRunning = isRunning
        self.savePos1 = savePos1
        self.savePos2 = savePos2
        self.savePos3 = savePos3
    
    #updates the variables and 
    def update_timings(self):
        
        if changeTI:
            self.timeDelay = currentTI
        if changeTO:
            self.timeOut = currentTO

        file = open("timings.txt","r")
        data = file.readlines()
        file.close()

        file = open("timings.txt","w")
        if self.lub_type == "lfl" and not dig.value() and changeTI: #for saving propel timing
            data[self.savePos3] = str(currentTI)+'\n'
        else:
            data[self.savePos1] = str(currentTI)+'\n'
            data[self.savePos2] = str(currentTO)+'\n'
        for line in data:
            file.write(line)
        file.close()

        file = open("timings.txt","r")
        data = file.readlines()
        for lines in data:
            print(lines)
        file.close()


# read settings from timings file
#settings are as follows: lfl ti_dig, to, ufl ti, to, ogl ti, to, lfl ti_propel
file = open("timings.txt","r")
data = file.readlines()
for x in range(0, len(data)):
    data[x] = int(data[x])
file.close()

# set lub params after reading data from timing file
lfl = Lub("lfl", 22, 33, data[0], 0, data[1], "n", 0, "", 0, 1, 6)
ufl = Lub("ufl", 23, 4, data[2], 0, data[3], "n", 0, "", 2, 3, None)
ogl = Lub("ogl", 18, 13, data[4], 0, data[5], "n", 0, "", 4, 5, None)

#set the dig and proper mode pins
dig = Pin(12, Pin.IN, Pin.PULL_DOWN) #check for dig mode
propel = Pin(14, Pin.IN, Pin.PULL_DOWN) #check for propel mode

time_delay_dig = time_delay_propel = 0


#check for dig or propel mode
if dig.value() == 1:
    print("DIG Mode")
    print(propel.value())
    lfl.timeDelay = data[0]
    time_delay_dig = data[0]
elif propel.value() == 1:
    print("PROPEL Mode")
    time_delay_propel = data[6]
    lfl.timeDelay = data[6]

#set current time in milliseconds
def current_milli_time():
  return round(time.time() * 1000)

#set lubrication queue....add to this queue to run a pump
queue = ""

def show_save_message():
    oled.fill(0)
    oled.text("Saving....", 1, 1)
    oled.show()
    sleep(2)
    #save to memory
    oled.fill(0)
    oled.text("Saved", 1, 1)
    oled.show()
    sleep(2)    

def show_menu(menu):
    """ Shows the menu on the screen"""
    
    # bring in the global variables
    global line, highlight, shift, list_length

    # menu variables
    item = 1
    line = 1
    line_height = 10

    # clear the display
    oled.fill_rect(0,0,width,height,0)

    # Shift the list of files so that it shows on the display
    list_length = len(menu)
    short_list = menu[shift:shift+total_lines]

    for item in short_list:
        if highlight == line:
            oled.fill_rect(0,(line-1)*line_height, width,line_height,1)
            oled.text(">",0, (line-1)*line_height,0)
            oled.text(item, 10, (line-1)*line_height,0)
            oled.show()
        else:
            oled.text(item, 10, (line-1)*line_height,1)
            oled.show()
        line += 1 
    oled.show()

#variable for encoder menu
pumps = [lfl, ufl, ogl]
menu_for_level_1 = [f"LFL {lfl.isRunning}", f"UFL {ufl.isRunning}", f"OGL {ogl.isRunning}"]
menu = menu_for_level_1
level = 0
whichPump_index = 0

def createSubMenu(counter):
    submenu = []
    submenu.append("Change "+pumps[counter].lub_type+" TI")
    submenu.append("Change "+pumps[counter].lub_type+" TO")  
    submenu.append("Manual")
    submenu.append("Back") 
    return submenu

show_menu(menu) #show intial menu

c = 0 #bug to remove: when clicked on a particular pump in level 0, when level 1 is opened, change TI opens automatically

changeTI = False
changeTO = False
currentTI = 0
currentTO = 0

# for tracking the direction and button state
previous_value = True
button_down = False

showUFLfault = True
showLFLfault = True
showOGLfault = True

manualRun = False

x1 = 0
y1 = 0
# Repeat forever
while True:
    #print(lfl.timeDelay, " ", lfl.timeOut)
    #print(highlight, shift)
    #print(ufl.fault, lfl.fault, ogl.fault, ufl.prSwitchPin.value(), lfl.prSwitchPin.value(), ogl.prSwitchPin.value())
    #only when direct of the encoder is changed        
    
    if dig.value() == 1 and x1 == 0:
        file = open("timings.txt","r")
        data = file.readlines()
        for x in range(0, len(data)):
            data[x] = int(data[x])
        file.close()
        currentTI = lfl.timeDelay = data[0]
        
        x1 += 1
        y1 = 0
        print("DIG")
        
        
    if not dig.value() == 1 and y1 == 0:
        file = open("timings.txt","r")
        data = file.readlines()
        for x in range(0, len(data)):
            data[x] = int(data[x])
        file.close()
        currentTI = lfl.timeDelay = data[6]  
        y1 += 1
        x1 = 0
        print("PROPEL")

    if previous_value != step_pin.value() and not manualRun:
        if step_pin.value() == False:
            # Turned Left 
            if direction_pin.value() == False:

                if changeTI:
                    currentTI += 1
                    oled.fill(0)
                    oled.text(str(currentTI)+" secs", 1, 1)
                    oled.show()
                
                if changeTO:
                    currentTO += 1
                    oled.fill(0)
                    oled.text(str(currentTO)+" secs", 1, 1)
                    oled.show()


                if highlight > 1:
                    highlight -= 1  
                else:
                    if shift > 0:
                        shift -= 1  

            # Turned Right
            else:
                if changeTI:
                    currentTI -= 1
                    oled.fill(0)
                    oled.text(str(currentTI)+" secs", 1, 1)
                    oled.show()

                if changeTO:
                    currentTO -= 1
                    oled.fill(0)
                    oled.text(str(currentTO)+" secs", 1, 1)
                    oled.show()

                if highlight < total_lines:
                    highlight += 1
                else: 
                    if shift+total_lines < list_length:
                        shift += 1

            if not changeTI and not changeTO:
                if level == 0:
                    show_menu([f"LFL {lfl.isRunning}", f"UFL {ufl.isRunning}", f"OGL {ogl.isRunning}"])
                else:
                    show_menu(createSubMenu(whichPump_index))
        previous_value = step_pin.value()   
        
    # Check for button pressed
    if button_pin.value() == False and not button_down:
        print("button pressed")
        button_down = True
        c=c+1

        if level == 0: #click on a pump
            whichPump_index = highlight-1 + shift
            currentTI = pumps[whichPump_index].timeDelay
            currentTO = pumps[whichPump_index].timeOut
            level +=1
            highlight = 1
            shift = 0
            show_menu(createSubMenu(whichPump_index))

        if level == 1 and not changeTI and not changeTO:
            if highlight-1 + shift == 3: #back button for level 1
                level -=1
                highlight = 1
                shift = 0
                show_menu([f"LFL {lfl.isRunning}", f"UFL {ufl.isRunning}", f"OGL {ogl.isRunning}"])
                #showFORlevel0(level, whichPump_index)
                c=0

            elif highlight-1 + shift == 2: #manual button for level 1
                oled.fill(0)
                oled.text("Running Manual",1,1)
                oled.text(pumps[whichPump_index].lub_type, 1, 10)
                oled.show()

                #stop all the pump running at the moment
                for p in pumps:
                    p.relayPin.value(0) #signal to x's solenoid
                    if p.fault != 'y':
                        p.isRunning = ""

                #Run the pump and after that display the menu
                queue = pumps[whichPump_index]
                manualRun = True

                #highlight = 1
                #shift = 0
                #menu = createSubMenu(whichPump_index)
                #show_menu(menu)

            elif highlight-1 + shift == 0 and c!=1: #change time interval
                oled.fill(0)
                oled.text(str(currentTI)+" secs", 1, 1)
                oled.show()
                changeTI = True

            elif highlight-1 + shift == 1: #change timeout
                oled.fill(0)
                oled.text(str(currentTO)+" secs", 1, 1)
                oled.show()
                changeTO = True

        elif changeTI and not changeTO:
            #show_save_message()
            pumps[whichPump_index].update_timings()
            changeTI = False
            highlight = 1
            shift = 0
            show_menu(createSubMenu(whichPump_index))
        
        elif changeTO and not changeTI:
            #show_save_message()
            pumps[whichPump_index].update_timings()
            #pumps[whichPump_index].timeOut = currentTO
            changeTO = False
            highlight = 1
            shift = 0
            show_menu(createSubMenu(whichPump_index))

        #print("Returned from launch")
        
    # Decbounce button
    if button_pin.value() == True and button_down:
        button_down = False

    for p in pumps:
        if(time.time() - p.lastRun > p.timeDelay) and queue == "" and not manualRun:
            if p.fault == "n": #if ufl not in queue and ufl.fault == 'n':
                queue = p    

  
    #start the pump
    if queue!="" and queue.relayPin.value() == 0 and queue.prSwitchPin.value() == 0:
        queue.startTime = time.time()
        queue.relayPin.value(1) #signal to x's solenoid
        queue.isRunning = "Now Rng"
        if level == 0:
            show_menu([f"LFL {lfl.isRunning}", f"UFL {ufl.isRunning}", f"OGL {ogl.isRunning}"])

    #stop the pump
    if queue!="":
        if queue.prSwitchPin.value() == 1 or time.time() - queue.startTime >= queue.timeOut:
            queue.relayPin.value(0) #signal to x's solenoid
            queue.lastRun = time.time()
            queue.isRunning = ""
            if time.time() - queue.startTime >= queue.timeOut: #if the pump has stopped because it ran till its timout time indicates there a problem and set the flag to fault
                queue.fault = "y"
                queue.isRunning = "fault"
            queue = "" #pump has stopped running and is now ready for next pump
            if manualRun:
                manualRun = False
                oled.text("Fault" if pumps[whichPump_index].fault == 'y' else "OK",1,20)
                oled.show()
                sleep(2)
                show_menu(createSubMenu(whichPump_index))
            if level == 0:
                show_menu([f"LFL {lfl.isRunning}", f"UFL {ufl.isRunning}", f"OGL {ogl.isRunning}"])