'''
Created on 24 Jan 2017
@author: muth    
'''
import os
import RPi.GPIO as GPIO
import threading
import time
import pygame
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 picamera import PiCamera
from io import BytesIO
from subprocess import check_output
from symbol import except_clause


# Constants
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 240
SCREEN_SIZE = (SCREEN_WIDTH, SCREEN_HEIGHT)
PRINTER_WIDTH = 640
PRINTER_HEIGHT = 384
PRINTER_SIZE = (PRINTER_WIDTH, PRINTER_HEIGHT)
FILE_WIDTH = PRINTER_WIDTH*2
FILE_HEIGHT = PRINTER_HEIGHT*2
FILE_SIZE = (FILE_WIDTH, FILE_HEIGHT)
LCD_ratio = 1.0*SCREEN_WIDTH/SCREEN_HEIGHT

SHOT_PIN = 16
PRINT_PIN = 15
NEXT_PIN = 13
PREV_PIN = 11
HALT_PIN = 31
NO_SCAN = 1
SCAN_MODE = 2
SCAN_MODE_FIX = 3

class SlitScan(object):
    def __init__(self):
        self.image_stack = Image.new('L', PRINTER_SIZE, 0)
        self.x = 0
        self.mode = NO_SCAN
        self.scanDone = False
        self.lastTime = time.time()
        self.screen = screen
        
    def write(self, s):
            
        if self.mode == SCAN_MODE:
            image = Image.frombuffer('L', PRINTER_SIZE, s, "raw", 'L', 0, 1)
            image = image.crop((self.x, 0, self.x+1, PRINTER_HEIGHT))
            self.image_stack.paste(image,(self.x, 0))
            
            if self.x < PRINTER_WIDTH-1:
                self.x += 1
            else:
                self.scanDone = True
                print("spent for 640 lines: ", time.time()-self.lastTime)
            
        if self.mode == SCAN_MODE_FIX:
            image = Image.frombuffer('L', PRINTER_SIZE, s, "raw", 'L', 0, 1)
            image = image.crop((PRINTER_WIDTH/2, 0, (PRINTER_WIDTH/2)+1, PRINTER_HEIGHT))
            image_total = Image.new('L', (self.x+1, PRINTER_HEIGHT), 0)
            image_total.paste(self.image_stack, (0, 0))
            image_total.paste(image,(self.x, 0))
            self.image_stack = image_total.copy()
              
            if self.x < 5000:
                self.x += 1
            else:
                self.scanDone = True
                print("spent for 5000 lines: ", time.time()-self.lastTime)
                  
    def flush(self):
        print('Stop SlitScan') 

# Variables
currentFileNumber = -1
print check_output(['hostname', '-I'])

# pygame & splash screen
screen_size = width, height = 640, 480
backgroundColor = 255, 255, 255
screen = pygame.display.set_mode(screen_size)
pygame.mouse.set_visible(False)
screen.fill(backgroundColor)
logo = pygame.image.load("logo01.png")
previousimage = logo
screen.blit(logo, (40,95))
pygame.display.flip()
clock = pygame.time.Clock()

# greyscale Palette
grey_palette = [(0, 0, 0)]
for i in range(1, 256):
    grey_palette.append( (i, i, i) )

# 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=1000)  
GPIO.add_event_detect(PRINT_PIN, GPIO.FALLING, bouncetime=1000)  
GPIO.add_event_detect(NEXT_PIN, GPIO.FALLING, bouncetime=500)  
GPIO.add_event_detect(PREV_PIN, GPIO.FALLING, bouncetime=500)  
GPIO.add_event_detect(HALT_PIN, GPIO.FALLING, bouncetime=1000)  

# get IP adress
hostIP = check_output(['hostname', '-I'])

# 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 = FILE_SIZE
camera.framerate_range = (0.16666, 90)
camera.contrast = 50
camera.exposure_mode = 'night'

# Start frame buffer to LCD program
# os.system("/home/pi/project/polapi-zero/fb2memLCD/build/fb2memLCD &")

sleep(1)

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

def slideImage(filename, direction):
    global previousimage
    image = pygame.image.load(filename)
    i_width = image.get_width()
    i_height = image.get_height()
    i_ratio = 1.0*i_width/i_height 
    l_height = SCREEN_HEIGHT
    l_width = SCREEN_WIDTH
    
    print 'displays ', filename
    
    if i_ratio < LCD_ratio:
        image = pygame.transform.scale( image, (int(i_width*(1.0*l_height/i_height)),  l_height) )

    else:
        image = pygame.transform.scale( image, (l_width,  int(i_height*(1.0*l_width/i_width))) )
        
    ynew = (l_height-image.get_height())/2
    yold = (l_height-previousimage.get_height())/2
    
    if direction == 1:    
        for i in range(0, l_width, 50):
            xnew = ((l_width-image.get_width())/2)+i-l_width 
            xold = ((l_width-previousimage.get_width())/2)+i
            screen.fill(backgroundColor)
            screen.blit(image, ( xnew , ynew ) )
            screen.blit(previousimage, ( xold, yold) )
            pygame.display.flip()
            clock.tick(20)
    else:
        for i in range(l_width, 0, -50):
            xnew = ((l_width-image.get_width())/2)+i
            xold = ((l_width-previousimage.get_width())/2)+i-l_width 
            screen.fill(backgroundColor)
            screen.blit(image, ( xnew , ynew ) )
            screen.blit(previousimage, ( xold, yold) )
            pygame.display.flip()
            clock.tick(20)
        
    screen.fill(backgroundColor)
    screen.blit(image, ((l_width - image.get_width())/2, (l_height-image.get_height())/2))
    pygame.display.flip()
    previousimage = image

