# -*- encodung=utf-8 -*-

from collections import OrderedDict

from django.core.exceptions import NON_FIELD_ERRORS
from django.forms import BaseForm
from django.forms.utils import ErrorList

from django.utils.safestring import mark_safe

from .prefixdict import PrefixCombiningDict


class MultiForm(BaseForm):
	PREFIX_SEP = '-'
	VAR_PREFIX_SEP = '__'

	base_fields = {}

	def __init__(self, data=None, files=None, auto_id='id_%s', prefix=None,
		initial=None, error_class=ErrorList, label_suffix=None,
		empty_permitted=False,
		**kwargs
	):
		self.prefix_sep = self.PREFIX_SEP
		self.var_prefix_sep = self.VAR_PREFIX_SEP

		super(MultiForm, self).__init__(data=data, files=files,
			auto_id=auto_id, prefix=prefix, initial=initial, error_class=error_class, label_suffix=label_suffix,
			empty_permitted=empty_permitted
		)
		self._init_subforms(data=data, initial=initial, label_suffix=label_suffix, **kwargs)
		self.fields = PrefixCombiningDict(((prefix, form.fields) for prefix, form in self.forms.iteritems() if form.fields), sep=self.prefix_sep)

	def _init_subforms(self, data=None, files=None, auto_id='id_%s',
		prefix=None, initial=None, error_class=ErrorList, label_suffix=None,
		empty_permitted=False, **kwargs
	):
		if not prefix:
			prefix = ''

		form_classes = self.get_form_classes()
		if len(form_classes) == 0:
			raise ValueError("Need at least one form class for MultiForm")

		forms = OrderedDict()
		for key, form_class in form_classes.iteritems():
			sub_initial = initial.get(key) if initial else None
			sub_prefix = prefix + key
			sub_kwargs = {kw_key: kw_value[key] for kw_key, kw_value in kwargs.iteritems() if key in kw_value}
			forms[key] = self.create_form(
				form_class,
				data=data, files=files, auto_id=auto_id,
				prefix=sub_prefix, initial=sub_initial, error_class=error_class,
				label_suffix=label_suffix, empty_permitted=empty_permitted,
				**sub_kwargs
			)
		self.forms = forms

	def create_form(self,
		form_class,
		data=None, files=None, auto_id=None,
		prefix=None, initial=None, error_class=None,
		label_suffix=None, empty_permitted=None,
		*args, **kwargs
	):
		return form_class(
			data=data, files=files, auto_id=auto_id,
			prefix=prefix, initial=initial, error_class=error_class,
			label_suffix=label_suffix, empty_permitted=empty_permitted,
			*args, **kwargs
		)

	def full_clean(self):
		for form in self.forms.itervalues():
			if form:
				form.full_clean()
		self.cleaned_data = PrefixCombiningDict(
			((prefix, form.clean()) for prefix, form in self.forms.iteritems() if form and form.is_valid()),
			sep=self.prefix_sep
		)
		self._errors = {NON_FIELD_ERRORS: []}
		for prefix, form in (form for form in self.forms.iteritems() if form):
			for field, errors in form.errors.iteritems():
				if field == NON_FIELD_ERRORS:
					self._errors[NON_FIELD_ERRORS].extend(errors)
				else:
					self._errors[prefix + self.prefix_sep + field] = errors

		return self.cleaned_data

	def get_form_classes(self):
		try:
			return self.form_classes
		except NameError:
			raise NameError('Subclasses must set form_classes or override get_form_classes()')

	def __unicode__(self):
		return mark_safe(u"\n".join(unicode(form) for form in self.forms.itervalues()))

	def __str__(self):
		return mark_safe("\n".join(str(form) for form in self.forms.itervalues()))

	def __iter___(self):
		for name in self.fields:
			yield self[name]

	def __getitem__(self, key):
		return PrefixCombiningDict(((prefix, form) for prefix, form in self.forms.iteritems() if form), sep=self.prefix_sep)[key]

	@property
	def varfields(self):
		return PrefixCombiningDict(((prefix, form) for prefix, form in self.forms.iteritems() if form), sep=self.var_prefix_sep)

	def is_valid(self):
		self.full_clean()
		return all(form.is_valid() for form in self.forms.itervalues())


class ModelMultiForm(MultiForm):
	def create_form(self,
		form_class,
		data=None, files=None, auto_id=None,
		prefix=None, initial=None, instance=None,
		error_class=None, label_suffix=None, empty_permitted=None,
		*args, **kwargs
	):
		if instance:
			return form_class(data=data, prefix=prefix, initial=initial, instance=instance, *args, **kwargs)
		else:
			return form_class(data=data, prefix=prefix, initial=initial, *args, **kwargs)

	@property
	def instance(self):
		return dict((prefix, form.instance) for prefix, form in self.forms.iteritems() if form)

	def save(self, commit=True):
		return dict((key, form.save(commit=commit)) for key, form in self.forms.iteritems() if form)