#Import the necessary Packages for this software to run
import mediapipe
import cv2
from collections import Counter
import random
import time
from time import sleep
import RPi.GPIO as GPIO


GPIO.setmode(GPIO.BOARD)

trigger= GPIO.setup(11, GPIO.OUT)
echo= GPIO.setup(13, GPIO.IN)

in1 = 32
in2 = 33
in3 = 36
in4 = 37

GPIO.setup(8,GPIO.OUT)
GPIO.setup(10,GPIO.OUT)
enA= GPIO.PWM(8,100)
enB= GPIO.PWM(10,100)


GPIO.setup(in1, GPIO.OUT)
GPIO.setup(in2, GPIO.OUT)
GPIO.setup(in3, GPIO.OUT)
GPIO.setup(in4, GPIO.OUT)

GPIO.setup(26, GPIO.OUT)


#Use MediaPipe to draw the hand framework over the top of hands it identifies in Real-Time
drawingModule = mediapipe.solutions.drawing_utils
handsModule = mediapipe.solutions.hands


#Use CV2 Functionality to create a Video stream and add some values
cap = cv2.VideoCapture(0)
fourcc = cv2.VideoWriter_fourcc('m', 'p', '4', 'v')



forwardcounter=0
secondcounter=0
obstacle_end=0
dancecounter=0
h=480
w=640
tip=[8,12,16,20]
mid=[6,10,14,18] 
tipname=[8,12,16,20]
midname=[6,10,14,18] 
fingers=[]
finger=[]
start_time=0
elapsed_time=0
beat2=0


def distance(): #ultrasonic sensor function
    GPIO.output(11,GPIO.LOW) #reset trigger
    sleep(0.2)
    GPIO.output(11,GPIO.HIGH)
    sleep(0.00001) #send a short 0.01ms trigger pulse
    GPIO.output(11,GPIO.LOW)
    while GPIO.input(13)==0:
        start_time= time.time() #save start time
    while GPIO.input(13)==1:
        stop_time= time.time() #get the arrival time
    timeelapsed = stop_time-start_time
   # multiply with the sound speed (34300 cm/s)
    obstacle_distance = (timeelapsed *34300)/2    # dis = speed*time speed of sound=343.2m/s  . divide by 2 as we half the distance echo travelled
    #print("distane from object is", obstacle_distance,"cm")
    return obstacle_distance #distance in cm


def forward():
    GPIO.output(in1, GPIO.HIGH)
    GPIO.output(in2, GPIO.LOW)
    GPIO.output(in3, GPIO.HIGH)
    GPIO.output(in4, GPIO.LOW)
    enA.start(40)
    enB.start(40)

def dance():
    for i in range(3):    
        GPIO.output(in1, GPIO.HIGH)
        GPIO.output(in2, GPIO.LOW)
        GPIO.output(in3, GPIO.HIGH)
        GPIO.output(in4, GPIO.LOW)
        enA.start(40)
        enB.start(40)
        sleep(1)
        GPIO.output(in1, GPIO.LOW)
        GPIO.output(in2, GPIO.HIGH)
        GPIO.output(in3, GPIO.LOW)
        GPIO.output(in4, GPIO.HIGH)
        enA.start(40)
        enB.start(40)
        sleep(1)
    
def backwards():
    GPIO.output(in1, GPIO.LOW)
    GPIO.output(in2, GPIO.HIGH)
    GPIO.output(in3, GPIO.LOW)
    GPIO.output(in4, GPIO.HIGH)
    enA.start(50)
    enB.start(50)
    
def stop():
    GPIO.output(in1,GPIO.LOW)
    GPIO.output(in2,GPIO.LOW)
    GPIO.output(in3, GPIO.LOW)
    GPIO.output(in4, GPIO.LOW)
    
def left():
    enA.start(50)
    enB.start(75)
    GPIO.output(in1,GPIO.HIGH)
    GPIO.output(in2,GPIO.LOW)
    GPIO.output(in3, GPIO.LOW)
    GPIO.output(in4, GPIO.HIGH)

def right():
    enA.start(75)
    enB.start(50)
    GPIO.output(in1,GPIO.LOW)
    GPIO.output(in2,GPIO.HIGH)
    GPIO.output(in3, GPIO.HIGH)
    GPIO.output(in4, GPIO.LOW)
    
def all_lightup():
    GPIO.output(26,GPIO.HIGH)
    sleep(0.5)
    GPIO.output(26,GPIO.LOW)
    sleep(0.5)
    GPIO.output(26,GPIO.HIGH)
    sleep(0.5)
    GPIO.output(26,GPIO.LOW)
    sleep(0.5)
    GPIO.output(26,GPIO.HIGH)
    sleep(0.5)
    GPIO.output(26,GPIO.LOW)
def two_lightup():
    GPIO.output(26,GPIO.HIGH)
    sleep(0.5)
    GPIO.output(26,GPIO.LOW)
    sleep(0.5)
    GPIO.output(26,GPIO.HIGH)
    sleep(0.5)
    GPIO.output(26,GPIO.LOW)
