# coding=utf-8
from django import forms
from django.conf import settings
from django.utils.safestring import mark_safe

from django.contrib import admin
#from django.contrib.admin.util import unquote, flatten_fieldsets, get_deleted_objects, model_format_dict
#from django import forms, template
#from django.shortcuts import get_object_or_404, render_to_response

from django.db import models

import operator
from django.http import HttpResponse, HttpResponseNotFound
from django.db.models.query import QuerySet
from django.utils.encoding import smart_str

from django.contrib.admin.widgets import RelatedFieldWidgetWrapper
from django.contrib.admin.options import InlineModelAdmin

from django.utils.translation import ugettext as _
from django.utils.encoding import force_unicode
from django.utils.html import escape
from django.utils.safestring import mark_safe, SafeUnicode
from django.utils.datastructures import MultiValueDict, MergeDict
from django.http import HttpResponseRedirect

from functools import update_wrapper

#from django.utils.decorators import method_decorator
#from django.views.decorators.csrf import csrf_protect
#from django.db import models, transaction, router
#csrf_protect_m = method_decorator( csrf_protect )

media_css={ 'all': ( '%sjquery.autocomplete.css'%settings.STATIC_URL, ) }
media_js=( '%sadmin/js/jquery.js'%settings.STATIC_URL,
           '%sadmin/js/jquery.autocomplete.js'%settings.STATIC_URL, )


class ForeignKeySearchInput( forms.HiddenInput ):
	"""
	A Widget for displaying ForeignKeys in an autocomplete search input 
	instead in a <select> box.
	"""
	class Media:
		css=media_css
		js=media_js

	input_type='hidden'		# чтобы не отрисовывать текстовое поле
	is_hidden=False			# Чтобы отрисовывать заголовок в Inline

	def label_for_value( self, value ):
		rel_name=self.search_fields[0].split( '__' )[0]

		key=self.rel.get_related_field().name
		obj=self.rel.to._default_manager.get( **{key: value} )

		return getattr( obj, rel_name )

	def __init__( self, rel, search_fields, attrs = None ):
		self.rel=rel
		self.search_fields=search_fields
		super( ForeignKeySearchInput, self ).__init__( attrs )

	def render( self, name, value, attrs = None ):
		if attrs is None:
			attrs={}
		rendered=super( ForeignKeySearchInput, self ).render( name, value, attrs )
		if value:
			label=self.label_for_value( value )
		else:
			label=u''

		return SafeUnicode( rendered+u'''
			<input type="text" id="lookup_%(name)s" value="%(label)s" size="40" search_fields="%(search_fields)s" app_label="%(app_label)s" model_name="%(model_name)s" autocomplete_mode="ForeignKey" class="to_autocomplete"/>
			<a class="deletelink" title="Delete" onclick="autocomplete_delete(this)">&nbsp;</a>
		'''%{
			'search_fields': ','.join( self.search_fields ),
			'STATIC_URL': settings.STATIC_URL,
			'model_name': self.rel.to._meta.module_name,
			'app_label': self.rel.to._meta.app_label,
			'label': label,
			'name': name,
			'value': value,
		}
		)

class ManyToManySearchInput( forms.MultipleHiddenInput ):
	"""
	A Widget for displaying ForeignKeys in an autocomplete search input 
	instead in a <select> box.
	"""
	class Media:
		css=media_css
		js=media_js

	input_type='hidden'		# чтобы не отрисовывать текстовое поле
	is_hidden=False			# Чтобы отрисовывать заголовок в Inline

	def __init__( self, rel, search_fields, attrs = None ):
		self.rel=rel
		self.search_fields=search_fields
		super( ManyToManySearchInput, self ).__init__( attrs )
		self.help_text=u""

	def value_from_datadict( self, data, files, name ):
		if isinstance( data, ( MultiValueDict, MergeDict ) ):
			res=data.getlist( name )
		else:
			res=data.get( name, None )

		return res

	def render( self, name, value, attrs = None ):
		if attrs is None:
			attrs={}

		if value is None:
 			value=[]

		label=''
		selected=''
		rel_name=self.search_fields[0].split( '__' )[0]

		for id in value:
			obj=self.rel.to.objects.get( pk = id )

			selected=selected+mark_safe( u"""
				<div class="to_delete deletelink" ><input type="hidden" name="%(name)s" value="%(value)s"/>%(label)s</div>"""
				)%{
					'label': getattr( obj, rel_name ),
					'name': name,
					'value': obj.id,
		}


		return mark_safe( u'''
			<input type="text" id="lookup_%(name)s" value="" size="40" search_fields="%(search_fields)s" app_label="%(app_label)s" model_name="%(model_name)s" autocomplete_mode="ManyToMany"  class="to_autocomplete"/>%(label)s
			<div style="float:left; padding-left:105px; width:300px;">
			<font  style="color:#999999;font-size:10px !important;">%(help_text)s</font>
			<div id="box_%(name)s" style="padding-left:20px;cursor:pointer;">
				%(selected)s
			</div></div>
		''' )%{
			'search_fields': ','.join( self.search_fields ),
			'model_name': self.rel.to._meta.module_name,
			'app_label': self.rel.to._meta.app_label,
			'label': label,
			'name': name,
			'value': value,
			'selected':selected,
			'help_text':self.help_text
		}

