from django.forms import model_to_dict
from django.shortcuts import redirect
from django.http import JsonResponse
from django.views import generic
from django.views.generic.edit import CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import PermissionDenied

from .models import TemaVCisleNode, RocnikNode, CisloNode, UlohaVzorakNode, UlohaZadaniNode, TreeNode, CastNode, TextNode, ReseniNode, PohadkaNode, OrgTextNode
from .models import Text, Obrazek
from treenode import treelib
import treenode.forms as f
import treenode.templatetags as tnltt
import treenode.serializers as vr

import logging

logger = logging.getLogger(__name__)


class TNLData(object):
	def __init__(self,anode,parent=None, index=None):
		self.node = anode
		self.sernode = vr.TreeNodeSerializer(anode)
		self.children = []
		self.parent = parent
		self.tema_in_path = False
		self.index = index

		if parent:
			self.tema_in_path = parent.tema_in_path
		if isinstance(anode, TemaVCisleNode):
			self.tema_in_path = True

	def add_edit_options(self):
		self.deletable = tnltt.deletable(self)
		self.editable_siblings = tnltt.editableSiblings(self)
		self.editable_children = tnltt.editableChildren(self)
		self.text_only_subtree = tnltt.textOnlySubtree(self)
		self.can_podvesit_za = tnltt.canPodvesitZa(self)
		self.can_podvesit_pred = tnltt.canPodvesitPred(self)
		self.appendable_children = tnltt.appendableChildren(self)
		print("appChld",self.appendable_children)
		if self.parent:
			self.appendable_siblings = tnltt.appendableChildren(self.parent)
		else:
			self.appendable_siblings = []
	@classmethod
	def public_above(cls, anode):
		""" Returns output of verejne for closest Rocnik, Cislo or Problem above.
		(All of them have method verejne.)"""
		parent = anode # chceme začít už od konkrétního node včetně
		while True:
			rocnik = isinstance(parent, RocnikNode)
			cislo = isinstance(parent, CisloNode)
			uloha = (isinstance(parent, UlohaVzorakNode) or
				isinstance(parent, UlohaZadaniNode))
			tema = isinstance(parent, TemaVCisleNode)

			if (rocnik or cislo or uloha or tema) or parent==None:
				break
			else:
				parent = treelib.get_parent(parent)
		if rocnik:
			return parent.rocnik.verejne()
		elif cislo:
			return parent.cislo.verejne()
		elif uloha:
			return parent.uloha.verejne()
		elif tema:
			return parent.tema.verejne()
		elif None:
			print("Existuje TreeNode, který není pod číslem, ročníkem, úlohou"
			"ani tématem. {}".format(anode))
			return False

	@classmethod
	def all_public_children(cls, anode):
		for ch in treelib.all_children(anode):
			if TNLData.public_above(ch):
				yield ch
			else:
				continue

	@classmethod
	def from_treenode(cls, anode, user, parent=None, index=None):
		if TNLData.public_above(anode) or user.has_perm('auth.org'):
			out = cls(anode,parent,index)
		else:
			raise PermissionDenied()

		if user.has_perm('auth.org'):
			enum_children = enumerate(treelib.all_children(anode))
		else:
			enum_children = enumerate(TNLData.all_public_children(anode))

		for (idx,ch) in enum_children:
			outitem = cls.from_treenode(ch, user, out,  idx)
			out.children.append(outitem)
		out.add_edit_options()
		return out

	@classmethod
	def from_tnldata_list(cls, tnllist):
		"""Vyrobíme virtuální TNL, který nemá obsah, ale má za potomky všechna zadaná TNLData"""
		result = cls(None)
		for idx, tnl in enumerate(tnllist):
			result.children.append(tnl)
			tnl.parent = result
			tnl.index = idx
		result.add_edit_options()
		return result

	@classmethod
	def filter_treenode(cls, treenode, predicate):
		tnll = cls._filter_treenode_recursive(treenode, predicate) # TreeNodeList List :-)
		return TNLData.from_tnldata_list(tnll)

	@classmethod
	def _filter_treenode_recursive(cls, treenode, predicate):
		if predicate(treenode):
			return [cls.from_treenode(treenode)]
		else:
			found = []
			for tn in treelib.all_children(treenode):
				result = cls.filter_treenode(tn, predicate)
				# Result by v tuhle chvíli měl být seznam TNLDat odpovídající treenodům, jež matchnuly predikát.
				for tnl in result:
					found.append(tnl)
			return found

	def to_json(self):
		#self.node = anode
		#self.children = []
		#self.parent = parent
		#self.tema_in_path = False
		#self.index = index
		out = {}
		out['node'] = self.sernode.data
		out['children'] = [n.to_json() for n in self.children]
		out['tema_in_path'] = self.tema_in_path
		out['index'] = self.index
		out['deletable'] = self.deletable
		out['editable_siblings'] = self.editable_siblings
		out['editable_children'] = self.editable_children
		out['text_only_subtree'] = self.text_only_subtree
		out['can_podvesit_za'] = self.can_podvesit_za
		out['can_podvesit_pod'] = self.can_podvesit_pred
		out['appendable_children'] = self.appendable_children
		out['appendable_siblings'] = self.appendable_siblings

		return out



	def __repr__(self):
		return("TNL({})".format(self.node))


