'''
Created on 24 Jan 2017
@author: muth    
'''
import os
import RPi.GPIO as GPIO
import threading
from Adafruit_Thermal import *
from time import sleep
from PIL import Image
from PIL import ImageOps
from PIL import ImageEnhance
from PIL import ImageDraw
from PIL import ImageFont
from smemlcd import SMemLCD
from picamera import PiCamera
from io import BytesIO

# Constants
S_WIDTH = 400
S_HEIGHT = 240
S_SIZE = (S_WIDTH, S_HEIGHT)
P_WIDTH = 640
P_HEIGHT = 384
P_SIZE = (P_WIDTH, P_HEIGHT)
F_WIDTH = 1280
F_HEIGHT = 768
F_SIZE = (F_WIDTH, F_HEIGHT)

SHOT_PIN = 16
PRINT_PIN = 15
NEXT_PIN = 13
PREV_PIN = 11
HALT_PIN = 31

# Thread using the image full resolution
class CameraThread(threading.Thread):
    takeAshot = False
    exitNOshot = False
    stream2 = BytesIO()
        
    def __init__(self):
        threading.Thread.__init__(self)
        self.event = threading.Event()

    def run(self):
        global currentFileNumber
        for bug in camera.capture_continuous(self.stream2, format='jpeg', use_video_port=True, splitter_port=0):
            self.stream2.seek(0) # "Rewind" the stream to the beginning so we can read its content
#             print('Capture thread: ', self.takeAshot)
            if self.takeAshot:
                image = Image.open(self.stream2)
                
                # Increment file number    
                i = 1
                while os.path.exists("pz%05d.jpg" % i):
                    i += 1
                currentFileNumber = i
#              
                # Save last to a jpeg file
                saveImageToFile(image, "pz%05d.jpg" % currentFileNumber)
                
                self.takeAshot = False
                break
            
            if self.exitNOshot:
                break
            
# Variables
currentFileNumber = -1

# GPIO setup
GPIO.setmode(GPIO.BOARD)
GPIO.setup(SHOT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(PRINT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(NEXT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(PREV_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
GPIO.setup(HALT_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)

# add edge detection on a channel
GPIO.add_event_detect(SHOT_PIN, GPIO.FALLING, bouncetime=250)  
GPIO.add_event_detect(PRINT_PIN, GPIO.FALLING, bouncetime=250)  
GPIO.add_event_detect(NEXT_PIN, GPIO.FALLING, bouncetime=250)  
GPIO.add_event_detect(PREV_PIN, GPIO.FALLING, bouncetime=250)  

# Create Sharp mempry LCD
lcd = SMemLCD('/dev/spidev0.0')

# Create Printer
printer = Adafruit_Thermal("/dev/ttyAMA0", 115200, timeout=0, rtscts=True)

# Create camera and in-memory stream
stream = BytesIO()
camera = PiCamera()
camera.rotation = 180
camera.resolution = (F_WIDTH, F_HEIGHT)
camera.framerate = 8
camera.contrast = 50
camera.start_preview()
sleep(1)
#Printer resolution camera Thread to minimize shot delay
liveViewThread = CameraThread()

def haltSystem(channel):
    print 'Halt...'
    os.system("sudo halt")
    
GPIO.add_event_detect(31, GPIO.FALLING, callback = haltSystem, bouncetime = 2000)

def displayImageFileOnLCD(filename):
    print 'displays ', filename
    title = 'Review Mode'
    # resize/dither to screen resolution and send to LCD
    image = Image.open(filename)
    im_width, im_height = image.size
    if im_width < im_height:
        image = image.rotate(90)
    image.thumbnail(S_SIZE, Image.ANTIALIAS)
    image_sized = Image.new('RGB', S_SIZE, (0, 0, 0))
    image_sized.paste(image,((S_SIZE[0] - image.size[0]) / 2, (S_SIZE[1] - image.size[1]) / 2))
    # draw filename
    draw = ImageDraw.Draw(image_sized)
    font = ImageFont.truetype('arial.ttf', 18)
    draw.rectangle([(0, 0), (115, 22)], fill=(255,255,255), outline=(0,0,0))
    draw.text((2, 2), title, fill='black', font=font)
    draw.rectangle([(279, 217), (399, 239)], fill=(255,255,255), outline=(0,0,0))
    draw.text((290, 218), filename, fill='black', font=font)
    # display on LCD
    image_sized = ImageOps.invert(image_sized)
    image_sized = image_sized.convert('1') # convert image to black and white
    lcd.write(image_sized.tobytes())
    
def printImageFile(filename):
    print 'prints ', filename
    # resize to printer resolution and send to printer
    image = Image.open(filename)
    im_width, im_height = image.size
    if im_width > im_height:
        image = image.rotate(90)
    image.thumbnail((P_HEIGHT, P_WIDTH), Image.ANTIALIAS)
    printer.printImage(image, False)
    printer.feed(3)
    
def saveImageToFile(image, filename):
    print 'saves image ', filename
    # save full image
    image.save(filename)
    
#Main loop
while True:
    # Restart shooting thread
    if not liveViewThread.isAlive():
        liveViewThread = CameraThread()
        liveViewThread.start()

    # View Loop
    stream.seek(0)
    for foo in camera.capture_continuous(stream, format='jpeg', use_video_port=True, resize=(S_WIDTH, S_HEIGHT), splitter_port=1):
        t1 = time.time()
        stream.seek(0) # "Rewind" the stream to the beginning so we can read its content
        image_source = Image.open(stream)
        imageInverted = ImageOps.invert(image_source)
        
        # convert image to black or white and send to LCD
        lcd.write(imageInverted.convert('1').tobytes())
        stream.seek(0)
#         print('Live view : capture and display time: %f' % (time.time() - t1))
        
        if GPIO.event_detected(SHOT_PIN):
            liveViewThread.takeAshot = True
            break
        if GPIO.event_detected(PRINT_PIN):
            liveViewThread.exitNOshot = True
            break
    
    # Wait the picture is taken
    liveViewThread.join(5)
        
    # Set current file number if not set yet
    if currentFileNumber == -1 :
        i = 0
        while True:
            if os.path.exists("pz%05d.jpg" % (i+1)):
                i += 1
            else :
                break
        currentFileNumber = i
    
    # Display current image
    displayImageFileOnLCD("pz%05d.jpg" % currentFileNumber)
    
    # Review Loop
    while True:
        sleep(0.25)
        if GPIO.event_detected(NEXT_PIN):
            # Increment current file name and display it
            if os.path.exists("pz%05d.jpg" % (currentFileNumber+1)):
                currentFileNumber += 1
            displayImageFileOnLCD("pz%05d.jpg" % currentFileNumber)
        if GPIO.event_detected(PREV_PIN):
            # Decrement current file name and display it
            if os.path.exists("pz%05d.jpg" % (currentFileNumber-1)):
                currentFileNumber -= 1
            displayImageFileOnLCD("pz%05d.jpg" % currentFileNumber)
        if GPIO.event_detected(PRINT_PIN):
            # Print current file
            printImageFile("pz%05d.jpg" % currentFileNumber)
        if GPIO.event_detected(SHOT_PIN):
            # Exit review
            break
            
print("Main loop has exited")