# FIXME vypreparovat treenode

import datetime

import lorem
import django.contrib.auth
import logging

from .models import Rocnik, Cislo, Deadline, Problem, Tema, Uloha, Clanek

from odevzdavatko.models import Reseni, Hodnoceni
# TODO zbavit se treenodů do treenode.testutils (ty pak klidně volat odtud)
from treenode.models import TextNode, UlohaZadaniNode, UlohaVzorakNode, RocnikNode, CisloNode, TemaVCisleNode, CastNode, MezicisloNode, ReseniNode
from treenode.models import Text

from treenode.treelib import all_children, insert_last_child, all_children_of_type, create_node_after

from odevzdavatko.testutils import gen_reseni_ulohy

logger = logging.getLogger(__name__)

User = django.contrib.auth.get_user_model()


def gen_zadani_ulohy(rnd, cisla, organizatori, pocet_oboru, poradi_cisla, poradi_problemu):
	
	# Proměnné pro náhodné generování názvů a zadání.
	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá",
		"Zákeřná", "Fyzikální"]
	co = ["kostka", "smršť", "díra", "zrada", "toulka", "tyč",
		"úloha", "blecha"]
	sloveso = ["Najděte", "Spočítejte", "Zapište", "Změřte", "Odhadněte"]
	koho = ["délku", "počet", "množství", "dílky"]
	ceho = ["všech", "správných", "konstatních", "zelených"]
	jmeno = ["řešení", "tahů", "čísel", "kalhot", "koulí", "hadů"]
	kde = ["na zemi", "ve vesmíru", "ve vzduchu", "na šňůře", "v letadle"]
	obory = ["M", "F", "I", "O", "B"]

	p = Uloha.objects.create(
		# atributy třídy Problem
		nazev=" ".join([rnd.choice(jaka), rnd.choice(co)]),
		stav=Problem.STAV_ZADANY,
		zamereni=rnd.sample(obory, pocet_oboru),
		autor=rnd.choice(organizatori),
		garant=rnd.choice(organizatori),
		kod=str(poradi_problemu),
		# atributy třídy Uloha
		cislo_zadani=cisla[poradi_cisla-2-1],
		cislo_reseni=cisla[poradi_cisla-1],
		cislo_deadline=cisla[poradi_cisla-1],
		max_body = rnd.randint(1, 8)
	)

	text = " ".join(
		[rnd.choice(sloveso),
		rnd.choice(koho),
		rnd.choice(ceho),
		rnd.choice(jmeno),
		rnd.choice(kde)]
		)
	text_zadani = Text.objects.create(
		na_web = text,
		do_cisla = text,
	)
	zad = TextNode.objects.create(text = text_zadani, root = p.cislo_zadani.rocnik.rocniknode)
	uloha_zadani = UlohaZadaniNode.objects.create(uloha = p, first_child = zad, root = p.cislo_zadani.rocnik.rocniknode)
	p.ulohazadaninode = uloha_zadani
	otec_syn(cisla[poradi_cisla-2-1].cislonode, uloha_zadani)

	return p

def gen_vzoroveho_reseni_ulohy(rnd, organizatori, uloha, pocet_opravovatelu):
	reseni = ["to je přece jasné", "triviální", "omlouváme se,"
		"otevřený problém", "neřešitelné", "triviálně triviální",
		"použitím věty z prvního semestru na matfyzu",
		"jednoduše pomocí látky z druhého semestru na matfyzu",
		"netriviální aplikace diferenciálních rovnic", "zadání je vnitřně"
		"sporné", "nepopsatelně jednoduché", "pokud jste na to nepřišli,"
		"tak jste fakt hloupí"]

	# Generování vzorového řešení.
	obsah = rnd.choice(reseni)
	text_vzoraku = Text.objects.create(
		na_web = obsah,
		do_cisla = obsah
	)
	vzorak = TextNode.objects.create(text = text_vzoraku, root = uloha.cislo_zadani.rocnik.rocniknode)
	uloha_vzorak = UlohaVzorakNode.objects.create(uloha = uloha, first_child = vzorak, root = uloha.cislo_zadani.rocnik.rocniknode)
	uloha.ulohavzoraknode = uloha_vzorak

	uloha.opravovatele.set(rnd.sample(organizatori, pocet_opravovatelu))
	uloha.save()
	return uloha_vzorak


