# -*- encoding: utf-8 -*-
from django import forms
from django.utils.translation import ugettext as _
from django.http import Http404

from drill.forms import GameSettings, Question
from drill.models import Word, WordTranslation, Tag, Form

from django.db.models import Q, Count

from gamesettings.yamltools import yget

import settings as S

yfile = S.GAME_SETTINGS


class YAMLSettings(object):
	
	def get(self, path):
		return yget(self.yamldict, path)

	def toGetTextTuples(self, opts):
		return [(a, _(b)) for a, b in opts]


	def taxonomies(self):
		taxes = self.yamldict['taxonomy']
		choices = {}
		taxonomies = {}
		if taxes:
			for tax, opts in taxes.items():
				proc_opts = self.toGetTextTuples(opts['choices'])
				self.__setattr__(tax, proc_opts)
				taxonomies[tax] = proc_opts
		else:
			taxonomies = False
			choices = False

		self.choices = choices
		self.taxonomies = taxonomies

	def questionAnswer(self):
		QAs = self.yamldict['questions']
		
		self.Q_CHOICES = [(a['question']['abbr'], a['question']['name']) for a in QAs]
		# QA = []
		# for item in QAs:
			# pass
	
	def question(self, abbr):
		Qs = self.yamldict['questions']

		for question in Qs:
			if question['question']['abbr'] == abbr:
				return question

		return False

	def __init__(self, fname, gamename):
		
		import yaml
		with open(fname, 'r') as F:
			data = F.read()
			config_data = yaml.load(data)

		self.yamldict = config_data[gamename] or False
		self.taxonomies()
		self.questionAnswer()
		
		# parse YAML
		# question sets
		# taxonomies (create objects to set forms) 

