from gheat import managers
import datetime
import binascii
import hashlib

from django.core import validators
from django.core.files.storage import FileSystemStorage
from django.db import models
from django.dispatch import receiver
from django.utils.encoding import smart_unicode
from django.utils.translation import ugettext_lazy as _


# http://djangosnippets.org/snippets/976/
class OverwriteStorage(FileSystemStorage):
    def get_available_name(self, name):
        """
        Returns a filename that's free on the target storage system, and
        available for new content to be written to.
        """
        # If the filename already exists, remove it as if it was a true file system
        if self.exists(name):
            self.delete(name)
        return name

class BinaryField(models.Field):
  MAGIC = '###MAGIC###'
  description = _('Binary object using base64 (up to %(max_length)s)')

  __metaclass__ = models.SubfieldBase

  def __init__(self, *args, **kwargs):
    # base64 roughly max the binary size with with 4 times
    kwargs['max_length'] = kwargs['max_length'] * 4 + len(self.MAGIC)
    super(BinaryField, self).__init__(*args, **kwargs)
    self.validators.append(validators.MaxLengthValidator(self.max_length))

  def to_python(self,value):
    if value.startswith(self.MAGIC):
      return binascii.a2b_base64(value[len(self.MAGIC):])
    else:
      return value

  def get_db_prep_value(self, value, connection, prepared=False):
    target = self.MAGIC + binascii.b2a_base64(value)
    if len(target) > self.max_length:
      raise ValueError(len(target))
    return target

  def get_prep_lookup(self, lookup_type, value):
    raise TypeError('Lookup type %r not supported.' % lookup_type)

  def get_internal_type(self):
    return 'TextField'

class TileCache(models.Model):
  key = models.CharField(max_length=34,unique=True)
  data = BinaryField(max_length=10000)
  creation_time = models.DateTimeField(auto_now_add=True)

  def __unicode__(self):
    return self.key

  @staticmethod
  def make_key(source):
    return hashlib.md5(source).hexdigest()

class WirelessClient(models.Model):
  mac = models.CharField(max_length=17, unique=True)
  def __unicode__(self):
    return self.mac


class Organization(models.Model):
  fullname = models.CharField(max_length=100,unique=True)
  name = models.CharField(max_length=100,unique=True)
  def __unicode__(self):
    return self.fullname

  @staticmethod
  def get_name_by_ssid(ssid):
    """ Try to determine the organization via the SSID """
    ssid = ssid.lower()
    name = None
    if ssid.startswith('ap') and ssid.endswith('wleiden.net'):
      name = 'WirelessLeiden'
    elif ssid.startswith('il-') and ssid.endswith('wleiden.net'):
      name = 'WirelessLeiden'
    elif ssid.startswith('ap') and 'wirelessleiden' in ssid:
      name = 'WirelessLeiden'
    elif ssid.startswith('ap-') and 'westeinder' in ssid:
      name = 'WirelessPlassen'
    elif ssid.endswith('walphen.net'):
      name = 'WirelessAlphen'
    elif 'wirelessarnhem' in ssid:
      name = 'WirelessArnhem'
    return name

  @staticmethod
  def get_by_ssid(ssid):
    name = Organization.get_name_by_ssid(ssid)
    if not name:
      return None
    else:
      return Organization.objects.get(name=name)


class Node(models.Model):
  name = models.CharField(max_length=50, unique=True)
  latitude = models.FloatField(null=True,blank=True)
  longitude = models.FloatField(null=True,blank=True)
  organization = models.ForeignKey(Organization,null=True, blank=True)

  def __unicode__(self):
    return "%s - %s" % (self.name, self.organization)

class Accespoint(models.Model):
  mac = models.CharField(max_length=17)
  ssid = models.CharField(max_length=64)
  organization = models.ForeignKey(Organization,null=True, blank=True)
  encryptie = models.BooleanField()
  class Meta:
    unique_together = ('mac', 'ssid')
  def __unicode__(self):
    return "%s - %s" % (self.mac, self.ssid)


  def save(self, *args, **kwargs):
    self.organization = self.get_organization(self.ssid)
    super(Accespoint, self).save(*args, **kwargs)

class Gebruiker(models.Model):
  naam = models.CharField(max_length=64)
  email = models.CharField(max_length=64)
  def __unicode__(self):
    return "%s - %s" % (self.naam, self.email)

class Apparatuur(models.Model):
  antenne = models.CharField(max_length=64)
  kaart = models.CharField(max_length=64)
  def __unicode__(self):
    return "%s - %s" % (self.antenne, self.kaart)
  class Meta:
    verbose_name_plural = 'Apparatuur'

class MeetRondje(models.Model):
  datum = models.DateTimeField(blank=True,null=True,default=datetime.datetime.now)
  naam = models.CharField(max_length=64)
  gebruiker = models.ForeignKey(Gebruiker)
  apparatuur = models.ForeignKey(Apparatuur)
  def __unicode__(self):
    return "%s - %s" % (self.gebruiker.naam, self.naam)
  class Meta:
    verbose_name_plural = 'MeetRondjes'

class MeetBestand(models.Model):
  meetrondje = models.ForeignKey(MeetRondje)
  bestand = models.FileField(upload_to='scan-data/%Y/%m/%d',storage=OverwriteStorage())
  is_imported = models.BooleanField(default=False)
  class Meta:
    verbose_name_plural = 'MeetBestanden'
  def __unicode__(self):
    return "%s - %s" % (self.meetrondje, self.bestand)
  

class Meting(models.Model):
  meetrondje = models.ForeignKey(MeetRondje)
  accespoint = models.ForeignKey(Accespoint)
  latitude = models.FloatField()
  longitude = models.FloatField()
  signaal = models.IntegerField(max_length=3)
  objects = managers.MetingManager()
  def __unicode__(self):
    return "%s @ %.5f,%.5f : %s" % (self.accespoint.ssid, float(self.latitude), float(self.longitude), self.signaal)
  class Meta:
    # This implies that you cannot have multiple messurements on the same
    # location for a single 'run', thus the data needs cleaned before to make
    # this properly hold.
    unique_together = ('meetrondje', 'accespoint', 'latitude', 'longitude'),
    verbose_name_plural = 'Metingen'

