def __init__(self, site_name, database,  lazy=False):   
     self._set_all()
     self._db = ITP_CouchDB( site_name , database )
     self._lazy = lazy
     self.couch_handler = self._db #mask
class CouchQuery():

    def _set_all(self):

        self._filter_dict = {}
        self._exclude_dict = {}
        self._filter_dict__arr = {
            'in' : {},
            'gte' : {},
            'lte' : {},
        }
        self._exclude_dict__arr = {
            'in' : {},
            'gte' : {},
            'lte' : {},
        }

        self._order = "-id"
        self._limit = None
        self._descending = None
        self._skip = None # skip n number of documents
        self._cs = None # country switch for all queries
        self.return_k = "d._id";
        self.return_v = "d";
        self._filter_dict = {}
        self._clean_query = False #if set to true it invalidates any criterias and return all docs
        self.extras = ""

    def __init__(self, site_name, database,  lazy=False):   
        self._set_all()
        self._db = ITP_CouchDB( site_name , database )
        self._lazy = lazy
        self.couch_handler = self._db #mask

    def change_db( self, db ):
        self._db.select_db(db)

    def _query(self):

        """
        It's cleaner to have a set of for loops than a set of conidtions inside a for loop.
        """

        #malcala: added support for query options
        # The list of query parameters can be found at:
        # http://wiki.apache.org/couchdb/HTTP_view_API
        query_options = dict()
        if self._limit:
            query_options['limit'] = self._limit
        if self._descending:
            query_options['descending'] = self._descending
        if self._skip:
            query_options['skip'] = self._skip

        #if return all objects
        if self._clean_query:
            query = """function(d) {
                    emit ( %(return_k)s , %(return_v)s  )
            }"""% { 
                'return_k' : self.return_k,
                'return_v' : self.return_v,
            }

            return self._db.query(query, **query_options)


        filter_str = ""
        filter_str_in = "" 
        filter_str_gte = ""
        filter_str_lte = ""
        exclude_str = ""
        exclude_str_in = "" 
        exclude_str_gte = ""
        exclude_str_lte = ""

        sep = ""
        prev = 0 #used to see if we need to add && to the final query, if there is a previous one to this 

        for counter, flt in enumerate(self._filter_dict.iteritems()):
            prev = 1
            #malcala: country_id compound data support. Workaround
            #flt_cero = flt[0] # tuples do not support modifications
            #if flt_cero == 'country_id':
            #    flt_cero = 'countries.country.id'
            if counter != 0: sep="&&"
            filter_str = "%s%s"% ( filter_str, "%s ( d.%s == '%s' ) "% ( sep, flt[0] ,  flt[1] ) )
            #filter_str = "%s%s"% ( filter_str, "%s ( d.%s == '%s' ) "% ( sep, flt_cero ,  flt[1] ) )

        sep = ""
        for counter, flt in enumerate(self._exclude_dict.iteritems()):
            prev += 1
            if counter != 0: sep="&&"
            exclude_str = "%s%s"% ( exclude_str, "%s ( d.%s != '%s' ) "% ( sep, flt[0] , flt[1] ) )
            if prev > 1: exclude_str = " && %s" % exclude_str  

        sep = ""
        for counter, flt in enumerate(self._filter_dict__arr['gte'].iteritems()):
            prev += 1
            if counter != 0: sep="&&"
            #print filter_str_gte, counter
            filter_str_gte = "%s%s" % ( filter_str_gte, "%s ( d.%s > %s  )  " % ( sep, flt[0] , flt[1]  )  )
            #print filter_str_gte
            if prev > 1: filter_str_gte = " && %s" % filter_str_gte  
            #print filter_str_gte

        sep = ""
        for counter, flt in enumerate(self._exclude_dict__arr['gte'].iteritems()):
            prev += 1
            if counter != 0: sep="&&"
            exclude_str_gte = "%s%s" % ( exclude_str_gte, "%s ( d.%s <= %s  )  " % ( sep, flt[0] , flt[1]  )  )
            if prev > 1: exclude_str_gte = " && %s" % exclude_str_gte  

        sep = ""
        for counter, flt in enumerate(self._filter_dict__arr['lte'].iteritems()):
            prev += 1
            if counter != 0: sep="&&"
            filter_str_lte = "%s%s" % ( filter_str_lte, "%s ( d.%s < %s  )  " % ( sep, flt[0] , flt[1]  )  )
            if prev > 1: filter_str_lte = " && %s" % filter_str_lte  

        sep = ""
        for counter, flt in enumerate(self._exclude_dict__arr['lte'].iteritems()):
            prev += 1
            if counter != 0: sep="&&"
            exclude_str_lte = "%s%s" % ( exclude_str_lte, "%s ( d.%s >= %s  )  " % ( sep, flt[0] , flt[1]  )  )
            if prev > 1: exclude_str_lte = " && %s" % exclude_str_lte  

        sep = ""
        arr_content = ""
        for counter, flt in enumerate(self._filter_dict__arr['in'].iteritems()):
            prev += 1
            if counter != 0: sep="&&"
            flt_val = flt[1]
            try:
                flt_val  = [str(k).encode('ascii') for k in flt[1]]
            except:
                pass
            arr_content = "%s %s" % ( arr_content,  "var arr0%d = %s ; " % ( counter, str(flt_val)  ) )
            filter_str_in = "%s%s" % ( filter_str_in, "%s ( %s.indexOf( d.%s ) != -1 )  " % ( sep, "arr0%d"%counter , flt[0]  )  )
            if prev > 1: filter_str_in = " && %s" % filter_str_in  


        sep = ""
        for counter, flt in enumerate(self._exclude_dict__arr['in'].iteritems()):
            prev += 1
            if counter != 0: sep="&&"
            flt_val = flt[1]
            try:
                flt_val  = [str(k).encode('ascii') for k in flt[1]]
            except:
                pass
            arr_content = "%s %s" % ( arr_content,  "var arr1%d = %s ; " % ( counter, str(flt_val )  ) )
            exclude_str_in = "%s%s" % ( exclude_str_in, "%s ( %s.indexOf( d.%s ) == -1 )  " % ( sep, "arr1%d"%counter , flt[0]  )  )
            if prev > 1: exclude_str_in = " && %s" % exclude_str_in  



        query = """function(d) {
            %(arr_content)s

            %(extras)s            
 
            if (  %(filter_str)s  %(exclude_str)s  %(filter_str_gte)s  %(exclude_str_gte)s  %(filter_str_lte)s  %(exclude_str_lte)s  %(filter_str_in)s  %(exclude_str_in)s  ) {
                emit ( %(return_k)s , %(return_v)s  )
            } 
        }"""% { 
            'arr_content' : arr_content,
            'extras' : self.extras,
            'filter_str' : filter_str,
            'filter_str_lte' : filter_str_lte,
            'filter_str_gte' : filter_str_gte,
            'filter_str_in' : filter_str_in,
            'exclude_str' : exclude_str,
            'exclude_str_lte' : exclude_str_lte,
            'exclude_str_gte' : exclude_str_gte,
            'exclude_str_in' : exclude_str_in,
            'return_k' : self.return_k,
            'return_v' : self.return_v,
        }


        #print query

        #malcala: added support for query options
        # The list of query parameters can be found at:
        # http://wiki.apache.org/couchdb/HTTP_view_API
        query_options = dict()
        if self._limit:
            query_options['limit'] = self._limit
        if self._descending:
            query_options['descending'] = self._descending
        if self._skip:
            query_options['skip'] = self._skip


        res= self._db.query(query, **query_options)

        #import pdb; pdb.set_trace()
        return res

    def _add_filter_or_exclude(self, exclude, key, value):
        if exclude:
            self._exclude_dict.update( { key : value }  )
        else:
            self._filter_dict.update( { key : value }  )

    def _add_filter_or_exclude__(self, exclude, key, __val, param):
        if exclude:
            self._exclude_dict__arr[param].update( { key : __val }  )
        else:
            self._filter_dict__arr[param].update( { key : __val }  )


    def _filter_or_exclude( self, exclude,  dic={} , *args, **kwargs ):
        parse = kwargs if dic=={} else dic
        for key in parse:
            params = key.split("__") 
            if len( params  ) > 1:
                if params[1] not in ( "in" , "gte" , "lte"  ):
                    raise Exception( "Unsupported key word argument %s" %params[1] )
                self._add_filter_or_exclude__( exclude, params[0] , parse[key] , params[1]  )
                continue 
            self._add_filter_or_exclude(exclude, key, parse[key])

        if self._lazy:
            return self
        else:
            return self.fire()

    def dict_filter_or_exclude(self, exclude, dic={}):
        self._filter_or_exclude( exclude, dic)
        return self
    
    def filter(self, *args, **kwargs):
        """News.filter( slug = "", id="" , date__gte="", country_id=""  )"""
        return self._filter_or_exclude(False, *args, **kwargs)

    def exclude(self, *args, **kwargs):
        return self._filter_or_exclude(True, *args, **kwargs)

    def get(self, *args, **kwargs):
        """Returns one object"""
        self._lazy = True
        self.limit(1)
        self.filter( *args, **kwargs )
        res = self.fire()
        try:
            return res.rows[0].value
        except KeyError:
            return None

    def all(self):
        """That's when you don't want to write a criteria"""
        self._clean_query = True
        if not self._lazy: 
            return self._query()
        else:
            return self

    def view(self, view_name, options={}):
        """example: get all countries"""
        return self._db.query_view(view_name,options )


    def delete(self, _id, _rev):
        self._db.db.resource.delete_json(_id,rev= _rev)

    def order_by(self, field_name):
        """News.filter( slug = "", id="" , date__gte=""  ).exclude( id__in="2,4,5"  ).order_by("-id")"""
        self._order = field_name
        return self

    def limit(self, number):
        '''Limit results of a query

        :param number: Number of results to show
        '''
        self._limit = number
        return self

    def descending(self, condition):
        '''Reverse the output of a query

        :param condition: Boolean value. If 'True' the query will be reversed. Default is 'False'.
        '''
        descend = 'false'
        if condition:
            descend = 'true'

        self._descending = descend
        return self

    def skip(self, number):
        '''skip n number of documents

        :param number: number.
        '''
        self._skip = number
        return self

    def fire(self):
        res= self._query()
        self._set_all()
        return res