class AutocompleteModelAdmin( admin.ModelAdmin ):
	def __call__( self, request, url ):
		if url is None:
			pass
		elif url=='search':
			return self.search( request )
		return super( AutocompleteModelAdmin, self ).__call__( request, url )


	def get_urls( self ):
		from django.conf.urls import patterns, url

		def wrap( view ):
			def wrapper( *args, **kwargs ):
				return self.admin_site.admin_view( view )( *args, **kwargs )
			return update_wrapper( wrapper, view )

		info=self.model._meta.app_label, self.model._meta.module_name

		urlpatterns=patterns( '',
			url( r'^$', 			wrap( self.changelist_view ), name = '%s_%s_changelist'%info ),
			url( r'^add/$', 		wrap( self.add_view ), 		name = '%s_%s_add'%info ),
			url( r'^(.+)/history/$', wrap( self.history_view ), name = '%s_%s_history'%info ),
			url( r'^(.+)/delete/$', wrap( self.delete_view ), 	name = '%s_%s_delete'%info ),
			url( r'^search/$', 		wrap( self.search_view ), 	name = '%s_%s_search'%info ),
			url( r'^(.+)/$', 		wrap( self.change_view ), 	name = '%s_%s_change'%info ),
		)
		return urlpatterns

	def search_view( self, request ):

		#	Searches in the fields of the given related model and returns the 
		#	result as a simple string to be used by the jQuery Autocomplete plugin

		query=request.GET.get( 'q', None )
		app_label=request.GET.get( 'app_label', None )
		model_name=request.GET.get( 'model_name', None )
		search_fields=request.GET.get( 'search_fields', None )

		if search_fields and app_label and model_name and query:
			def construct_search( field_name ):
				# use different lookup methods depending on the notation
				if field_name.startswith( '^' ):
					return "%s__istartswith"%field_name[1:], field_name[1:]
				elif field_name.startswith( '=' ):
					return "%s__iexact"%field_name[1:], field_name[1:]
				elif field_name.startswith( '@' ):
					return "%s__search"%field_name[1:], field_name[1:]
				else:
					return "%s__icontains"%field_name, field_name

			model=models.get_model( app_label, model_name )
			q=None
			fields=[]
			for field_name in search_fields.split( ',' ):
				name, name1=construct_search( field_name )
				fields.append( name1 )

				if q:
					q=q|models.Q( **{str( name ):query} )
				else:
					q=models.Q( **{str( name ):query} )

			qs=model.objects.filter( q )


			data=''
			for f in qs[0:10]:
				res=[]
				for field in fields:

					parts=field.split( '__' )
					if len( parts )>1:
						s='%s'%getattr( getattr( f, parts[0] ), parts[1] )
					else:
						s='%s'%getattr( f, parts[0] )
					if s and s not in( res ):
						res.append( s )
				data+=u'%s|%s\n'%( ', '.join( res ), f.pk )
			return HttpResponse( data )

			rel_name=field_name.split( '__' )[0]

			data=''.join( [u'%s|%s\n'%( getattr( f, rel_name ), f.pk ) for f in qs] )
			return HttpResponse( data )
		return HttpResponseNotFound()

	def formfield_for_dbfield( self, db_field, **kwargs ):
		# For ForeignKey use a special Autocomplete widget.
		if isinstance( db_field, models.ForeignKey ) and db_field.name in self.related_search_fields:
			kwargs['widget']=ForeignKeySearchInput( db_field.rel,
									self.related_search_fields[db_field.name] )

			# extra HTML to the end of the rendered output.
			if 'request' in kwargs.keys():
				kwargs.pop( 'request' )

			formfield=db_field.formfield( **kwargs )
			# Don't wrap raw_id fields. Their add function is in the popup window.
			if not db_field.name in self.raw_id_fields:
				# formfield can be None if it came from a OneToOneField with
				# parent_link=True
				if formfield is not None:
					formfield.widget=AutocompleteWidgetWrapper( formfield.widget, db_field.rel, self.admin_site )
			return formfield

		# For ManyToManyField use a special Autocomplete widget.
		if isinstance( db_field, models.ManyToManyField )and db_field.name in self.related_search_fields:
			kwargs['widget']=ManyToManySearchInput( db_field.rel,
									self.related_search_fields[db_field.name] )
			db_field.help_text=''

			# extra HTML to the end of the rendered output.
			if 'request' in kwargs.keys():
				kwargs.pop( 'request' )

			formfield=db_field.formfield( **kwargs )
			# Don't wrap raw_id fields. Their add function is in the popup window.
			if not db_field.name in self.raw_id_fields:
				# formfield can be None if it came from a OneToOneField with
				# parent_link=True
				if formfield is not None:
					formfield.widget=AutocompleteWidgetWrapper( formfield.widget, db_field.rel, self.admin_site )
			return formfield

		return super( AutocompleteModelAdmin, self ).formfield_for_dbfield( db_field, **kwargs )