def gen_ulohy_do_cisla(rnd, organizatori, resitele, rocnik_cisla, rocniky, size):
	logger.info('Generuji úlohy do čísla (size={})...'.format(size))

	k = 0
	for rocnik in rocniky:
		k += 1
		print("Generuji {}. číslo.".format(k))
		cisla = rocnik_cisla[k - 1]
		for ci in range(3, len(cisla) + 1): # pro všechna čísla
			resitele_size = round(7/8 * 30 * size) # očekáváný celkový počet řešitelů
			poc_res = rnd.randint(resitele_size//8, resitele_size//4)
 			# dané číslo řeší něco mezi osminou a čtvrtinou všech řešitelů
			# (náhodná hausnumera, možno změnit)
			# účelem je, aby se řešení generovala z menší množiny řešitelů a tedy
			# bylo více řešení od jednoho řešitele daného čísla
			resitele_cisla = rnd.sample(resitele, poc_res)
			for pi in range(1, ((size + 1) // 2) + 1): # počet problémů

				poc_op = rnd.randint(1, 4) # počet opravovatelů
				poc_oboru = rnd.randint(1, 2)
				
				# Generování zadání úlohy a UlohaZadaniNode, 
				# přivěšení pod dané číslo
				p = gen_zadani_ulohy(rnd, cisla, organizatori, poc_oboru, ci, pi)	
				# Generování vzorového řešení
				uloha_vzorak = gen_vzoroveho_reseni_ulohy(rnd, organizatori, 
					p, poc_op)
				insert_last_child(cisla[ci-1].cislonode, uloha_vzorak)

				# Generování řešení a hodnocení k úloze
				gen_reseni_ulohy(rnd, cisla, p, poc_res, ci, 
					resitele_cisla, resitele) 

	return


def gen_rocniky(last_rocnik, size):
	logger.info('Generuji ročníky (size={})...'.format(size))

	rocniky = []
	node = None
	for ri in range(min(last_rocnik - size, 1), last_rocnik + 1):
		rocnik = Rocnik.objects.create(prvni_rok = 1993 + ri, rocnik = ri)
		node2 = RocnikNode.objects.create(rocnik = rocnik, succ = node)
		rocnik.save()
		node = node2
		rocniky.append(rocnik)
	return rocniky


def gen_cisla(rnd, rocniky):
	logger.info('Generuji čísla...')

	rocnik_cisla = []
	for rocnik in rocniky:
		otec = True
		cisla = []
		cisel = rnd.randint(4, 8)
		node = None
		for ci in range(1, cisel + 1):
			# první číslo vydáváme typicky okolo prázdnin
			# (ci - 1)*2 zaručuje první číslo v červnu a všechna
			# další po dvou měsících (což je rozumná aproximace)
			mesic_vydani = (ci - 1)*2 + 6
			# celociselné dělení mi řekne, jestli to je první nebo druhý rok ročníku
			vydano = datetime.date(rocnik.prvni_rok + mesic_vydani // 12,
				(mesic_vydani - 1) % 12 + 1,
				rnd.randint(1, 28))
			deadline = datetime.date(rocnik.prvni_rok + (mesic_vydani + 2) // 12,
				(mesic_vydani + 1) % 12 + 1,
				rnd.randint(1, 28))

			cislo = Cislo.objects.create(
				rocnik = rocnik,
				poradi = str(ci), 
				datum_vydani=vydano,
				verejne_db=True,
			)
			node2 = CisloNode.objects.get(cislo = cislo)
			node2.succ = node
			node2.root = rocnik.rocniknode
			cislo.save()
			deadline = Deadline.objects.create(
				cislo=cislo,
				deadline=deadline,
				typ=Deadline.TYP_CISLA,
				verejna_vysledkovka=True,
			)
			deadline.save()
			node = node2
			if otec:
				otec = False
				rocnik.rocniknode.first_child = node
				rocnik.save()

			cisla.append(cislo)
		rocnik_cisla.append(cisla)
	return rocnik_cisla

def add_first_child(node, child):
	node.first_child = child
	node.save()
	return

def get_text():
	odstavec = lorem.paragraph()
	return Text.objects.create(na_web = odstavec, do_cisla = odstavec)

def gen_dlouhe_tema(rnd, organizatori, rocnik, nazev, obor, kod):
	tema = Tema.objects.create(
				nazev=nazev,
				stav=Problem.STAV_ZADANY,
				zamereni="M",
				autor=rnd.choice(organizatori),
				garant=rnd.choice(organizatori),
				kod=str(kod),
				tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0],
				rocnik=rocnik,
				abstrakt = lorem.paragraph() 
		)
	
	# Generování struktury k tématu
	cisla = sorted(rocnik.cisla.all(), key=lambda cislo: cislo.poradi)
	for cislo in cisla:
		# Přidáme TemaVCisleNode do daného čísla
		cislo_node = cislo.cislonode	
		tema_cislo_node = TemaVCisleNode.objects.create(tema = tema, root = cislo_node.root)
		insert_last_child(cislo_node, tema_cislo_node)
		
		# Přidávání obsahu do čísla
		cast_node = CastNode.objects.create(nadpis = "Příspěvek k číslu {}".format(cislo.kod), root=cislo_node.root)
		add_first_child(tema_cislo_node, cast_node)
	
		text_node = TextNode.objects.create(text = get_text(), root=cislo_node.root)
		add_first_child(cast_node, text_node)

		cast_node2 = CastNode.objects.create(nadpis = "První podproblém", root=cislo_node.root)
		add_first_child(text_node, cast_node2)
		
		text_node2 = TextNode.objects.create(text = get_text(), root=cislo_node.root)
		add_first_child(cast_node2, text_node2)
		
		cast_node3 = CastNode.objects.create(nadpis = "Druhý podproblém", root=cislo_node.root)
		add_first_child(text_node2, cast_node3)

		text_node3 = TextNode.objects.create(text = get_text(), root=cislo_node.root)
		add_first_child(cast_node3, text_node3)

		cast_node4 = CastNode.objects.create(nadpis = "Třetí podproblém", root=cislo_node.root)
		add_first_child(text_node3, cast_node4)	

		text_node4 = TextNode.objects.create(text = get_text(), root=cislo_node.root)
		add_first_child(cast_node3, text_node4)
		
		cast_node3a = CastNode.objects.create(nadpis = "Podproblém paralelní s "
					"druhým podproblémem", root=cislo_node.root)
		cast_node3.succ = cast_node3a
		cast_node3.save()

		text_node3a = TextNode.objects.create(text = get_text(), root=cislo_node.root)
		add_first_child(cast_node3a, text_node3a)

		# Občas přidáme mezičíslo
		if rnd.randint(1, 3) == 1:
			create_node_after(cislo_node, MezicisloNode, root=cislo_node.root)
			mezicislo_node = cislo_node.succ
	
			cast_node_mezicislo = CastNode.objects.create(
					nadpis = "Příspěvek k mezičíslu".format(cislo.kod), root=cislo_node.root)
			add_first_child(mezicislo_node, cast_node_mezicislo)

			odstavec = lorem.paragraph()
			text_mezicislo = Text.objects.create(na_web = odstavec, do_cisla = odstavec)
			text_node_mezicislo = TextNode.objects.create(text = text_mezicislo, root=cislo_node.root)
			add_first_child(cast_node_mezicislo, text_node_mezicislo)

	return tema

def gen_temata(rnd, rocniky, rocnik_cisla, organizatori):
	logger.info('Generuji témata...')

	jake = ["Hravé", "Fyzikální", "Nejlepší", "Totálně masakrální",
					"Šokující", "Magnetické", "Modré", "Překvapivé",
					"Plasmatické", "Novoroční"]
	co = ["téma", "záření", "stavení", "jiskření", "jelito",
					"drama", "kuře", "moře", "klání", "proudění", "čekání"]
	poc_oboru = rnd.randint(1, 2)

	rocnik_temata = []
	# Věříme, že rocnik_cisla je pole polí čísel podle ročníků, tak si necháme dát 
	# vždycky jeden ročník a k němu příslušná čísla.
	for rocnik, cisla in zip(rocniky, rocnik_cisla):
		kod = 1
		letosni_temata = []
		# Do každého ročníku vymyslíme tři (zatím) témata, v každém z prvních čísel jedno
		for zacatek_tematu in range(1, 3):
			# Vygenerujeme téma
			t = Tema.objects.create(
				# atributy třídy Problem
				nazev=" ".join([rnd.choice(jake), rnd.choice(co)]),
				stav=Problem.STAV_ZADANY,
				zamereni=rnd.sample(["M", "F", "I", "O", "B"], poc_oboru),
				autor=rnd.choice(organizatori),
				garant=rnd.choice(organizatori),
				kod=str(kod),
				# atributy třídy Téma
				tema_typ=rnd.choice(Tema.TEMA_CHOICES)[0],
				rocnik=rocnik,
				abstrakt = "Abstrakt tematka {}".format(kod)
			)
			kod += 1

			# Vymyslíme, kdy skončí
			konec_tematu = min(rnd.randint(zacatek_tematu, 7), len(cisla))

			# Vyrobíme TemaVCisleNody pro obsah
			for i in range(zacatek_tematu, konec_tematu+1):
				node = TemaVCisleNode.objects.create(tema = t,root=rocnik.rocniknode)
				# FIXME: Není to off-by-one?
				otec = cisla[i-1].cislonode
				otec_syn(otec, node)

			# Vymyslíme, kdo to bude opravovat
			poc_opravovatelu = rnd.randint(1, 3)
			t.opravovatele.set(rnd.sample(organizatori, poc_opravovatelu))

			# Uložíme všechno
			t.save()
			letosni_temata.append((zacatek_tematu, konec_tematu, t))
		rocnik_temata.append(letosni_temata)
	return rocnik_temata

def gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod, cislo, cislo_se_vzorakem):
	""" Generování úlohy k danému tématu. """
	
	# Proměnné pro náhodné generování názvů a zadání.
	jaka = ["Šachová", "Černá", "Větrná", "Dlouhá", "Křehká", "Rychlá",
		"Zákeřná", "Fyzikální"]
	co = ["kostka", "smršť", "díra", "zrada", "toulka", "tyč",
		"úloha", "blecha"]
	sloveso = ["Najděte", "Spočítejte", "Zapište", "Změřte", "Odhadněte"]
	koho = ["délku", "počet", "množství", "dílky"]
	ceho = ["všech", "správných", "konstatních", "zelených"]
	jmeno = ["řešení", "tahů", "čísel", "kalhot", "koulí", "hadů"]
	kde = ["na zemi", "ve vesmíru", "ve vzduchu", "na šňůře", "v letadle"]
	obory = ["M", "F", "I", "O", "B"]
	
	uloha = Uloha.objects.create(
		nazev=": ".join([tema.nazev, 
			"úloha {}.".format(kod)]),
		nadproblem=tema,
		stav=Problem.STAV_ZADANY,
		zamereni=tema.zamereni,
		autor=tema.autor,
		garant=tema.garant,
		kod=str(kod),
		cislo_zadani=cislo,
		cislo_reseni=cislo_se_vzorakem,
		cislo_deadline=cislo_se_vzorakem,
		max_body = rnd.randint(1, 8)
	)
	
	# Samotný obsah následně vzniklého Textu zadání
	obsah = " ".join(
		[rnd.choice(sloveso), 
		rnd.choice(koho), 
		rnd.choice(ceho), 
		rnd.choice(jmeno), 
		rnd.choice(kde)]
		)
	text_zadani = Text.objects.create(
		na_web = obsah,
		do_cisla = obsah,
		)
	zad = TextNode.objects.create(text = text_zadani, root=tema.temavcislenode_set.first().root)
	uloha_zadani = UlohaZadaniNode.objects.create(uloha=uloha, first_child = zad, root=tema.temavcislenode_set.first().root)
	uloha.ulohazadaninode = uloha_zadani

	# Generování řešení a hodnocení k úloze
	gen_reseni_ulohy(rnd, [cislo], uloha, len(resitele)//4, 1,
					 resitele, resitele)

	return uloha, uloha_zadani


def gen_ulohy_k_tematum(rnd, rocniky, rocnik_cisla, rocnik_temata, organizatori, resitele):
	logger.info('Generuji úlohy k tématům...')

	# Ke každému ročníku si vezmeme příslušná čísla a témata
	for rocnik, cisla, temata in zip(rocniky, rocnik_cisla, rocnik_temata):
		# Do každého čísla nagenerujeme ke každému témátku pár úložek
		for cislo in cisla:
			print("Generuji úložky do {}-tého čísla".format(cislo.poradi))
			# Vzorák bude o dvě čísla dál
			cislo_se_vzorakem = Cislo.objects.filter(
					rocnik=rocnik,
					poradi=str(int(cislo.poradi) + 2),
					)
			# Pokud není číslo pro vzorák, tak se dá do posledního čísla 
			# (i kdyby tam mělo být zadání i řešení...)
			# Tohle sice umožňuje vygenerovat vzorák do čísla dávno po konci témátka, 
			# ale to nám pro jednoduchost nevadí.
			if len(cislo_se_vzorakem) == 0:
				cislo_se_vzorakem = cisla[-1]
			else:
				cislo_se_vzorakem = cislo_se_vzorakem.first()

			for tema_node in all_children_of_type(cislo.cislonode, TemaVCisleNode):
				tema = tema_node.tema
					
				# Pokud už témátko skončilo, žádné úložky negenerujeme
				# FIXME: Bylo by hezčí, kdyby se čísla předávala jako Cislo a ne 
				# jako int v té trojici (start, konec, tema)
				if not temata[int(tema.kod)-1][1] >= int(cislo_se_vzorakem.poradi):
					continue
					
				# Generujeme 1 až 4 úložky k tomuto témátku do tohoto čísla.
				for kod in range(1, rnd.randint(1, 4)):
					u, uz = gen_ulohy_tematu(rnd, organizatori, resitele, tema, kod,
						cislo, cislo_se_vzorakem)

					insert_last_child(tema_node, uz)
				
					poc_op = rnd.randint(1, 4)
					uvz = gen_vzoroveho_reseni_ulohy(rnd, organizatori, 
						u, poc_op) 
					
					# Najdeme správný TemaVCisleNode pro vložení vzoráku
					res_tema_node = None;
					for node in all_children(cislo_se_vzorakem.cislonode):
						if isinstance(node, TemaVCisleNode):
							if node.tema == tema:
								res_tema_node = node
					if res_tema_node is None:
						raise LookupError("Nenalezen Node pro vložení vzoráku")
					insert_last_child(res_tema_node, uvz)
					u.save()
	return


def otec_syn(otec, syn):
	bratr = otec.first_child
	syn.succ = bratr
	otec.first_child = syn
	syn.save()
	otec.save()

def gen_clanek(rnd, organizatori, resitele):
	logger.info("Generuji článek do čísla 22.2")
	clanek = Clanek.objects.create(
		nazev="Článek o Lorem ipsum",
		nadproblem=None,
		stav='vyreseny',
		zamereni=['I'],
		garant=rnd.choice(organizatori),
		kod='cl',
		)
	clanek.save()

	reseni = Reseni.objects.create(
		zverejneno=True,
		)
	reseni.resitele.add(rnd.choice(resitele))
	reseni.save()

	cislo = Cislo.objects.get(rocnik__rocnik=22, poradi=2)
	cislonode = cislo.cislonode

	hodnoceni = Hodnoceni.objects.create(
		body=15.0,
		cislo_body=cislo,
		reseni=reseni,
		problem=clanek,
		)
	hodnoceni.save()

	reseninode = ReseniNode.objects.create(
		reseni=reseni
		)
	reseninode.save()

	# Bude to celý text
	reseni.text_cely = reseninode
	reseni.save()

	from treenode.treelib import insert_last_child, create_child
	insert_last_child(cislonode, reseninode)

	# Vyrobíme nějaký obsah
	# FIXME: Ten, kdo vymyslel TreeLib (mj. týž, kdo psal tenhle kód),
	# nevyrobil vhodnou funkci, takže to postavíme pozpátku pomocí create_child
	# (které vyrábí _prvního_ syna)
	create_child(reseninode, CastNode, nadpis="Lorem ipsum")
	# Taky ten člověk nevyrobil vracení nových věcí...
	castnode = reseninode.first_child
	
	# Úvodní odstaveček
	obsah = "Tohle je zamyšlení o textu lorem ipsum. Začneme a skončíme ukázkou."
	text = Text.objects.create(
		na_web=obsah,
		do_cisla=obsah,
		)
	text.save()
	create_child(reseninode, TextNode, text=text)

	# Několik odstavců lorem ipsum
	for _ in range(rnd.randint(3, 7)):
		lipsum = lorem.paragraph()
		text = Text.objects.create(
			na_web=lipsum,
			do_cisla=lipsum,
			)
		text.save()
		create_child(castnode, TextNode, text=text)
	logger.info(f"Článek vygenerován (reseni={reseni.id}, treenode={reseninode.id})")