class TreeNodeView(generic.DetailView):
	model = TreeNode
	template_name = 'treenode/treenode.html'

	def get_context_data(self,**kwargs):
		context = super().get_context_data(**kwargs)
		context['tnldata'] = TNLData.from_treenode(self.object,self.request.user)
		return context


class TreeNodeJSONView(generic.DetailView):
	model = TreeNode

	def get(self,request,*args, **kwargs):
		self.object = self.get_object()
		data = TNLData.from_treenode(self.object,self.request.user).to_json()
		return JsonResponse(data)


class TreeNodePridatView(generic.View):
	type_from_str = {
		'rocnikNode': RocnikNode,
		'cisloNode': CisloNode,
		'castNode': CastNode,
		'textNode': TextNode,
		'temaVCisleNode': TemaVCisleNode,
		'reseniNode': ReseniNode,
		'ulohaZadaniNode': UlohaZadaniNode,
		'ulohaVzorakNode': UlohaVzorakNode,
		'pohadkaNode': PohadkaNode,
		'orgText': OrgTextNode,
		}

	def post(self, request, *args, **kwargs):
		######## FIXME: ROZEPSANE, NEFUNGUJE, DOPSAT !!!!!! ###########
		node = TreeNode.objects.get(pk=self.kwargs['pk'])
		kam = self.kwargs['kam']
		co = self.kwargs['co']
		typ = self.type_from_str[co]

		raise NotImplementedError('Neni to dopsane, dopis to!')

		if kam not in ('pred','syn','za'):
			raise ValidationError('Přidat lze pouze před nebo za node nebo jako syna')

		if co == TextNode:
			new_obj = Text()
			new_obj.save()
		elif co == CastNode:
			new_obj = CastNode()
			new_obj.nadpis = request.POST.get('pridat-castNode-{}-{}'.format(node.id,kam))
			new_obj.save()
		elif co == ReseniNode:
			new_obj = m
			pass
		elif co == UlohaZadaniNode:
			pass
		elif co == UlohaReseniNode:
			pass
		else:
			new_obj = None


		if kam == 'pred':
			pass


		if kam == 'syn':
			if typ == TextNode:
				text_obj = Text()
				text_obj.save()
				node = treelib.create_child(node, typ, text=text_obj)
			else:
				node = treelib.create_child(node, typ)
		if kam == 'za':
			if typ == TextNode:
				text_obj = Text()
				text_obj.save()
				node = treelib.create_node_after(node, typ, text=text_obj)
			else:
				node = treelib.create_node_after(node, typ)

		return redirect(node.get_admin_url())


class TreeNodeSmazatView(generic.base.View):
	def post(self, request, *args, **kwargs):
		node = TreeNode.objects.get(pk=self.kwargs['pk'])
		if node.first_child:
			raise NotImplementedError('Mazání TreeNode se syny není zatím podporováno!')
		treelib.disconnect_node(node)
		node.delete()
		return redirect(request.headers.get('referer'))


class TreeNodeOdvesitPrycView(generic.base.View):
	def post(self, request, *args, **kwargs):
		node = TreeNode.objects.get(pk=self.kwargs['pk'])
		treelib.disconnect_node(node)
		node.root = None
		node.save()
		return redirect(request.headers.get('referer'))


class TreeNodePodvesitView(generic.base.View):
	def post(self, request, *args, **kwargs):
		node = TreeNode.objects.get(pk=self.kwargs['pk'])
		kam = self.kwargs['kam']
		if kam == 'pred':
			treelib.lower_node(node)
		elif kam == 'za':
			raise NotImplementedError('Podvěsit za není zatím podporováno')
		return redirect(request.headers.get('referer'))


class TreeNodeProhoditView(generic.base.View):
	def post(self, request, *args, **kwargs):
		node = TreeNode.objects.get(pk=self.kwargs['pk'])
		treelib.swap_succ(node)
		return redirect(request.headers.get('referer'))
		#FIXME ve formulari predat puvodni url a vratit redirect na ni

class SirotcinecView(generic.ListView):
	model = TreeNode
	template_name = 'treenode/orphanage.html'

	def get_queryset(self):
		return TreeNode.objects.not_instance_of(RocnikNode).filter(root=None,prev=None,succ=None,father_of_first=None)

# FIXME pouzit Django REST Framework
class TextWebView(generic.DetailView):
	model = Text

	def get(self,request,*args, **kwargs):
		self.object = self.get_object()
		return JsonResponse(model_to_dict(self.object,exclude='do_cisla'))


class VueTestView(generic.TemplateView):
	template_name = 'treenode/vuetest.html'


class NahrajObrazekKTreeNoduView(LoginRequiredMixin, CreateView):
	model = Obrazek
	form_class = f.NahrajObrazekKTreeNoduForm

	def get_initial(self):
		initial = super().get_initial()
		initial['na_web'] = self.request.FILES['upload']
		return initial


	def form_valid(self,form):
		print(self.request.headers)
		print(self.request.headers['Textid'])
		print(form.instance)
		print(form)
		self.object = form.save(commit=False)
		print(self.object.na_web)
		self.object.text = Text.objects.get(pk=int(self.request.headers['Textid']))
		self.object.save()

		return JsonResponse({"url":self.object.na_web.url})