from django.db import models
import operator
from django.db.models import Q
from ox.text import smartSplit
from ox.django.fields import DictField
from django.core.paginator import Paginator, InvalidPage, EmptyPage


def splitSearch(string):
  ret = []
  for s in smartSplit(string):
    word = s.replace("'", "").replace('"', '')
    ret.append(word)
  return ret

class ItfModel(models.Model):
  fts_fields = []
  fk_filters = []
#  related_models = []
  sort_fields = []
  hasComments = True
    

  class Meta:
    abstract = True

  def list_dict(self):
    return {}

  def info_dict(self):
    return self.get_dict()

  def get_dict(self):
    return self.get(self._get_fields().keys())

  def get(self, props):
    typ = type(props)
    if typ == list:
      ret = {}
      for p in props:
        ret[p] = self._get_property(p)
      return ret
    elif typ == str:
      return _get_property(props)
    else:
      return False

  def _get_property(self, prop):
    fields = self._get_fields()
    if prop in fields.keys():
      field_type = fields[prop]
      if field_type in ["TextField", "CharField", "IntegerField"]:
        return self.__getattribute__(prop)
      elif field_type == "ImageField":
        imagefield = self.__getattribute__(prop)
        return imagefield.url if imagefield.name != '' else ''
      elif field_type == "ForeignKey":
        return self._get_fk(prop)
      elif field_type == "ManyToMany":
        return self._get_m2m(prop)
    elif prop.lower() in self.related_models:
      return self._get_related(prop)
    else:
      try:
        val = self.__getattribute__(prop)
        if type(val).__name__ == 'instancemethod' and prop in self.getters:
          return val()
        else:
          return val
      except:
        return False #FIXME

  def _get_fk(self, prop):
    prop = prop.replace("_id", "")
    return self.__getattribute__(prop).get_dict()

#  def _get_related_objects(self, fk_field):
  def _get_m2m(self, prop):
    ret = []
    for o in self.__getattribute__(prop).all():
      ret.append(o.get_dict())
    return ret
    
  def _get_related(self, prop):
    attr = prop.lower() + "_set"
    ret = []
    for o in self.__getattribute__(attr).all():
      ret.append(o.get_dict())
    return ret

  @classmethod
  def _get_fields(kls):
    ret = {}
    for f in kls._meta.fields:
      ret[f.get_attname()] = type(f).__name__
    return ret    


  
  @classmethod
  def fts(kls, qset, search):
    terms = splitSearch(search)
    qobjects = []
    for t in terms:  
      for f in kls.fts_fields:
        qstring = f + '__icontains'
        qobject = Q(**{qstring:t})
        qobjects.append(qobject)
    return qset.filter(reduce(operator.or_, qobjects))

  '''
  eg. fks = {
      'somefield': [1, 5, 7],
      'someotherfield': [3]
      }
  '''
  @classmethod
  def filter_by_fks(kls, qset, fks):
    qobjects = []
    for key in fks.keys():
      field = getField(kls._meta.fields, key)
      if field:
#        rel_class = field.related.parent_model
        for i in fks[key]:
          qobject = Q(**{field.name: i})
          qobjects.append(qobject)
    return qset.filter(reduce(operator.or_, qobjects))               


  @classmethod
  def get_fk_objects(kls):
    ret = {}
    for f in kls.fk_filters:
      ret[f] = []
      field = getField(kls._meta.fields, f)
      rel_class = field.related.parent_model
      for o in rel_class.objects.all():
        ret[f].append({
          'id': o.id,
          'title': unicode(o)
        })  
    return ret

  
  @classmethod
  def get_list(kls, data):
    options = {
      'page_no': 1,
      'list_size': 8,
      'search': '',
      'sort': [],
      'range': [0,50]
    }
    options.update(data)
    ret = []
    page_no = options['page_no']
    list_size = options['list_size']
    try:
      qset = kls.get_qset()
    except:
      qset = kls.objects.all()

    search = options['search']
    if search != '':
      qset = kls.fts(qset, search) 
   
    sort = options['sort']
    if sort != []:
      for s in sort:
        if s['operator'] == '-':
          operator = '-'
        else:
          operator = ''
        sort = operator + s['key']
        qset = qset.order_by(sort)

    r0 = options['range'][0]
    r1 = options['range'][1]
    results = qset[r0:r1]
    for r in results:
      ret.append(r.list_dict())
    return ret

  @classmethod
  def get_qset(kls):
    '''
    Override this method in your model class to define a custom queryset instead of objects.all(), for instance, to always exclude unpublished items.
    '''
    raise NotImplementedError

def getField(fields, name):
  for f in fields:
    if f.name == name:
      return f
  return False



def site_config():
    with open(settings.SITE_CONFIG) as f:
        site_config = json.load(f)
        site_config['keys'] = {}
        for key in site_config['itemKeys']:
            site_config['keys'][key['id']] = key
        site_config['_findKeys'] = {}
        for key in site_config['findKeys']:
            site_config['_findKeys'][key['id']] = key
    return site_config
