text: # vim:ts=4:sts=4:sw=4:expandtab
  
  from   datetime  import datetime
  from   django.db import models
  import inspect
  
  from satori.ars.model import *
  from satori.ars import perf
  
  from satori.core.export.type_helpers import DefineException, ArsDeferredStructure, python_to_ars_type
  from satori.core.export.pc import AccessDenied
  
  ArgumentNotFound = DefineException('ArgumentNotFound', 'The specified argument cannot be found: model={model}, id={id}',
      [('model', unicode, False), ('id', long, False)])
  
  
  CannotReturnObject = DefineException('CannotReturnObject', 'You don\'t have rights to view the returned object')
  
  CannotDeleteObject = DefineException('CannotDeleteObject', 'You can\'t delete this object')
  
  
  field_basic_types = {
      models.AutoField: long,
      models.IntegerField: int,
      models.CharField: unicode,
      models.TextField: unicode,
      models.BooleanField: bool,
      models.DateTimeField: datetime,
      models.IPAddressField: unicode,
      models.FloatField: float,
  }
  
  
  field_type_map = {}
  
  
  def django_field_to_python_type(field):
      if not field in field_type_map:
          field_type = None
          if type(field) in field_basic_types:
              field_type = field_basic_types[type(field)]
          if isinstance(field, models.ForeignKey):
              if issubclass(field.rel.to, models.Model):
                  field_type = DjangoId(field.rel.to.__name__)
              else:
                  field_type = DjangoId(field.rel.to.split('.')[-1])
          field_type_map[field] = field_type
  
      return field_type_map[field]
  
  
  ars_django_id = {}
  ars_django_structure = {}
  ars_django_id_list = {}
  ars_django_structure_list = {}
  
  
  class ArsDjangoId(ArsTypeAlias):
      def __init__(self, model):
          super(ArsDjangoId, self).__init__(name=(model.__name__ + 'Id'), target_type=ArsInt64)
          self.model = model
  
      def do_needs_conversion(self):
          return True
  
      def do_convert_to_ars(self, value):
  #        # decided not to enforce
  #        if not Privilege.demand(value, 'VIEW'):
  #            raise CannotReturnObject()
  
          return value.id
  
      def do_convert_from_ars(self, value):
          try:
              ret = Privilege.wrap(self.model, select=['VIEW']).get(id=value)
          except self.model.DoesNotExist:
              raise ArgumentNotFound(model=self.model.__name__, id=value)
          else:
              if not Privilege.demand(ret, 'VIEW'):
                  raise AccessDenied()
              return ret
  
  
  class DjangoId(object):
      def __init__(self, model):
          super(DjangoId, self).__init__()
          self.model = model
  
      def ars_type(self):
          if not self.model in ars_django_id:
              raise RuntimeError('Model not exported: {0}'.format(self.model))
  
          return ars_django_id[self.model]
  
  
  class ArsDjangoStructure(ArsDeferredStructure):
      def __init__(self, model, fields, extra_fields):
          super(ArsDjangoStructure, self).__init__(model.__name__ + 'Struct', [])
          self.model = model
          self.django_fields = fields
          self.django_extra_fields = extra_fields
  
      def init_fields(self):
          field_dict = dict((field.name, field) for field in self.model._meta.fields)
      
          for (field_name, field_permission) in self.django_fields:
              self.add_field(name=field_name, type=python_to_ars_type(django_field_to_python_type(field_dict[field_name])), optional=True)
  
          for (field_name, field_type, field_permission) in self.django_extra_fields:
              self.add_field(name=field_name, type=python_to_ars_type(field_type), optional=True)
  
      def do_needs_conversion(self):
          return True
  
      def do_convert_to_ars(self, value):
          if not hasattr(value, '_can_VIEW'):
          	value = Privilege.wrap(value.__class__, struct=True).get(id=value.id)
  
          if not Privilege.demand(value, 'VIEW'):
              raise CannotReturnObject()
          
          ret = self.get_class()()
  
          for (field_name, field_permission) in self.django_fields:
              if Privilege.demand(value, field_permission):
                  field_type = self.fields[field_name].type
                  if field_type.needs_conversion():
                      if isinstance(field_type, ArsDjangoId):
                          setattr(ret, field_name, getattr(value, field_name + '_id'))
                      else:
                          setattr(ret, field_name, field_type.convert_to_ars(getattr(value, field_name)))
                  else:
                      setattr(ret, field_name, getattr(value, field_name))
  
          for (field_name, field_type, field_permission) in self.django_extra_fields:
              if Privilege.demand(value, field_permission):
                  if field_type.needs_conversion():
                      setattr(ret, field_name, field_type.convert_to_ars(getattr(value, field_name)))
                  else:
                      setattr(ret, field_name, getattr(value, field_name))
  
          return ret
  
      def do_convert_from_ars(self, value):
          return super(ArsDjangoStructure, self).do_convert_from_ars(value)
  
  
  class DjangoStruct(object):
      def __init__(self, model):
          super(DjangoStruct, self).__init__()
          self.model = model
  
      def __call__(self, *args, **kwargs):
          return self.ars_type().get_class()(*args, **kwargs)
  
      def ars_type(self):
          if not self.model in ars_django_structure:
              raise RuntimeError('Model not exported: {0}'.format(self.model))
  
          return ars_django_structure[self.model]
  
  
  class ArsDjangoIdList(ArsList):
      def __init__(self, model):
          super(ArsDjangoIdList, self).__init__(element_type=ars_django_id[model])
          self.model = model
  
      def do_needs_conversion(self):
          return True
  
      def do_convert_to_ars(self, value):
          return super(ArsDjangoIdList, self).do_convert_to_ars(Privilege.wrap(value, where=['VIEW'], select=['VIEW']))
  
  
      def do_convert_from_ars(self, value):
          return super(ArsDjangoIdList, self).do_convert_from_ars(value)
  
  
  class DjangoIdList(object):
      def __init__(self, model):
          super(DjangoIdList, self).__init__()
          self.model = model
  
      def ars_type(self):
          if not self.model in ars_django_id_list:
              raise RuntimeError('Model not exported: {0}'.format(self.model))
  
          return ars_django_id_list[self.model]
  
  
  class ArsDjangoStructureList(ArsList):
      def __init__(self, model):
          super(ArsDjangoStructureList, self).__init__(element_type=ars_django_structure[model])
          self.model = model
  
      def do_needs_conversion(self):
          return True
  
      def do_convert_to_ars(self, value):
          return super(ArsDjangoStructureList, self).do_convert_to_ars(Privilege.wrap(value, where=['VIEW'], struct=True))
  
      def do_convert_from_ars(self, value):
          return super(ArsDjangoStructureList, self).do_convert_from_ars(value)
  
  
  class DjangoStructList(object):
      def __init__(self, model):
          super(DjangoStructList, self).__init__()
          self.model = model
  
      def ars_type(self):
          if not self.model in ars_django_structure_list:
              raise RuntimeError('Model not exported: {0}'.format(self.model))
  
          return ars_django_structure_list[self.model]
  
  
  def generate_django_types(cls):
      ars_django_id[cls] = ArsDjangoId(cls)
      ars_django_id[cls.__name__] = ars_django_id[cls]
  
      fields = []
      extra_fields = []
  
      for parent_cls in reversed(inspect.getmro(cls)):
          if 'ExportMeta' in parent_cls.__dict__:
              if hasattr(parent_cls.ExportMeta, 'fields'):
                  fields.extend(parent_cls.ExportMeta.fields)
              if hasattr(parent_cls.ExportMeta, 'extra_fields'):
                  extra_fields.extend(parent_cls.ExportMeta.extra_fields)
  
      ars_django_structure[cls] = ArsDjangoStructure(cls, fields, extra_fields)
      ars_django_structure[cls.__name__] = ars_django_structure[cls]
  
      ars_django_id_list[cls] = ArsDjangoIdList(cls)
      ars_django_id_list[cls.__name__] = ars_django_id_list[cls]
  
      ars_django_structure_list[cls] = ArsDjangoStructureList(cls)
      ars_django_structure_list[cls.__name__] = ars_django_structure_list[cls]
  
      cls._struct_rights = set([field_permission for (field_name, field_permission) in fields]
              + [field_permission for (field_name, field_type, field_permission) in extra_fields]
              + ['VIEW'])
  
  def init():
      global Privilege
      from satori.core.models import Privilege
  
  