import warnings

from utils import rc
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
from django.conf import settings

typemapper = { }
handler_tracker = [ ]

class HandlerMetaClass(type):
    """
    Metaclass that keeps a registry of class -> handler
    mappings.
    """
    def __new__(cls, name, bases, attrs):
        new_cls = type.__new__(cls, name, bases, attrs)

        def already_registered(model, anon):
            for k, (m, a) in typemapper.iteritems():
                if model == m and anon == a:
                    return k

        if hasattr(new_cls, 'model'):
            if already_registered(new_cls.model, new_cls.is_anonymous):
                if not getattr(settings, 'PISTON_IGNORE_DUPE_MODELS', False):
                    warnings.warn("Handler already registered for model %s, "
                        "you may experience inconsistent results." % new_cls.model.__name__)

            typemapper[new_cls] = (new_cls.model, new_cls.is_anonymous)
        else:
            typemapper[new_cls] = (None, new_cls.is_anonymous)

        if name not in ('BaseHandler', 'AnonymousBaseHandler'):
            handler_tracker.append(new_cls)

        return new_cls

class BaseHandler(object):
    """
    Basehandler that gives you CRUD for free.
    You are supposed to subclass this for specific
    functionality.

    All CRUD methods (`read`/`update`/`create`/`delete`)
    receive a request as the first argument from the
    resource. Use this for checking `request.user`, etc.
    """
    __metaclass__ = HandlerMetaClass

    allowed_methods = ('GET', 'POST', 'PUT', 'DELETE')
    anonymous = is_anonymous = False
    exclude = ( 'id', )
    fields =  ( )

    def flatten_dict(self, dct):
        return dict([ (str(k), dct.get(k)) for k in dct.keys() ])

    def has_model(self):
        return hasattr(self, 'model') or hasattr(self, 'queryset')

    def queryset(self, request):
        return self.model.objects.all()

    def value_from_tuple(tu, name):
        for int_, n in tu:
            if n == name:
                return int_
        return None

    def exists(self, **kwargs):
        if not self.has_model():
            raise NotImplementedError

        try:
            self.model.objects.get(**kwargs)
            return True
        except self.model.DoesNotExist:
            return False

    def read(self, request, *args, **kwargs):
        if not self.has_model():
            return rc.NOT_IMPLEMENTED

        pkfield = self.model._meta.pk.name

        if pkfield in kwargs:
            try:
                return self.queryset(request).get(pk=kwargs.get(pkfield))
            except ObjectDoesNotExist:
                return rc.NOT_FOUND
            except MultipleObjectsReturned: # should never happen, since we're using a PK
                return rc.BAD_REQUEST
        else:
            return self.queryset(request).filter(*args, **kwargs)

    def create(self, request, *args, **kwargs):
        if not self.has_model():
            return rc.NOT_IMPLEMENTED

        attrs = self.flatten_dict(request.data)

        try:
            inst = self.queryset(request).get(**attrs)
            return rc.DUPLICATE_ENTRY
        except self.model.DoesNotExist:
            inst = self.model(**attrs)
            inst.save()
            return inst
        except self.model.MultipleObjectsReturned:
            return rc.DUPLICATE_ENTRY

    def update(self, request, *args, **kwargs):
        if not self.has_model():
            return rc.NOT_IMPLEMENTED

        pkfield = self.model._meta.pk.name

        if pkfield not in kwargs:
            # No pk was specified
            return rc.BAD_REQUEST

        try:
            inst = self.queryset(request).get(pk=kwargs.get(pkfield))
        except ObjectDoesNotExist:
            return rc.NOT_FOUND
        except MultipleObjectsReturned: # should never happen, since we're using a PK
            return rc.BAD_REQUEST

        attrs = self.flatten_dict(request.data)
        for k,v in attrs.iteritems():
            setattr( inst, k, v )

        inst.save()
        return rc.ALL_OK

    def delete(self, request, *args, **kwargs):
        if not self.has_model():
            raise NotImplementedError

        try:
            inst = self.queryset(request).get(*args, **kwargs)

            inst.delete()

            return rc.DELETED
        except self.model.MultipleObjectsReturned:
            return rc.DUPLICATE_ENTRY
        except self.model.DoesNotExist:
            return rc.NOT_HERE

class AnonymousBaseHandler(BaseHandler):
    """
    Anonymous handler.
    """
    is_anonymous = True
    allowed_methods = ('GET',)

@@ -395,24 +395,57 @@
 es, attrs)%0A%0A
+        if new_cls.register:%0A    
         def 
@@ -485,24 +485,28 @@
             
+    
 for k, (m, a
@@ -550,16 +550,20 @@
         
+    
 if model
@@ -599,24 +599,28 @@
             
+  
+  
 return k%0A%0A  
@@ -617,16 +617,20 @@
 turn k%0A%0A
+    
         
@@ -655,24 +655,28 @@
 , 'model'):%0A
+    
             
@@ -747,24 +747,28 @@
             
+ 
+   
 if not getat
@@ -837,16 +837,20 @@
         
+    
 warnings
@@ -923,16 +923,20 @@
         
+   
+ 
 %22you may
@@ -1001,32 +1001,36 @@
 _)%0A%0A            
+    
 typemapper%5Bnew_c
@@ -1073,32 +1073,36 @@
 nymous)%0A        
+ 
+   
 else:%0A          
@@ -1095,32 +1095,36 @@
 se:%0A            
+  
+  
 typemapper%5Bnew_c
@@ -1155,24 +1155,28 @@
 anonymous)%0A%0A
+    
         if n
@@ -1223,24 +1223,28 @@
 eHandler'):%0A
+    
             
@@ -1776,15 +1776,13 @@
  = (
- 
 'id',
- 
 )%0A  
@@ -1796,12 +1796,30 @@
 s = 
- ( )
+()%0A    register = True
 %0A%0A  
@@ -2866,32 +2866,33 @@
 ObjectsReturned:
+ 
  # should never 
@@ -3989,16 +3989,17 @@
 eturned:
+ 
  # shoul
@@ -4134,16 +4134,17 @@
   for k,
+ 
 v in att
@@ -4179,17 +4179,16 @@
 setattr(
- 
 inst, k,
@@ -4189,17 +4189,16 @@
 st, k, v
- 
 )%0A%0A     

Add option not to register handler to tracker