def one_lightup():
    GPIO.output(26,GPIO.HIGH)
    sleep(0.5)
    GPIO.output(26,GPIO.LOW)

    
def findnameoflandmark(frame1):
     list=[]
     results = hands.process(cv2.cvtColor(frame1, cv2.COLOR_BGR2RGB))
     if results.multi_hand_landmarks != None:
        for handLandmarks in results.multi_hand_landmarks:


            for point in handsModule.HandLandmark:
                 list.append(str(point).replace ("< ","").replace("HandLandmark.", "").replace("_"," ").replace("[]",""))
     return list
     

stop()
with handsModule.Hands(static_image_mode=False, min_detection_confidence=0.7, min_tracking_confidence=0.7, max_num_hands=1) as hands:

#Create an infinite loop which will produce the live feed to our desktop and that will search for hands
     while True:
           
           ret, frame = cap.read()
           #Unedit the below line if your live feed is produced upsidedown
           #flipped = cv2.flip(frame, flipCode = -1)
           
           #Determines the frame size, 640 x 480 offers a nice balance between speed and accurate identification
           frame1 = cv2.resize(frame, (640, 480))
           
           #produces the hand framework overlay ontop of the hand, you can choose the colour here too)
           list=[] #initializing position list before calling landmark
           results = hands.process(cv2.cvtColor(frame1, cv2.COLOR_BGR2RGB))
           
           #Incase the system sees multiple hands this if statment deals with that and produces another hand overlay
           if results.multi_hand_landmarks != None:
              for handLandmarks in results.multi_hand_landmarks:
                  drawingModule.draw_landmarks(frame1, handLandmarks, handsModule.HAND_CONNECTIONS)
                  list=[]
                  for id, pt in enumerate (handLandmarks.landmark):
                      x = int(pt.x * w)
                      y = int(pt.y * h)
                      list.append([id,x,y])  #Gets landmarks position
                 
         # print('list=', list)
           a = list
           b= findnameoflandmark(frame1)
           
         
           
           if len(b and a)!=0:  #a[id,x,y]; x,y=[0,0] on top left of the screen; and x,y=[1,1] at the bottom right
               print('start is working')
               fingers=[]
               for id in range(0,4):
                  if tip[id]==8 and mid[id]==6:  #index_finger_tip landmark 
                      if (a[tip[id]][2:] < a[mid[id]][2:]):
                          fingers.append(1)
                      else:
                          fingers.append(0)
                  if tip[id]==12 and mid[id]==10: #middle_finger_tip landmark
                      if (a[tip[id]][2:] < a[mid[id]][2:]):
                          fingers.append(1)
                      else:
                          fingers.append(0)
                  if tip[id]==16 and mid[id]==14: #ring_finger_tip landmark
                      if (a[tip[id]][2:] < a[mid[id]][2:]):
                          fingers.append(1)
                      else:
                          fingers.append(0)
                  if tip[id]==20 and mid[id]==18: #pinky_finger_tip landmark
                      if (a[tip[id]][2:] < a[mid[id]][2:]):
                          fingers.append(1)
                      else:
                          fingers.append(0)
                                              
                        
               x=fingers 
               c=Counter(x)
               up=c[1]
               down=c[0]
              

               if x[0] == 1 and x[1]==1 and x[2]==1 and x[3]==1 and forwardcounter==0:
                   print('all up')
                   forwardcounter=forwardcounter+1
                   all_lightup()
                   forward()
                   sleep(2)
               if x[0] == 1 and x[1]==1 and x[2]==0 and x[3]==0: #reset guesture
#                    print('index and middle up')
                   forwardcounter=0 
                   secondcounter=0
                   obstacle_end=0
                   dancecounter=0
                   stop()
                   two_lightup()
                   
                   
               if x[0] == 1 and x[1]==0 and x[2]==0 and x[3]==0 and dancecounter==0:
                   one_lightup()
                   sleep(0.1)
                   dance()
                   dancecounter=dancecounter+1
                   stop()
            

              # else:
               #    stop()
                    
                            
               thumbs_up=0
               elapsed_time =0
               start_time=0
               
           if distance() > 40 and forwardcounter==1 and obstacle_end < 1:
               forward()
               #sleep(2)
        
          
           if distance() < 40 and forwardcounter==1 and secondcounter < 2:
               stop()
               sleep(5)
               secondcounter=secondcounter+1
               print('object detected')
               print(secondcounter)
               
               
               
           if secondcounter == 2 and obstacle_end < 1:
               print("stopping soon")
               forward()
               sleep(2)
               stop()
               obstacle_end = obstacle_end + 1
        
        
                  
            
           #Below shows the current frame to the desktop 
           cv2.imshow("Frame", frame1);
           key = cv2.waitKey(1) & 0xFF
           
           #Below states that if the |q| is press on the keyboard it will stop the system
           if key == ord("q"):
              break

GPIO.cleanup()
