# (c) Roland van Laar 2006

from django.db import models
from django.http import HttpResponse, HttpResponseRedirect
from django.template import Context, loader
from django.shortcuts import render_to_response
from django.core.urlresolvers import reverse, NoReverseMatch
from django.forms.formsets import formset_factory
from django import forms
from socket import gethostname 

from exodus.models import *
from exodus.wllogic import free_masterip, newSSIDName, addInterlinkIP, \
		freeInterlinkIP, freePublicAPIP

from exodus.utils import pdebug

class GenericHandler(object):
	"""Conventions used: type has to to the same name as the dependency 
	object, delet template is named delete<type>.html
	"""
	def __init__(self, request, mode):
		pdebug(100, "Function: GenericHandler.__init__")
		# Strip 'Handler' of name
		type = self.__class__.__name__[:-7:]
		formClass = eval(type + 'Form')

		self.object = eval('self.' + type[0].lower() + type[1::])
		self.title = self.object._meta.verbose_name
		self.request = request
		self.mode = mode
		self.is_changed = {}
		
		if request.POST.has_key('cancel'):
			pdebug(100, "Action cancel")
			self.form_action = 'cancel'
			# Allow overriding cancel call, in particular with regards to the
			# response call
			self._cancel()
		elif request.POST.has_key('proceed'):
			pdebug(100, "Action proceed")
			self.form_action = 'proceed'
			
			# Delete does not require a valid form
			if mode == 'delete':
				self._delete()
			else:
				# First checking whether form is valid, then add/edit actions
				self.form = formClass(request.POST, instance=self.object)
				try:
					if self.form.is_valid():
						pdebug(100, "Form valid")
						# Set response on forehand, to allow override \
						# in procedure
						try:
							self.response = HttpResponseRedirect(reverse( \
								'exodus.views.viewNode', args=[self.node.name]))
						except (AttributeError, NoReverseMatch):
							self.response = HttpResponseRedirect(reverse( \
								'exodus.views.viewNodelist'))
						
						#Checking whether data did change
						_oldInstance = dict(self.object.as_list())
						_instance  = dict(self.form.save(commit=False).as_list())
						for key,value in _instance.items():
							if value != _oldInstance[key]:
								pdebug(100, "Key %s changed value '%s' -> '%s'"\
									% (key, _oldInstance[key], value))
								self.is_changed[key] = value
						
						# Call override procedure
						if mode == 'add':
							self._add()
						elif mode == 'edit':
							self._edit()
						else:
							raise ValueError, 'Mode "%s" not defined' % mode
					else:
						raise ValueError, 'Form error, please edit and resubmit'
				except ValueError, message:
					self.response = render_to_response('genericForm.html', {
						'form': self.form, 'message' : message, 
						'title' : self.title, 'mode' : mode, 
						'type' : type, 'object': self.object,
						'delInclude' : "delete" + type.capitalize() + ".html",
						'addInclude' : "add" + type.capitalize() + ".html",
						'editInclude' : "edit" + type.capitalize() + ".html" })
		else:
			message = 'Please edit and submit'
			
			# Dirty? hack to allow initial form to be filled with date \
			# for GET request, no errors raised
			if request.GET and mode == 'add':
				self.form = formClass(request.GET, instance=self.object)
				self.form._errors = {}
			else:
				self.form = formClass(instance=self.object)
			
			self.response = render_to_response('genericForm.html', {
				'form': self.form, 'message' : message, 'title' : self.title,
				'mode' : mode, 'type' : type, 'object': self.object,
				'delInclude' : "delete" + type.capitalize() + ".html",
				'addInclude' : "add" + type.capitalize() + ".html",
				'editInclude' : "edit" + type.capitalize() + ".html" })
	
	def _add(self):
		pdebug(100, "Function: GenericHandler._add")
		self.form.save()
	
	def _edit(self):
		pdebug(100, "Function: GenericHandler._edit")
		self.form.save()
	
	def _delete(self):
		pdebug(100, "Function: GenericHandler._delete")
		self.object.delete()
		self.response = HttpResponseRedirect(reverse('exodus.views.viewNode',\
				args=[self.node.name]))
	
	def _cancel(self):
		pdebug(100, "Function: GenericHandler._cancel")
		self.response = HttpResponseRedirect(reverse('exodus.views.viewNode',\
				 args=[self.node.name]))
		#self.response = HttpResponseRedirect(reverse('exodus.views.viewNodelist'))
	
	def render_to_response(self):
		pdebug(100, "Function: GenericHandler.render_to_response")
		return self.response