def displayImage(image):
    global grey_palette
    
    l_height = SCREEN_HEIGHT
    l_width = SCREEN_WIDTH

    pgImage = pygame.image.frombuffer(image.tobytes(), image.size, 'P' )
    pgImage.set_palette(grey_palette)

    i_width = pgImage.get_width()
    i_height = pgImage.get_height()
    
    pgImage = pygame.transform.scale( pgImage, (int(i_width*(1.0*l_height/i_height)),  l_height) )
    screen.fill(backgroundColor)
    screen.blit(pgImage, ((l_width - pgImage.get_width())/2, (l_height-pgImage.get_height())/2))
    pygame.display.flip()
    
    
def printImageFile(filename):
    print 'prints ', filename
    # resize to printer resolution and send to printer
    try:
        image = Image.open(filename)
        im_width, im_height = image.size
        if im_width > im_height:
            image = image.rotate(90, expand=1)
            im_width, im_height = image.size
        ratio = (PRINTER_HEIGHT/float(im_width))
        height = int((float(im_height)*float(ratio)))
        image = image.resize((PRINTER_HEIGHT, height), Image.ANTIALIAS)
        
        printer.printImage(image, False)
        printer.justify('C')
        printer.setSize('S')
        printer.println("PolaPi-Zero")
        printer.feed(3)
    except IOError:
        print ("cannot identify image file", filename)
    
def saveImageToFile(image, filename):
    print 'saves image ', filename
    # save full image
    image.save(filename)

#Main loop
while True:
    slitScanProcess = SlitScan()
    camera.start_preview()
    camera.preview.fullscreen = False
    camera.preview.window = (0,0,400,240)
    # Buttons loop
    while True:
        sleep(0.1)
        # take a picture   
        if GPIO.event_detected(SHOT_PIN):
            
            if slitScanProcess.mode == NO_SCAN:
                # Increment file number    
                i = 1   
                while os.path.exists("pz%05d.jpg" % i):
                    i += 1
                currentFileNumber = i
                print("capture pz%05d.jpg" % currentFileNumber)
                # take picture
                camera.capture("pz%05d.jpg" % currentFileNumber, use_video_port=False)
                camera.stop_preview()
                break
            
            if slitScanProcess.mode == SCAN_MODE_FIX:
                slitScanProcess.scanDone = True
                
            if slitScanProcess.mode == SCAN_MODE:
                slitScanProcess.scanDone = True
            
        # start slit-scan mode
        if GPIO.event_detected(PREV_PIN):
            slitScanProcess.mode = SCAN_MODE
            slitScanProcess.lastTime = time.time()
            camera.start_recording(slitScanProcess, format='yuv', resize=PRINTER_SIZE)
            camera.stop_preview()
        # start slit-scan mode
        if GPIO.event_detected(NEXT_PIN):
            slitScanProcess.mode = SCAN_MODE_FIX
            slitScanProcess.lastTime = time.time()
            camera.start_recording(slitScanProcess, format='yuv', resize=PRINTER_SIZE)
            camera.stop_preview()
        # halt system
        if GPIO.event_detected(HALT_PIN):
            haltSystem()
        # slit-scan mode done
        if slitScanProcess.scanDone:
            # Increment file number    
            i = 1
            while os.path.exists("pz%05d.jpg" % i):
                i += 1
            currentFileNumber = i
            print("capture pz%05d.jpg" % currentFileNumber)
            slitScanProcess.image_stack.save("pz%05d.jpg" % currentFileNumber)
            camera.stop_recording()
            camera.stop_preview()
            break
        # review mode
        if GPIO.event_detected(PRINT_PIN):
            hostIP = check_output(['hostname', '-I']) #refresh IP adress
            
            if slitScanProcess.mode == NO_SCAN:
                camera.stop_preview()
                break
                
            if slitScanProcess.mode == SCAN_MODE_FIX:
                slitScanProcess.scanDone = True
                
            if slitScanProcess.mode == SCAN_MODE:
                slitScanProcess.scanDone = True
            
        # show ongoing scan
        if slitScanProcess.mode == SCAN_MODE or slitScanProcess.mode == SCAN_MODE_FIX:
            displayImage(slitScanProcess.image_stack)
    
    # 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
    slideImage("pz%05d.jpg" % currentFileNumber, 1)
    
    # 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
            slideImage("pz%05d.jpg" % currentFileNumber, 1)
        if GPIO.event_detected(PREV_PIN):
            # Decrement current file name and display it
            if os.path.exists("pz%05d.jpg" % (currentFileNumber-1)):
                currentFileNumber -= 1
            slideImage("pz%05d.jpg" % currentFileNumber, 3)
        if GPIO.event_detected(PRINT_PIN):
            # Print current file
            printImageFile("pz%05d.jpg" % currentFileNumber)
        if GPIO.event_detected(HALT_PIN):
            # halt system
            haltSystem()
        if GPIO.event_detected(SHOT_PIN):
            # Exit review
            break
            
print("Main loop has exited")