# -*- coding: utf-8 -*-
# vi:si:et:sw=4:sts=4:ts=4
import re
from datetime import datetime
from urllib2 import unquote
from ox.utils import json

from django.contrib.auth.models import User
from django.core.exceptions import ObjectDoesNotExist
from django.db.models import Q, Manager
 
import models


def keyType(key):
    if key in ('released', ):
        return "date"
    if key in ('year', 'cast.length'):
        return "int"
    if key in ('rating', 'votes'):
        return "float"
    return "string"

def parseCondition(condition):
    '''
    condition: {
            value: "war"
    }
    or
    condition: {
            key: "year",
            value: "1970-1980,
            operator: "!="
    }
    ...
	'''
    k = condition.get('key', 'all')
    k = {'id': 'itemId'}.get(k, k)
    if not k: k = 'all'
    v = condition['value']
    op = condition.get('operator', None)
    if not op: op = '~'
    if op.startswith('!'):
        op = op[1:]
        exclude = True
    else:
        exclude = False
    if keyType(k) == "string":
        in_find=True
        value_key = 'find__value'
        if op == '=':
            if k in models.Item.facet_keys:
                in_find=False
                v = models.Item.objects.filter(facets__key=k, facets__value=v)
                k = 'id__in'
            else:
                value_key = 'find__value__iexact'
        elif op == '^':
            v = v[1:]
            value_key = 'find__value__istartswith'
        elif op == '$':
            v = v[:-1]
            value_key = 'find__value__iendswith'
        else: # elif op == '~':
            value_key = 'find__value__icontains'
        k = str(k)
        if exclude:
            if in_find and not k.startswith('itemId'):
                q = ~Q(**{'find__key':k, value_key:v})
            else:
                q = ~Q(**{k:v})
        else:
            if in_find and not k.startswith('itemId'):
                q = Q(**{'find__key':k, value_key:v})
            else:
                q = Q(**{k:v})
        return q
    else: #number or date
        def parseDate(d):
            while len(d) < 3:
                d.append(1)
            return datetime(*[int(i) for i in d])
        if op == '-':
            v1 = v[1]
            v2 = v[2]
            if keyType(k) == "date":
                v1 = parseDate(v1.split('.'))
                v2 = parseDate(v2.split('.'))

            if exclude: #!1960-1970
                k1 = 'value__lt'
                k2 = 'value__gte'
                return Q(**{'find__key': k, k1:v1})|Q(**{'find__key': k, k2:v2})
            else: #1960-1970
                k1 = 'value__gte'
                k2 = 'value__lt'
                return Q(**{'find__key': k, k1:v1})&Q(**{'find__key': k, k2:v2})
        else:
            if keyType(k) == "date":
                v = parseDate(v.split('.'))
            if op == '=':
                vk = 'value__exact'
            elif op == '>':
                vk = 'value__gt'
            elif op == '>=':
                vk = 'value__gte'
            elif op == '<':
                vk = 'value__lt'
            elif op == '<=':
                vk = 'value__lte'

            vk = 'find__%s' % vk
            vk = str(vk)

            if exclude: #!1960
                return ~Q(**{'find__key': k, vk:v})
            else: #1960
                return Q(**{'find__key': k, vk:v})

def parseConditions(conditions, operator):
    '''
    conditions: [
        {
            value: "war"
        }
        {
            key: "year",
            value: "1970-1980,
            operator: "!="
        },
        {
            key: "country",
            value: "f",
            operator: "^"
        }
    ],
    operator: "&"
	'''
    conn = []
    for condition in conditions:
        if 'conditions' in condition:
            q = parseConditions(condition['conditions'],
                             condition.get('operator', '&'))
            if q: conn.append(q)
            pass
        else:
            if condition.get('value', '') != '' or condition.get('operator', '') == '=':
                conn.append(parseCondition(condition))
    if conn:
        q = conn[0]
        for c in conn[1:]:
            if operator == '|':
                q = q | c
            else:
                q = q & c
        return q
    return None

class ItemManager(Manager):
    def get_query_set(self):
        return super(ItemManager, self).get_query_set()

    def filter_list(self, qs, l, user):
        if l != "all":
            l = l.split(":")
            only_public = True
            if not user.is_anonymous():
                if len(l) == 1: l = [request.user.username] + l
                if request.user.username == l[0]:
                    only_public = False
            if len(l) == 2:
                lqs = models.List.objects.filter(name=l[1], user__username=l[0])
                if only_public:
                    lqs = qls.filter(public=True)
                if lqs.count() == 1:
                    qs = qs.filter(listitem__list__id=lqs[0].id)
        return qs

    def find(self, data, user):
        '''
            query: {
                conditions: [
                    {
                        value: "war"
                    }
                    {
                        key: "year",
                        value: "1970-1980,
                        operator: "!="
                    },
                    {
                        key: "country",
                        value: "f",
                        operator: "^"
                    }
                ],
                operator: "&"
            }
		'''

        #join query with operator
        qs = self.get_query_set()
        #only include items that have hard metadata
        qs = qs.filter(available=True)
        conditions = parseConditions(data['query']['conditions'],
                                     data['query'].get('operator', '&'))
        if conditions:
            qs = qs.filter(conditions)

        #FIXME: lists are part of query now
        # filter list, works for own or public lists
        l = data.get('list', 'all')
        qs = self.filter_list(qs, l, user)
        return qs