#
# PublicAP
class PublicAPForm(forms.ModelForm):
	class Meta:
		model = PublicAP
		exclude = ('shortdesc', 'desc', 'ip', 'dhcpstart', 'dhcpstop')

class PublicAPHandler(GenericHandler):

	def __init__(self, request, node, interface, publicAP, mode):
		pdebug(100, "Function: PublicAPHandler.__init__")
		self.node = Node.objects.get(name=node)
		self.interface = Interface.objects.get(node=self.node, iface=interface)
		if mode == 'add':
			self.publicAP = PublicAP(iface=self.interface)
		else:
			self.publicAP =  PublicAP.objects.get(iface=self.interface, \
					pk=publicAP)
		super(PublicAPHandler, self).__init__(request, mode)

	def _add(self):
		pdebug(100, "Function: PublicAPHandler._add")
		_instance = self.form.save(commit=False)
		# Find IP range inside interface range with disired size/subnet
		_instance.ip = freePublicAPIP(_instance.iface, _instance.netmask)
		_instance.dhcpstart = 1
		_instance.dhcpstop = 2
		# If wireless generate ssid name
		_instance.ssid = newSSIDName(_instance.iface.node, _instance.iface, \
				'omni')
		_instance.save()

def genericPublicAP(request, node, interface, publicAP, mode):
	pdebug(100, "Function: genericPublicAP")
	handler = PublicAPHandler(request, node, interface, publicAP, mode)
	return handler.render_to_response()

#
# Interface 
class InterfaceForm(forms.ModelForm):
	class Meta:
		model = Interface
		exclude = ( 'ip', 'ssid', 'mode', 'channel', 'shortdesc', \
				'netmask' )

class InterfaceHandler(GenericHandler):
	def __init__(self, request, node, interface, mode):
		pdebug(100, "Function: InterfaceHandler.__init__")
		self.node = Node.objects.get(name=node)
		if mode == 'add':
			self.interface = Interface(node=self.node)
		else:
			self.interface = Interface.objects.get(node=self.node, \
				iface=interface)
		super(InterfaceHandler, self).__init__(request, mode)

	def _add(self):
		pdebug(100, "Function: InterfaceHandler._add")
		self._saveInterface()

	def _edit(self):
		pdebug(100, "Function: InterfaceHandler._edit")
		self._saveInterface()

	def _saveInterface(self):
		pdebug(100, "Function: InterfaceHandler._saveInterface")
		_instance  = self.form.save(commit=False)
		if _instance.link and (_instance.type != _instance.link.type):
			raise ValueError,'Type of local and remote interface needs to match'
		if str(_instance.type) != "eth":
			_instance.ssid = newSSIDName(_instance.node, _instance.iface, \
					'unused')
			_instance.channel = '1'
			_instance.mode = 1 # set to master
		
		# Only change IP if changes in interface link/mask or new of course :-)
		if self.mode == 'add' or self.is_changed.has_key('link') or \
				self.is_changed.has_key('netmask'):
			if not _instance.link:
				_instance.ip = freeInterlinkIP(_instance)
			else:
				_instance.ip = addInterlinkIP(_instance.link)

		# XXX: Change in netmask requires full range of netmask changes \
		# on slaves
		_instance.save()
		#Dirty to hack to get reference to self working
		if not _instance.link:
			_instance.link = _instance
			_instance.save()

def genericInterface(request, node, interface, mode):
	pdebug(100, "Function: genericInterface")
	handler = InterfaceHandler(request, node, interface, mode)
	return handler.render_to_response()

#
# Node
class NodeForm(forms.ModelForm):
	class Meta:
		model = Node
		exclude = ( 'masterip' )

class NodeHandler(GenericHandler):
	def __init__(self, request, node, mode):
		pdebug(100, "Function: NodeHandler.__init__")
		if mode == 'add':
			self.node = Node()
		else:
			self.node = Node.objects.get(name=node)
		super(NodeHandler, self).__init__(request, mode)

	def _add(self):
		pdebug(100, "Function: NodeHandler._add")
		# input a valid master ip into new_data
		_instance  = self.form.save(commit=False)
		_instance.masterip = free_masterip(_instance.network)
		_instance.save()
		self.response = HttpResponseRedirect(reverse('exodus.views.viewNode', \
				args=[_instance.name]))

	def _delete(self):
		pdebug(100, "Function: NodeHandler._delete")
		for _master in Interface.objects.filter(node=self.object):
			if _master.link == _master:
				for _makeMaster in Interface.objects.filter(link=_master):
					_makeMaster.link = _makeMaster
					_makeMaster.save()
		self.object.delete()
		# As node is deleted, goto overview page
		self.response = HttpResponseRedirect(reverse( \
					'exodus.views.viewNodelist'))

	def _cancel(self):
		pdebug(100, "Function: NodeHandler._cancel")
		if self.mode == 'new':
			self.response = HttpResponseRedirect(reverse( \
					'exodus.views.viewNodelist'))
		else:
			self.response = HttpResponseRedirect(reverse( \
					'exodus.views.viewNode', args=[self.node.name]))

