#important libraries
from picamera import PiCamera
import RPi.GPIO as GPIO
import time
#the camera can work without this library, but remove the led_remove() function
from gpiozero import LED
import os


#setting up GPIO pins
button = 17
up = 13
down = 26
led1 = LED(25)
led2 = LED(12)
led3 = LED(16)
lock = 22
pin1=24
pin2=23
pin3=18

#setting up variables
camera = PiCamera()
cam_rotate=0
i=0
style=0
opt=0
style=""
current_menu="main_menu"
event_detection_active = False

#the address below maybe different for you depending on where you want to save the images
address="/home/raspberry/Desktop/Image"
#one can change/add the object in the list to try out different camera styles raspberry pi has to offer.
style_menu=["none","negative","sketch","watercolor","solarize","cartoon","gpen","hatch","emboss","Exit"]
#you can add more functions and add more menu options, but you would havev to change handle_button function to handle the new options
main_menu=["Take Photo","Change Image Style","Flip Camera","Exit"]

#setting up buttons
GPIO.setmode(GPIO.BCM)
GPIO.setup(button,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(up,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(down,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
#setting up lock and pins
GPIO.setup(lock,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(pin1,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(pin2,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(pin3,GPIO.IN,pull_up_down=GPIO.PUD_DOWN)
GPIO.setup(25,GPIO.OUT)
GPIO.setup(12,GPIO.OUT)
GPIO.setup(16,GPIO.OUT)

#If you want to rotate camera initally, enter:
#camera.rotation =180

#this function checks which user is using the camera and saves the image in the respective folder
def check_user():
    global address
    if GPIO.input(pin1)==0 and GPIO.input(pin2)==1 and GPIO.input(pin3)==0:
        address="/home/raspberry/Desktop/User1"
    elif GPIO.input(pin1)==1 and GPIO.input(pin2)==0 and GPIO.input(pin3)==0:
        address="/home/raspberry/Desktop/User2"
    elif GPIO.input(pin1)==1 and GPIO.input(pin2)==1 and GPIO.input(pin3)==0:
        address="/home/raspberry/Desktop/User3"
    elif GPIO.input(pin1)==1 and GPIO.input(pin2)==1 and GPIO.input(pin3)==1:
        address="/home/raspberry/Desktop/User4"
    else:
        address="/home/raspberry/Desktop/Image"

#this function checks the number of files in the folder, dont need to modify
def check_file_no():
    global i
    i=0
    for path in os.listdir(address):
        if os.path.isfile(os.path.join(address,path)):
            i+=1

#this function displays the menu options
def display_list(menu,selected_index):
    for index,menu_opt in enumerate(menu):
        if index==selected_index:
            print(f"{menu_opt}<-")
        else:
            print(menu_opt)

#this function makes the led dance, not necessary functions but adds a little fun
def led_dance():
    GPIO.output(25,GPIO.HIGH)
    time.sleep(1)
    GPIO.output(12,GPIO.HIGH)
    time.sleep(1)
    GPIO.output(16,GPIO.HIGH)
    time.sleep(1)
    GPIO.output(25,GPIO.LOW)
    GPIO.output(12,GPIO.LOW)
    GPIO.output(16,GPIO.LOW)
    time.sleep(1)
    GPIO.output(25,GPIO.HIGH)
    GPIO.output(12,GPIO.HIGH)
    GPIO.output(16,GPIO.HIGH)
    time.sleep(1)
    GPIO.output(25,GPIO.LOW)
    GPIO.output(12,GPIO.LOW)
    GPIO.output(16,GPIO.LOW)
    
#this function takes the image and saves it in the respective folder
def take_image():
    check_user()
    check_file_no()
    camera.start_preview()
    led_dance()
    camera.stop_preview()
    camera.capture(address+'/image%s.jpg'%i)

#this function handles the button press. Very important function, if you want to add more options, add them here.
def handle_button(opt):
    global current_menu
    global cam_rotate
    if current_menu=="main_menu":
        if opt==0:
            take_image()
            return
        elif opt==1:
            current_menu="style_menu"
            update_display()
            return
        elif opt==2:
            cam_rotate+=1
            camera.start_preview()
            camera.rotation=(cam_rotate*90)
            led_dance()
            camera.stop_preview()
            return
        elif opt==3:
            print("Exiting...")
            exit()
        else:
            exit()
            return
    else:
        style=style_menu[opt]
        if style=="Exit":
            current_menu = "main_menu"
            update_display()
            return
        else:
            camera.start_preview()
            camera.image_effect=style
            led_dance()
            camera.stop_preview()
            update_display()
            return
        

#these functions are for the button press   
def up_callback(channel):
    global opt
    opt +=1
    if current_menu == "main_menu" and opt>=len(main_menu):
        opt=0
    elif current_menu == "style_menu" and opt>=len(style_menu):
        opt=0
    update_display()
    return

def down_callback(channel):
    global opt
    opt-=1
    if opt<0:
        if current_menu == "main_menu":
            opt =len(main_menu)-1
        elif current_menu == "style_menu":
            opt =len(style_menu)-1
    update_display()
    return

def button_callback(channel):
    handle_button(opt)
    return
#this function updates the display, one also has to change this function if you want to add more menu options
def update_display():
    global opt
    clear_screen()
    if current_menu == "main_menu":
        display_list(main_menu,opt)
    elif current_menu == "style_menu":
        display_list(style_menu,opt)
    return
#there can be a better way to do this by using os library, but im dumb
def clear_screen():
    print("\n"*50)
    return
        
#this function is to enable the GIOP pins when unlocked
def enable_buttons():
    global event_detection_active
    if not event_detection_active:
        GPIO.add_event_detect(up,GPIO.FALLING,callback=up_callback, bouncetime=300)
        GPIO.add_event_detect(down,GPIO.FALLING,callback=down_callback, bouncetime=300)
        GPIO.add_event_detect(button,GPIO.FALLING,callback=button_callback, bouncetime=300)
        event_detection_active = True

#this function is to disable the GIOP pins when locked
def disable_buttons():
    global event_detection_active
    if event_detection_active:
        GPIO.remove_event_detect(up)
        GPIO.remove_event_detect(down)
        GPIO.remove_event_detect(button)
        event_detection_active = False

#starting the programm 
try:
    update_display()
    while True:
        #check if its locked or not
        if GPIO.input(lock)==GPIO.HIGH:
            enable_buttons()
            time.sleep(0.1)
        else:
            disable_buttons()
            time.sleep(0.1)           
finally:
    GPIO.cleanup()