class MorfaSettings(GameSettings):
	""" Morfa settings form.

		The idea here is to subclass this for the varying Morfa games.
		Subclasses, MorfaV, MorfaS, etc., should have fields for forms, and
		the following methods.

			.makeQuery() - constructs Q() & Q() type queries to filter
						   words and tags.
						   must set self.query
						   optionally self.tag_query

		MorfaSettings then produces the initial data for formsets based
		on this query.

	"""
	
	select = {'widget': forms.Select}
	
	def __init__(self, *args, **kwargs):
		gametype = type(self).__name__ # MorfaS
		self.yaml = YAMLSettings(yfile, gametype)
		
		preset_fields = {}
		preset_fields['questiontype'] = forms.ChoiceField(initial=self.yaml.Q_CHOICES[0][0], 
															choices=self.yaml.Q_CHOICES, 
															**self.select)

		if self.yaml.taxonomies:
			for tax, opts in self.yaml.taxonomies.items():
				t = self.yaml.get('taxonomy/%s' % tax)
				
				if 'choices' in t:
					fieldtype = forms.ChoiceField
				
				preset_fields[tax] = fieldtype(initial=opts[0][0], 
												choices=opts, 
												**self.select)

				# self.__setattr__(tax, field)
		kwargs['preset_fields'] = preset_fields
		super(MorfaSettings, self).__init__(*args, **kwargs)

	def makeQuery(self):
		""" Produces the query for nouns, setting self.query and self.tag_query
			for use by self.getInitialData.
		"""
		if self.is_valid():
			options = self.cleaned_data
		else:
			options = self.initial_data
		
		self.question = self.yaml.question(abbr=options['questiontype'])

		if self.yaml.taxonomies:
			for tax, opts in self.yaml.taxonomies.items():
				t = self.yaml.get('taxonomy/%s' % tax)
				qkwargs = {}
				if tax in options:
					qkwargs[t['attr']] = options[tax]
		else:
			qkwargs = {}

		QUERY = Q(**qkwargs)
		
		question_items = self.question['question'] or False
		answer_items = self.question['answer'] or False
		
		if question_items:
			filters = question_items['filters']
			QUERY_Q = QUERY & Q(**filters)

		if answer_items:
			filters = answer_items['filters']
			QUERY_A = QUERY & Q(**filters)

		# print 'QUERY: ', QUERY

		self.query_q = QUERY_Q
		self.query_a = QUERY_A
	
	def getPronoun(self, tag):
		return self.pronouns[tag.personnumber] or False
	

	def getInitialData(self):
		str_to_models = {
			'Form': Form,
			'Tag': Tag,
			'Word': Word,
		}

		if 'pronouns' in self.question['answer']:
			p = self.question['answer']['pronouns']
			if p:
				pron_path = self.question['answer']['pronouns']
				self.pronouns = self.yaml.get(pron_path)
			else:
				self.pronouns = False
		else:
			self.pronouns = False
		
		model = str_to_models[self.yaml.get('data_model') or False] or False
		
		if model:
			if self.query_q:
				QUERY = self.query_q
			else:
				QUERY = self.query_a
			objects = model.objects.filter(QUERY)
		
		if objects.count() < 1:
			error = "Morfa.get_db_info: Database is improperly loaded.\
					 No tags for the query were found.\n\n"
			error += "Query: %s" % QUERY
			raise model.DoesNotExist(error)
		
		# TODO: max number here should come from global setting
		random_forms = objects.order_by('?')[0:5]
		
		inits = []
		for form in random_forms:
			try:
				# TODO: NOMPL doesn't work with this.
				# TODO: questions for noun in PL always.
				if self.query_q:
					forms = form.word.form_set.filter(self.query_q)
					question_form = forms.order_by('?')[0]
				else:
					question_form = form.getBaseform(match_num=True)
				
			except Form.DoesNotExist:
				question_form = form.word.lemma
			
			question_lemma = question_form.fullform.lower()
		
			# TODO: better handling of non-generated forms
			try:
				forms = form.word.form_set.filter(self.query_a)
				
				answer_form = forms.order_by('?')
				if answer_form.count() == 0:
					raise Http404('Question type configured wrong, or forms do not\
									exist for query.\n\
									\n\
									Query: %s' % self.query_a)
				else:
					answer_form = answer_form[0]
				answer_form = answer_form.matchNumber(question_form)[0]
			except Form.DoesNotExist:
				answer_form = form.word.lemma

			answer_lemma = answer_form.fullform.lower()
			
			app = {'id': form.word.id,
					'lemma': question_lemma, # form.word.lemma,
					'translations': [answer_lemma],
					}
			if self.pronouns:
				app['pronoun'] = self.getPronoun(answer_form.tag)
			
			inits.append(app)

		return inits


class MorfaS(MorfaSettings):
	""" Morfa for substantive/nouns
		Fields for case, syllables and book.
	"""
	POS = "N"

class MorfaV(MorfaSettings):
	POS = "V"

class MorfaNum(MorfaSettings):
	POS = "Num"

class MorfaAdj(MorfaSettings):
	POS = "Adj"