def genericNode(request, node, mode):
	pdebug(100, "Function: genericNode")
	handler = NodeHandler(request, node, mode)
	return handler.render_to_response()

#
# location
class LocationForm(forms.ModelForm):
	class Meta:
		model = Location

class LocationHandler(GenericHandler):
	def __init__(self, request, location, mode):
		pdebug(100, "Function: LocationHandler.__init__")
		if mode == 'add':
			self.location = Location()
		else:
			self.location = Location.objects.get(description=location)
	
		super(LocationHandler,self).__init__(request, mode)

	def _add(self):
		pdebug(100, "Function: LocationHandler._add")
		_instance = self.form.save()
		# After adding a location, allow adding a Node with this location
		self.response = HttpResponseRedirect( \
				reverse('exodus.views.genericNode', args=["add", "new"]) + \
				"?location=%i" % _instance.pk)

	def _delete(self):
		pdebug(100, "Function: LocationHandler._delete")
		self.object.delete()
		self.response = HttpResponseRedirect(reverse( \
				'exodus.views.viewNodelist'))

	def _cancel(self):
		pdebug(100, "Function: LocationHandler._cancel")
		self.response = HttpResponseRedirect(reverse( \
				'exodus.views.viewNodelist'))

#
# Views
def viewNode(request, node):
	pdebug(100, "Function: viewNode")
	node = Node.objects.get(name=node)
	return render_to_response('viewNode.html', {'node': node})

def viewNodelist(request):
	pdebug(100, "Function: viewNodelist")
	nodes = Node.objects.all()
	configFiles = (
		'rc.local',
		'rc.node.local',
		'dhcpd.conf',
		'named.conf',
		'resolv.conf' )
	return render_to_response('viewNodelist.html', {'nodes' : nodes, \
			'configFiles' : configFiles})

def configFile(request, version, node, file):
	pdebug(100, "Function: configFile")
	node = Node.objects.get(name=node)
	
	# Extra statictics information for use of generation
	server = {}
	server['host'] = gethostname()
	
	templateFile = version + '/' + file
	return render_to_response(templateFile, {'node' : node, 'server' : server},\
			mimetype='text/plain')

#
# DnsServer
class DnsServerForm(forms.ModelForm):
	class Meta:
		model = DnsServer

class DnsServerHandler(GenericHandler):
	def __init__(self, request, dnsServer, mode):
		pdebug(100, "Function: DnsServerHandler.__init__")
		if mode == 'add':
			self.dnsServer = DnsServer()
		else:
			self.dnsServer= DnsServer.objects.get(ipaddress=dnsServer)
		super(DnsServerHandler, self).__init__(request, mode)

	def _add(self):
		pdebug(100, "Function: DnsServerHandler._add")
		_instance = self.form.save()
		self.response = HttpResponseRedirect(reverse('exodus.views.viewList', \
				args=['dnsServer']))

	def _delete(self):
		pdebug(100, "Function: DnsServerHandler._delete")
		self.object.delete()
		self.response = HttpResponseRedirect(reverse('exodus.views.viewList', \
				args=['dnsServer']))

	def _cancel(self):
		pdebug(100, "Function: DnsServerHandler._cancel")
		self.response = HttpResponseRedirect(reverse('exodus.views.viewList', \
				args=['dnsServer']))

def genericModel(request, model, mode, object):
	pdebug(100, "Function: genericModel %s, %s, %s" % (model, mode, object))
	"""Wrapper, to get to the function needed"""
	model = model[0].upper() + model[1::]
	model = eval(model + 'Handler')
	handler = model(request, object, mode)
	return handler.render_to_response()

def viewList(request, model):
	pdebug(100, "Function: viewList")
	"""Standard interface for simple overview pages, with view/edit/delete 
	buttons on it
	"""
	modelURL = model
	model = model[0].upper() + model[1::]
	modelName = model
	model = eval(model)
	objects = model.objects.all()
	return render_to_response('viewList.html', {'objects': objects, \
			'modelURL' : modelURL, 'modelName' : modelName})
