# ------------------------------------------
# Name:     task_functions
# Purpose:  Functions creating/managing tasks/displaying.
#
# Author:   Robin Siebler
# ------------------------------------------
__author__ = 'Robin Siebler'

import arrow
import platform
import util

from tasklist import Task, TaskList
from collections import OrderedDict

from colorama import init, Fore, Back, Style

if platform.system() == 'Windows':
	init()

# TODO: Colors that work on the Mac don't work very well on Windows and vice versa
# TODO: Add an ini file so the user can specify the colors to use. Point to the colorma
# TODO: page for instructions

class Functions:
	def __init__(self):
		"""Initialize the task list."""

		self.tasklist = TaskList()
		self.legend = '\nLegend: Not Due  ' + Fore.CYAN + Style.BRIGHT + 'Upcoming  ' + Fore.BLUE + \
		              Style.BRIGHT + 'Due  ' + Fore.RED + Style.BRIGHT + 'Overdue  ' + Fore.WHITE + Style.BRIGHT + \
		              Back.WHITE + 'Completed' + Fore.RESET + Style.NORMAL + Back.RESET

	def show_tasks(self, tasks=None, date_format=None):
		"""Display the tasks (in ID order)

		:param tasks: tasks object
		"""

		if not tasks:
			tasks = self.tasklist.tasks

		if len(tasks) > 0:

			template = '{0:^3} {1:20} {2:^3} {3:20} {4:15} {5:20}'
			print template.format('\nID', 'Description', ' Pri', 'Due', 'Created', 'Tags')
			print template.format('---', '--------------------', '---', '--------------------', '---------------',
			                      '--------------------')
			for task in tasks:
				if task.priority == 'L':
					priority = Fore.YELLOW + Style.BRIGHT + task.priority.center(3) + Fore.RESET + Style.NORMAL
				elif task.priority == 'M':
					priority = Fore.BLUE + Style.BRIGHT + task.priority.center(3) + Fore.RESET + Style.NORMAL
				elif task.priority == 'H':
					priority = Fore.RED + Style.BRIGHT + task.priority.center(3) + Fore.RESET + Style.NORMAL
				else:
					priority = ''

				if task.due_date is None:
					due_date = ''
				else:
					if date_format:
						due_date = task.due_date.rsplit(' ', 1)[0].ljust(20)
					else:
						due_date = (arrow.get(task.due_date, task.due_date_format).humanize()).ljust(20)

					if not task.completed:
						today = arrow.now()
						diff = arrow.get(task.due_date, task.due_date_format) - today
						if diff.days >= 1 and diff.seconds > 0:
							due_date = Fore.CYAN + Style.BRIGHT + due_date + Fore.RESET + Style.NORMAL
						elif diff.days >= 0:
							due_date = Fore.BLUE + Style.BRIGHT + due_date + Fore.RESET + Style.NORMAL
						elif diff.days <= 0:
							due_date = Fore.RED + Style.BRIGHT + due_date + Fore.RESET + Style.NORMAL

				if date_format:
					age = (str(task.creation_date).split()[0]).ljust(15)  # drop the time zone
				else:
					age = (arrow.get(task.creation_date, 'MM/DD/YYYY h:mm:ss A ZZ').humanize()).ljust(15)

				if task.note:
					desc = task.task + ' *'
				else:
					desc = task.task

				if task.completed:
					if task.priority:
						priority = task.priority
					else:
						priority = ''
					task_id = Fore.WHITE + Style.BRIGHT + Back.WHITE + str(task.id).center(3)
					tags = str(task.tags) + Fore.RESET + Style.NORMAL + Back.RESET
					print template.format(task_id, desc, priority, due_date, age, tags)
				else:
					print template.format(task.id, desc, priority, due_date, age, task.tags)

			print self.legend
		else:
			print('\nThere are no tasks to display!\n')

	def show_tasks_by_priority(self, tasks=None, date_format=None):
		"""Display the tasks (in Priority order)

		:param tasks: tasks object
		"""

		low_dict_o = OrderedDict()
		med_dict_o = OrderedDict()
		high_dict_o = OrderedDict()
		no_dict_o = OrderedDict()
		completed_dict_o = OrderedDict()

		low_dict = {}
		med_dict = {}
		high_dict = {}
		no_dict = {}
		completed_dict = {}

		temp_dict = {}

		if not tasks:
			tasks = self.tasklist.tasks

		if len(tasks) > 0:
			for task in tasks:
				if task.due_date is None:
					due_date = ''
				else:
					if date_format:
						due_date = task.due_date.rsplit(' ', 1)[0].ljust(20)
					else:
						due_date = (arrow.get(task.due_date, task.due_date_format).humanize()).ljust(20)

				age = (str(task.creation_date).split()[0]).ljust(15)  # drop the time zone

				if task.note:
					desc = task.task + ' *'
				else:
					desc = task.task

				if task.completed:
					completed_dict[task.id] = task.priority, due_date, age, desc, task.tags
				elif task.priority == 'L':
					low_dict[task.id] = [task.priority, due_date, age, desc, task.tags]
				elif task.priority == 'M':
					med_dict[task.id] = [task.priority, due_date, age, desc, task.tags]
				elif task.priority == 'H':
					high_dict[task.id] = [task.priority, due_date, age, desc, task.tags]
				else:
					no_dict[task.id] = [task.priority, due_date, age, desc, task.tags]

		else:
			print('\nThere are no tasks to display!\n')
			return

		for key, value in sorted(no_dict.items(), key=lambda e: e[1][1]):
			if value[1] is not '':
				no_dict_o[key] = value
			else:
				temp_dict[key] = value

		for key in temp_dict:
			no_dict_o[key] = temp_dict[key]

		temp_dict.clear()

		for key, value in sorted(low_dict.items(), key=lambda e: e[1][1]):
			if value[1] is not '':
				low_dict_o[key] = value
			else:
				temp_dict[key] = value

		for key, value in temp_dict.items():
			low_dict_o[key] = value

		temp_dict.clear()

		for key, value in sorted(med_dict.items(), key=lambda e: e[1][1]):
			if value[1] is not '':
				med_dict_o[key] = value
			else:
				temp_dict[key] = value

		for key, value in temp_dict.items():
			med_dict_o[key] = value

		temp_dict.clear()

		for key, value in sorted(high_dict.items(), key=lambda e: e[1][1]):
			if value[1] is not '':
				high_dict_o[key] = value
			else:
				temp_dict[key] = value

		for key, value in sorted(temp_dict.items(), key=lambda e: e[1][1]):
			high_dict_o[key] = value

		temp_dict.clear()

		for key, value in sorted(completed_dict.items(), key=lambda e: e[1][1]):
			if value[1] is not '':
				completed_dict_o[key] = value
			else:
				temp_dict[key] = value

		for key, value in temp_dict.items():
			completed_dict_o[key] = value

		temp_dict.clear()

		del low_dict
		del med_dict
		del high_dict
		del no_dict
		del completed_dict

		today = arrow.now()

