import time
import datetime
import json
import redis
import threading
import sys
import os
import RPi.GPIO as GPIO
from picamera import PiCamera
sys.path.append('..')

import variables

#r = redis.Redis(host='127.0.0.1', port=6379)
GPIO.setmode(GPIO.BCM)

class CameraWorker():
	def __init__(self, config, main_thread_running, system_ready, camera_available):
		#self.config = {**config, **self.config}
		self.config = config
		self.pending_reset = False

		#Events
		self.main_thread_running = main_thread_running
		self.system_ready = system_ready
		self.camera_available = camera_available

		#Dynamic Properties based on config
		self.path = self.config['path'].replace(" ", "-") if self.config['path'] is not None else '/etc/mudpi/img/'
		self.topic = self.config['topic'].replace(" ", "/").lower() if self.config['topic'] is not None else 'mudpi/camera/'
		if self.config['resolution'] is not None:
			self.resolutionX = int(self.config['resolution'].get('x', 1920))
			self.resolutionY = int(self.config['resolution'].get('y', 1080))
		if self.config['delay'] is not None:
			self.hours = int(self.config['delay'].get('hours', 0))
			self.minutes = int(self.config['delay'].get('minutes', 0))
			self.seconds = int(self.config['delay'].get('seconds', 0))

		self.init()
		return

	def init(self):
		try:
			self.camera = PiCamera(resolution=(self.resolutionX, self.resolutionY))
			# Below we calibrate the camera for consistent imaging
			self.camera.framerate = 30
			# Wait for the automatic gain control to settle
			time.sleep(2)
			# Now fix the values
			self.camera.shutter_speed = self.camera.exposure_speed
			self.camera.exposure_mode = 'off'
			g = self.camera.awb_gains
			self.camera.awb_mode = 'off'
			self.camera.awb_gains = g
		except:
			self.camera = PiCamera()

		#Pubsub Listeners
		self.pubsub = variables.r.pubsub()
		self.pubsub.subscribe(**{self.topic: self.handleEvent})

		print('Camera Worker...\t\t\t\033[1;32m Ready\033[0;0m')
		return

	def run(self): 
		t = threading.Thread(target=self.work, args=())
		t.start()
		self.listener = threading.Thread(target=self.listen, args=())
		self.listener.start()
		print('Camera Worker...\t\t\t\033[1;32m Running\033[0;0m')
		return t

	def wait(self):
		# Calculate the delay
		try:
			self.next_time = (datetime.datetime.now() + datetime.timedelta(hours=self.hours, minutes=self.minutes, seconds=self.seconds)).replace(microsecond=0)
		except:
			#Default every hour
			self.next_time = (datetime.datetime.now() + datetime.timedelta(hours=1)).replace(minute=0, second=0, microsecond=0)
		delay = (self.next_time - datetime.datetime.now()).seconds
		time.sleep(delay)

	def elapsedTime(self):
		self.time_elapsed = time.perf_counter() - self.time_start
		return self.time_elapsed

	def resetElapsedTime(self):
		self.time_start = time.perf_counter()
		pass

	def handleEvent(self, message):
		data = message['data']
		decoded_message = None
		if data is not None:
			try:
				if isinstance(data, dict):
					decoded_message = data
				elif isinstance(data.decode('utf-8'), str):
					temp = json.loads(data.decode('utf-8'))
					decoded_message = temp
					if decoded_message['event'] == 'Timelapse':
						print("Camera Signaled for Reset")
						self.camera_available.clear()
						self.pending_reset = True
			except:
				print('Error Handling Event for Camera')

	def listen(self):
		while self.main_thread_running.is_set():
			if self.system_ready.is_set():
				if self.camera_available.is_set():
					self.pubsub.get_message()
					time.sleep(1)
				else:
					delay = (self.next_time - datetime.datetime.now()).seconds + 15
					time.sleep(delay) #wait 15 seconds after next scheduled picture
					self.camera_available.set()
			else:
				time.sleep(2)
		return

	def work(self):
		self.resetElapsedTime()
		while self.main_thread_running.is_set():
			if self.system_ready.is_set():
				if self.camera_available.is_set():
					# try:
					for i, filename in enumerate(self.camera.capture_continuous(self.path + 'mudpi-{counter:05d}.jpg')):
						if not self.camera_available.is_set():
							if self.pending_reset:
								try:
									os.remove(filename) #cleanup previous file
									self.pending_reset = False
								except:
									print("Error During Camera Reset Cleanup")
							break;
						message = {'event':'StateChanged', 'data':filename}
						variables.r.set('last_camera_image', filename)
						variables.r.publish(self.topic, json.dumps(message))
						print('Image Captured \033[1;36m%s\033[0;0m' % filename)
						self.wait()
					# except:
					# 	print("Camera Worker \t\033[1;31m Unexpected Error\033[0;0m")
					# 	time.sleep(30)
				else:
					time.sleep(1)
					self.resetElapsedTime()
			else:
				#System not ready camera should be off
				time.sleep(1)
				self.resetElapsedTime()
				
			time.sleep(0.1)

		#This is only ran after the main thread is shut down
		self.camera.close()
		self.listener.join()
		self.pubsub.close()
		print("Camera Worker Shutting Down...\t\t\033[1;32m Complete\033[0;0m")