# (c) Roland van Laar 2006

from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response
from django.core.urlresolvers import reverse, NoReverseMatch
from django import forms
from socket import gethostname 

from exodus.models import *
from exodus.wllogic import free_master_ip, new_ssid, add_interlink_ip, \
        free_interlink_ip

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 = free_publicap_ip(_instance.iface, _instance.netmask)
#        _instance.dhcpstart = 1
#        _instance.dhcpstop = 2
#        # If wireless generate ssid name
#        _instance.ssid = new_ssid(_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 = new_ssid(_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 = free_interlink_ip(_instance)
            else:
                _instance.ip = add_interlink_ip(_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_master_ip(_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})
