# TODO:
# - automatically .save() by default (although you can turn it off)
# - Map all Django lookup types
# - Map all Django lookup functions

mapping = {
    '>': 'gt',
    '<': 'lt',
    '>=': 'ge',
    '<=': 'le',
    '==': 'eq',
    '!=': 'ne'
}

u_mapping = {
    '+': 'pos',
    '-': 'neg',
    '~': 'inv'
}

class Atom:
    def __init__(self, start=(), modifiers=None):
        self._start = start
        self._modifiers = modifiers
        
        def makefunc(self, key):
            return lambda x: Statement(self, key, x)

        def u_makefunc(self, key):
            return lambda x: Atom(self._start, key)
        
        for (key,val) in mapping.items():
            setattr(self, '__'+val+'__', makefunc(self, key)) 
        
        for (key,val) in u_mapping.items():
            setattr(self, '__'+val+'__', u_makefunc(self, key))
    
    def __pos__(self): return Atom(self._start, '+')
    
    def __getattr__(self, x):
        if x.startswith('_'): return x.__dict__[x]
        return Atom(self._start+(x,))
    
    def __repr__(self): return '.'.join(self._start)
    
    def _django(self): return '__'.join(self._start)
            
class Statement:
    def __init__(self, atom, operator, value):
        self._atom = atom
        self._operator = operator
        self._value = value
        
        if isinstance(value, Atom) and value._modifiers:
            self._operator += value._modifiers

    def __and__(self, x): return Group(self, 'and', x)
    def __or__(self, x): return Group(self, 'or', y)
    
    def __repr__(self):
        return repr(self._atom) + ' ' + self._operator + ' ' +repr(self._value)
        
    def _django(self):
        mapping = {
            '>': 'gt',
            '<': 'lt',
            '>=': 'gte',
            '<=': 'lte',
            '==': 'exact',
            '!=': 'ne'
        }
            
        return {self._atom._django() + '__' + mapping[self._operator]:self._value}

class Group:
    def __init__(self, stmt1, operator, stmt2):
        self._stmt1 = stmt1
        self._operator = operator
        self._stmt2 = stmt2
    
    def __repr__(self):
        return '('+repr(self._stmt1)+') ' + self._operator + ' ('+repr(self._stmt2)+')'
    
    def _django(self):
        if self._operator == 'and':
            return self._stmt1._django() + self._stmt2._django()
        else:
            raise "I don't think Django supports that operator."

q = Atom()

## django-specific below this line

class Table:
    def __init__(self, table):
        self.table = table
        
    def get(self, *args):
        query = {}
        for x in args:
            query.update(x._django())
        return Result(self.table, query)
    
class Result:
    def __init__(self, table, query):
        self.table = table
        self.query = query

    def __getitem__(self, x):
        if x == 0: return self.table.get_object(**self.query)
        else: 
            iterator = self.table.get_iterator(**self.query)
            i = -1
            while i != x:
                iterator.next()
                i += 1
            return iterator.next()
    
    def __len__(self): return self.table.get_count(**self.query)
    
    def __iter__(self):
        return self.table.get_iterator(**self.query)