### class MorfaNum(MorfaSettings):
### 	""" Placeholder for stuff for MorfaNum
### 		TODO: Numra stuff.
### 	"""
### 	POS = 'Num'
### 	# num_bare = forms.ChoiceField(initial='N-ILL', choices=NUM_BARE_CHOICES, **select) 
### 	# num_level = forms.ChoiceField(initial='1', choices=NUM_LEVEL_CHOICES, **select) 
### 	# num_context = forms.ChoiceField(initial='NUM-ATTR', choices=NUM_CONTEXT_CHOICES, **select) 
### 
### 	def makeQuery(self):
### 		num_bare = options['num_bare']
### 		num_level = options['num_level']
### 		
### 		if self.is_valid():
### 			options = self.cleaned_data
### 		else:
### 			options = self.initial_data
### 		
### 		_POS = Q(pos=self.POS)
### 
### 		if options.has_key('num_level') and str(options['num_level']) == "1":
### 			smallnum = ["1","2","3","4","5","6","7","8","9","10"]
### 			QUERY = Q(pos__iexact=pos) & Q(presentationform__in=smallnum)
### 		else:
### 			QUERY = Q(pos__iexact=pos)
### 		return
### 
### 	def getInitialData(self):
### 		""" Numra probably needs its own special subclass, since it actually uses the analyzers
### 			to generate data.
### 			
### 			For bonus brownie points we could cache all of the stuff it generates so it gets
### 			faster with use. """
### 		pass
### 	
### 
### 
### class MorfaAdj(MorfaSettings):
### 	""" Stuff for MorfaAdj
### 	"""
### 	POS = 'A'
### 	select = {'widget': forms.Select}
### 	hide = {'widget': forms.HiddenInput()}
### 	
### 	casetable = {
### 		'NOMPL' : 'Nom', 
### 	#	'ATTR': 'Attr', 
### 		'N-GEN': 'Gen',
### 		'N-PAR': 'Par',
### 		'N-ILL': 'Ill',
### 		'N-ELA': 'Ela',
### 		'N-INE': 'Ine',
### 		'N-ALL': 'All',
### 		'N-ABL': 'Abl',
### 		'N-ADE': 'Ade',
### 		'N-TRA': 'Tra',
### 		'N-ESS': 'Ess',
### 		'N-ABE': 'Abe',
### 		'': ''
### 	}
### 	
### 	# adjcase = forms.ChoiceField(initial='N-PAR', choices=ADJCASE_CHOICES, **select)
### 	# adj_context = forms.ChoiceField(initial='N-PAR', choices=ADJ_CONTEXT_CHOICES, **select)
### 	# grade = forms.ChoiceField(initial='POS', choices=GRADE_CHOICES,  **select)
### 
### 	def makeQuery(self):
### 		if self.is_valid():
### 			options = self.cleaned_data
### 		else:
### 			options = self.initial_data
### 		
### 		case = options['adjcase']
### 		adj_context = options['adj_context']
### 		grade = options['grade']
### 		
### 		if case == "Attr":
### 			attributive = "Attr"
### 			case = ""
### 		
### 		if grade == "POS":	 grade = "Pos"
### 		if grade == "COMP":	grade = "Comp"
### 		if grade == "SUPERL":  grade = "Superl"
### 		
### 		_GRADE = Q(grade=grade)
### 		
### 		## 
### 		_POS = Q(pos=self.POS)
### 		_POSS = Q(possessive="")
### 
### 		# syll = ''
### 		# if 'bisyllabic' in options:
### 		# 	syll = 'bisyllabic'
### 		# elif 'trisyllabic' in options:
### 		# 	syll = 'trisyllabic'
### 		# elif 'contracted' in options:
### 		# 	syll = 'contracted'
### 
### 		# _SYLL = Q(stem__in=syll)
### 		
### 		# if 'book' in options:
### 		# 	book = options['book']
### 		# else:
### 		# 	book = ''
### 		# 
### 		# _SOURCE = Q(source__name=book)
### 
### 		if 'adjcase' in options:
### 			case = self.casetable[options['adjcase']]
### 		_CASE = Q(case=case)
### 
### 		numbers = ["Sg", "Pl", ""]
### 		if case == "Nom": numbers = ["Pl"]
### 		_NUMBER = Q(number__in=numbers)
### 
### 		TAG_QUERY = _POS & _POSS & _CASE & _NUMBER & _GRADE	
### 		QUERY = _POS # & _SYLL
### 
### 		# if book and book not in ['all', 'All', '']:
### 		# 	QUERY = QUERY & _SOURCE
### 		
### 		self.query = QUERY
### 		self.tag_query = TAG_QUERY


class MorfaQuestion(Question):
	""" Creates a Morfa question. Most of the functionality here is in parent
		classes, except for answer validation (check_answer).
		
	"""
	
	def check_answer(self):
		""" Gets analyses based on forms supplied and settingsform and compares
			to user input

			This question should be used in a Formset.
		"""
		# TODO: Spell relax
		answers = self.cleaned_data.get("answers")
		user_answer = self.cleaned_data.get("userans")
		if answers:
			if user_answer not in answers:
				self._errors["userans"] = self.Errors.try_again
				self.correct = False
			else:
				self.correct = True
		else:
			self._errors = self.Errors.empty_answers

		if not user_answer:
			self._errors["userans"] = self.Errors.blank_user_answer

		return
	


