from django.db import models
from django.conf import settings
from django.db.models.signals import post_delete
from django.template.loader import render_to_string
import libvirt

from insekta.common.virt import connections
from insekta.network.models import Address

RUN_STATE_CHOICES = (
    ('disabled', 'VM is not created yet'),
    ('preparing', 'Preparing'),
    ('started', 'VM started'),
    ('suspended', 'VM suspended'),
    ('stopped', 'VM stopped'),
    ('error', 'VM has weird error')
)

class VirtualMachineError(Exception):
    pass

class BaseImage(models.Model):
    name = models.CharField(max_length=80)
    hash = models.CharField(max_length=40)
    
    def get_pool(self, node):
        """Return the pool where volume of the scenario image is stored.
        
        :param node: A libvirt node, e.g. 'mynode'
        :rtype: :class:`libvirt.virStoragePool`
        """
        pool_name = settings.LIBVIRT_STORAGE_POOLS[node]
        return connections[node].storagePoolLookupByName(pool_name)
    
    def get_volume(self, node):
        """Return the volume where the image of this scenario is stored.

        :param node: A libvirt node, e.g. 'mynode'.
        :rtype: :class:`libvirt.virStorageVol`

        >>> scenario = Scenario.objects.get(pk=1)
        >>> vol = scenario.get_volume('mynode')
        >>> print(vol.path()) # Prints /dev/insekta/simple-buffer-overflow
        """
        pool = self.get_pool(node)
        return pool.storageVolLookupByName(self.name)

    def __unicode__(self):
        try:
            scenario = self.scenario
            return u'Image for "{0}"'.format(scenario.title)
        except:
            if not self.virtualmachine_set.count():
                return u'No longer used'
            else:
                return u'Deprecated image, but still in use'

class VirtualMachine(models.Model):
    memory = models.IntegerField()
    base_image = models.ForeignKey(BaseImage)
    node = models.CharField(max_length=80)
    address = models.OneToOneField(Address)
    state = models.CharField(max_length=10, default='disabled',
                             choices=RUN_STATE_CHOICES)

    def __unicode__(self):
        scenario_run = self.scenariorun
        return u'VM for scenario "{0}" played by {1}'.format(
                scenario_run.scenario.title,
                scenario_run.user.username)

    def start(self):
        """Start the virtual machine."""
        self._do_vm_action('create', 'started')

    def stop(self):
        """Stop the virtual machine."""
        self._do_vm_action('destroy', 'stopped')

    def suspend(self):
        """Suspend the virtual machine."""
        self._do_vm_action('suspend', 'suspended')

    def resume(self):
        """Resume the virtual machine."""
        self._do_vm_action('resume', 'started')

    def destroy(self):
        """Destroy this scenario run including virtual machine."""
        try:
            self.stop()
        except VirtualMachineError:
            pass
        self.destroy_domain()
        self.delete()

    def refresh_state(self):
        """Fetches the state from libvirt and saves it."""
        try:
            domain = self.get_domain()
        except libvirt.libvirtError:
            self.state = 'disabled'
        else:
            try:
                state, _reason = domain.state(flags=0)
            except libvirt.libvirtError:
                new_state = 'error'
            else:
                new_state = {
                    libvirt.VIR_DOMAIN_NOSTATE: 'error',
                    libvirt.VIR_DOMAIN_RUNNING: 'started',
                    libvirt.VIR_DOMAIN_BLOCKED: 'error',
                    libvirt.VIR_DOMAIN_PAUSED: 'suspended',