# Copyright (c) 2004 Infrae. All rights reserved.
# See also LICENSE.txt

from pprint import pprint

###

class Terminal ( object ):

    def __init__ ( self, value, args=None ):
        self._term = value
        self._args = {}
        if args is not None:
            self._args.update(args)
        return

    def render_with ( self, renderer ):
        return self._term

    def args ( self ):
        return self._args

    def __str__ ( self ):
        r = SimpleRenderer()
        return self.render_with(r)
#

class Variable ( Terminal ):

    def render_with ( self, renderer ):
        return renderer.render_variable(self._term)
#

class Value ( Terminal ):

    def render_with ( self, renderer ):
        return renderer.render_value(self._term, self._args)
#

class Condition ( object ):

    clause = ''

    def __init__ ( self, lhs, op, rhs ):
        self._cond = (lhs, op, rhs)
        return

    def set_lhs ( self, lhs ):
        self._cond = (lhs, ) + self._cond[1:]
        return

    def set_rhs ( self, rhs ):
        self._cond = self._cond[:2] + (rhs,)
        return

    def render_with ( self, renderer ):
        lhs, op, rhs = self._cond
        if type(lhs) != Value:
            rleft = lhs.render_with(renderer)
        else:
            rleft = renderer.render_op_value(op, lhs)
        if type(rhs) != Value:
            rright = rhs.render_with(renderer)
        else:
            rright = renderer.render_op_value(op, rhs)
        a = And(Terminal(rleft), Terminal(rright))
        return a.render_with(renderer)

    def __str__ ( self ):
        r = SimpleRenderer()
        return self.render_with(r)
#

class Equal ( Condition ):

    clause = 'Equal'

    def __init__ ( self, lhs, rhs ):
        super(Equal, self).__init__(lhs, '=', rhs)
        return
#

class LessEqual ( Condition ):

    clause = 'LessEqual'

    def __init__ ( self, lhs, rhs ):
        super(LessEqual, self).__init__(lhs, '<=', rhs)
        return
#

class GreaterEqual ( Condition ):

    clause = 'GreaterEqual'

    def __init__ ( self, lhs, rhs ):
        super(GreaterEqual, self).__init__(lhs, '>=', rhs)
        return
#

class Greater ( Condition ):

    clause = 'Greater'

    def __init__ ( self, lhs, rhs ):
        super(Greater, self).__init__(lhs, '>', rhs)
        return
#

class Less ( Condition ):

    clause = 'Less'

    def __init__ ( self, lhs, rhs ):
        super(Less, self).__init__(lhs, '<', rhs)
        return
#

class NotEqual ( Condition ):

    clause = 'NotEqual'

    def __init__ ( self, lhs, rhs ):
        super(NotEqual, self).__init__(lhs, '<>', rhs)
        return
#

class Like ( Condition ):

    clause = 'Like'

    def __init__ ( self, lhs, rhs ):
        super(Like, self).__init__(lhs, 'LIKE', rhs)
        return
#

class ILike ( Condition ):

    clause = 'ILike'

    def __init__ ( self, lhs, rhs ):
        super(ILike, self).__init__(lhs, 'ILIKE', rhs)
        return
#

class Between ( object ):

    clause = 'BETWEEN'

    def __init__ ( self, lhs, rhs1, rhs2 ):
        self._cond = (lhs, rhs1, rhs2)
        return

    def render_with ( self, renderer ):
        lhs, rhs1, rhs2 = self._cond
        rop = " %s " % self.clause
        v = Terminal('value %s' % self.clause)
        v = Terminal(renderer.render_op_value(self.clause, rhs1))
        rright = And(v, rhs2)
        return And(lhs, rright).render_with(renderer)

    def __str__ ( self ):
        r = SimpleRenderer()
        return self.render_with(r)
#

class TermList ( object ):

    clause = ''

    def __init__ ( self, *args ):
        self._subterms = list(args)
        return

    def append ( self, term ):
        self._subterms.append(term)
        return

    def prepend ( self, term ):
        self._subterms.insert(0, term)
        return

    def iter_terms ( self ):
        return iter(self._subterms)

    def render_term_list ( self, renderer ):
        res = [ t.render_with(renderer) for t in self._subterms ]
        return res

    def render_with ( self, renderer ):
        tl = self.render_term_list(renderer)
        s = " %s " % self.clause
        return s.join(tl)

    def __str__ ( self ):
        r = SimpleRenderer()
        return self.render_with(r)
#

class In ( TermList ):

    clause = 'IN'

    def __init__ ( self, lhs, *args ):
        super(In, self).__init__(*args)
        if type(lhs) != Variable:
            raise TypeError, 'lhs must be a variable!'
        self._lhs = lhs
        return

    def render_with ( self, renderer ):
        lhs = self._lhs
        br = Bracketed(*self._subterms)
        rlhs = lhs.render_with(renderer)
        rbr = br.render_with(renderer, delimiter=',')
        return rlhs + ' AND (value IN ' + rbr + ')'

    def __str__ ( self ):
        r = SimpleRenderer()
        return self.render_with(r)
#

class And ( TermList ):
    clause = 'AND'
#

class Or ( TermList ):
    clause = 'OR'
#

class Intersect ( TermList ):
    clause = 'INTERSECT'
#

class Union ( TermList ):
    clause = 'UNION'
#

class Bracketed ( TermList ):

    def render_with ( self, renderer, delimiter=' ' ):
        tl = self.render_term_list(renderer)
        return '(' + delimiter.join(tl) + ')'
#
###

class SimpleRenderer ( object ):

    def render_variable ( self, value ):
        return str(value)

    def render_value ( self, value ):
        return "'" + str(value) + "'"

    def render_op_value ( self, op, value ):
        v = value.render_with(self)
        ret = 'value %s %s' % (op, v)
        br = Terminal(ret)
        return br.render_with(self)
#

class SQLRenderer ( SimpleRenderer ):

    def render_value ( self, value, args ):
        v = str(value)
        try:
            f = float(v)
        except ValueError:
            v = "'" + v + "'"
            pass
        if args.has_key('cast'):
            dt = args['cast']
            v = '%s::%s' % (v, dt)
        return v

    def render_op_value ( self, op, value ):
        v = value.render_with(self)
        val = 'value'
##         if value.args().has_key('cast'):
##             dt = value.args()['cast']
##             val = 'value::%s' % dt
        if value.args().has_key('function'):
            fnc = value.args()['function']
            ret = '%s(%s) %s %s' % (fnc, val, op, v)
        else:
            ret = '%s %s %s' % (val, op, v)
        br = Terminal(ret)
        return br.render_with(self)

    def render_variable ( self, value ):
        v = "'" + value + "'"
        t = 'qname = %s' % v
        br = Terminal(t)
        return br.render_with(self)
#