# TODO: Figure out why the key is a tuple instead of a list

		for dict in [low_dict_o, med_dict_o, high_dict_o, no_dict_o]:
			for key, value in dict.items():
				dict[key] = list(dict[key])  # hack - how is this key a tuple!?!
				if value[0] == 'L':
					dict[key][0] = Fore.YELLOW + Style.BRIGHT + value[0].center(3) + Fore.RESET + Style.NORMAL
				elif value[0] == 'M':
					dict[key][0] = Fore.BLUE + Style.BRIGHT + value[0].center(3) + Fore.RESET + Style.NORMAL
				elif value[0] == 'H':
					dict[key][0] =  Fore.RED + Style.BRIGHT + value[0].center(3) + Fore.RESET + Style.NORMAL
				else:
					dict[key][0] = ''

				task = self.tasklist.find_task(key)
				if task.due_date:
					diff = arrow.get(task.due_date, task.due_date_format) - today
					if diff.days >= 1 and diff.seconds > 0:
						dict[key][1] = Fore.CYAN + Style.BRIGHT + value[1] + Fore.RESET + Style.NORMAL
					elif diff.days >= 0:
						dict[key][1] = Fore.BLUE + Style.BRIGHT + value[1] + Fore.RESET + Style.NORMAL
					elif diff.days <= 0:
						dict[key][1] = Fore.RED + Style.BRIGHT + value[1] + Fore.RESET + Style.NORMAL

		template = '{0:^3} {1:20} {2:^3} {3:20} {4:15} {5:20}'
		print template.format('\nPri', 'Description', 'ID', 'Due', 'Created', 'Tags')
		print template.format('---', '--------------------', '---', '--------------------', '---------------',
		                      '--------------------')

		if len(high_dict_o) > 0:
			for key in high_dict_o:
				print template.format(high_dict_o[key][0], high_dict_o[key][3], key, high_dict_o[key][1],
				                      high_dict_o[key][2], high_dict_o[key][4])
		if len(med_dict_o) > 0:
			for key in med_dict_o:
				print template.format(med_dict_o[key][0], med_dict_o[key][3], key, med_dict_o[key][1],
				                      med_dict_o[key][2], med_dict_o[key][4])
		if len(low_dict_o) > 0:
			for key in low_dict_o:
				print template.format(low_dict_o[key][0], low_dict_o[key][3], key, low_dict_o[key][1],
				                      low_dict_o[key][2], low_dict_o[key][4])
		if len(no_dict_o) > 0:
			for key in no_dict_o:
				print template.format(no_dict_o[key][0], no_dict_o[key][3], key, no_dict_o[key][1],
				                      no_dict_o[key][2], no_dict_o[key][4])

		completed_template = Fore.WHITE + Style.BRIGHT + Back.WHITE + '{0:^3} {1:20} {2:^3} {3:20} {4:15} {5:20}' + \
		                     Fore.RESET + Style.NORMAL + Back.RESET
		if len(completed_dict_o) > 0:
			for key in completed_dict_o:
				if completed_dict_o[key][0]:
					priority = completed_dict_o[key][0]
				else:
					priority = ''
				print completed_template.format(priority, completed_dict_o[key][3], key, completed_dict_o[key][1],
				                                completed_dict_o[key][2], completed_dict_o[key][4])
		print self.legend

	def show_task(self, task_id):
		"""Display the specified task, including its notes, if any.

		:param str task_id: the task_id of the task.
		"""

		task_id = self._validate_task_id(task_id)
		if task_id:
			task = self.tasklist.find_task(task_id)
			if task:
				if task.priority == 'L':
					priority = Fore.YELLOW + Style.BRIGHT + ' ' + task.priority + ' ' + Fore.RESET + Style.NORMAL
				elif task.priority == 'M':
					priority = Fore.BLUE + Style.BRIGHT + ' ' + task.priority + ' ' + Fore.RESET + Style.NORMAL
				elif task.priority == 'H':
					priority = Fore.RED + Style.BRIGHT + ' ' + task.priority + ' ' + Fore.RESET + Style.NORMAL
				else:
					priority = ''
				template = '{0:^3} {1:^3} {2:20} {3:40}'
				print template.format('\nID', ' Pri', 'Description', 'Note')
				print template.format('---', '---', '--------------------',
				                      '----------------------------------------')
				print template.format(task.id, priority, task.task, task.note)

	def search_tasks(self, search_string):
		"""Search the task list for a task whose contents contains the user provided search string.

		:param str search_string:    the string to search for.
		"""

		tasks = self.tasklist.search(search_string.lower())
		if tasks:
			self.show_tasks(tasks)
		else:
			print('\nThere were no tasks containing "{}".\n'.format(search_string))

	def add_task(self, task, priority=None, due_date=None, tags=None, note=None):
		"""Add a new task."""

		self.tasklist.add_task(task, priority, due_date, tags, note)

	def delete_task(self, task_id):
		"""Delete a task."""

		task_id = self._validate_task_id(task_id)
		if task_id:
			self.tasklist.delete_task(task_id)
			self.tasklist.renumber_tasks()
			print('Task ' + task_id + ' was deleted.')

	def modify_task(self, task_id, task_=None, completed=False, priority=None, due_date=None, note=None, tags=None, time=None):
		"""Modify a task."""

		task_id = self._validate_task_id(task_id)
		if task_id:
			task = self.tasklist.find_task(task_id)
			if task:
				print 'Modifying task ' + str(task_id) + ': ' + task.task
				if task_:
					task.task = task_
				elif priority:
					task.priority = priority
				elif due_date:
					if isinstance(due_date, list):
						task.due_date = due_date[0]
						task.due_date_format = due_date[1]
					else:
						task.due_date = due_date
				elif note:
					task.note = note
				elif tags:
					task.tags = tags
				elif time:
					time_str = time.split(' ')[0]
					time_hour, time_minute = time_str.split(':')
					if 'PM' in time:
						time_hour = int(time_hour) + 12
					due_date = arrow.get(task.due_date, task.due_date_format)
					due_date = due_date.replace(hour=time_hour, minute=int(time_minute))
					task.due_date = due_date.format(task.due_date_format)
				elif completed:
					task.completed = True
				print 'Modified task ' + str(task_id)

	def load_tasks(self, task_file):
		"""Load the task file and retrieve the tasks."""

		self.tasklist.tasks = util.load(task_file)
		Task.last_id = len(self.tasklist.tasks)

	def save_tasks(self, task_file):
		"""Save the task file."""

		util.save(self.tasklist.tasks, task_file)

	def _validate_task_id(self, task_id):
		"""Validate a task id.

		:return: None if an invalid ID was provided, otherwise a string containing the valid task id.
		"""

		if task_id.isdigit() and int(task_id) <= len(self.tasklist.tasks):
			return task_id
		else:
			print('{} is not an existing task!'.format(task_id))
			return None