class AutocompleteWidgetWrapper( RelatedFieldWidgetWrapper ):
	def render( self, name, value, *args, **kwargs ):
		rel_to=self.rel.to
		related_url='../../../%s/%s/'%( rel_to._meta.app_label, rel_to._meta.object_name.lower() )
		self.widget.choices=self.choices
		output=[self.widget.render( name, value, *args, **kwargs )]
		if rel_to in self.admin_site._registry: # If the related object has an admin interface:
			# TODO: "id_" is hard-coded here. This should instead use the correct
			# API to determine the ID dynamically.
			output.append( u'<a href="%sadd/" class="add-another" id="add_id_%s" onclick="return showAutocompletePopup(this);"> '%\
				( related_url, name ) )
			output.append( u'<img src="%simg/admin/icon_addlink.gif" width="10" height="10" alt="%s"/></a>'%( settings.ADMIN_MEDIA_PREFIX, _( 'Add Another' ) ) )
		return mark_safe( u''.join( output ) )


class AutocompleteInlineModelAdmin( InlineModelAdmin ):

	def formfield_for_dbfield( self, db_field, **kwargs ):
		# For ForeignKey use a special Autocomplete widget.
		if isinstance( db_field, models.ForeignKey ) and db_field.name in self.related_search_fields:
			kwargs['widget']=ForeignKeySearchInput( db_field.rel,
									self.related_search_fields[db_field.name] )

			# extra HTML to the end of the rendered output.
			if 'request' in kwargs.keys():
				kwargs.pop( 'request' )

			formfield=db_field.formfield( **kwargs )
			# Don't wrap raw_id fields. Their add function is in the popup window.
			if not db_field.name in self.raw_id_fields:
				# formfield can be None if it came from a OneToOneField with

				if formfield is not None:
					formfield.widget=AutocompleteWidgetWrapper( formfield.widget, db_field.rel, self.admin_site )
			return formfield

		# For ManyToManyField use a special Autocomplete widget.
		if isinstance( db_field, models.ManyToManyField )and db_field.name in self.related_search_fields:
			kwargs['widget']=ManyToManySearchInput( db_field.rel,
									self.related_search_fields[db_field.name] )
			db_field.help_text=''

			# extra HTML to the end of the rendered output.
			if 'request' in kwargs.keys():
				kwargs.pop( 'request' )

			formfield=db_field.formfield( **kwargs )
			# Don't wrap raw_id fields. Their add function is in the popup window.
			if not db_field.name in self.raw_id_fields:
				# formfield can be None if it came from a OneToOneField with
				# parent_link=True
				if formfield is not None:
					formfield.widget=AutocompleteWidgetWrapper( formfield.widget, db_field.rel, self.admin_site )
			return formfield
		return super( AutocompleteInlineModelAdmin, self ).formfield_for_dbfield( db_field, **kwargs )


class AutocompleteStackedInline( AutocompleteInlineModelAdmin ):
	template='admin/edit_inline/stacked.html'

class AutocompleteTabularInline( AutocompleteInlineModelAdmin ):
	template='admin/edit_inline/tabular.html'




class WildModelSearchInput( forms.HiddenInput ):
	"""
	A Widget for displaying ForeignKeys in an autocomplete search input 
	instead in a <select> box.
	"""
	class Media:
		css=media_css
		js=media_js

	input_type='hidden'
	is_hidden=False

	def label_for_value( self, value ):
		model=models.get_model( self.app_label, self.model_name )
		try:
			return model.objects.get( pk = value )
		except:
			return None

	def __init__( self, app_label, model_name, search_fields, attrs = None ):
		self.search_fields=search_fields
		self.app_label=app_label
		self.model_name=model_name
		super( WildModelSearchInput, self ).__init__( attrs )

	def render( self, name, value, attrs = None ):
		if attrs is None:
			attrs={}
		rendered=super( WildModelSearchInput, self ).render( name, value, attrs )
		if value:
			label=self.label_for_value( value )
		else:
			label=u''
		return rendered+mark_safe( u'''
			<input type="text" id="lookup_%(name)s" value="%(label)s" size="40" search_fields="%(search_fields)s" app_label="%(app_label)s" model_name="%(model_name)s" autocomplete_mode="ForeignKey"  class="to_autocomplete"/>
		'''%{
			'search_fields': ','.join( self.search_fields ),
			'STATIC_URL': settings.STATIC_URL,
			'model_name': self.model_name,
			'app_label': self.app_label,
			'label': label,
			'name': name,
			'value': value,
		} )
