# -*- coding: utf-8 -*-
"""
Promote and demote values of differing types in a minivect AST. This is run
before code generation. In LLVM types need to be equivalent for binary
operations.
"""
from __future__ import print_function, division, absolute_import

import sys
import copy

from . import minivisitor
from . import miniutils
from . import minitypes
from . import minierror

comparison_ops = set(['<', '>', '==', '!=', '>=', '<=',])

class TypePromoter(minivisitor.GenericTransform):
    """
    Promote and demote values of differing types.
    """

    def resolve_type(self, node):
        if node.type.is_array:
            node.type = node.type.dtype

    def promote(self, dst_type, node):
        return self.context.astbuilder.promote(dst_type, node)

    def visit_UnopNode(self, node):
        self.resolve_type(node)
        node.operand = self.promote(node.type, self.visit(node.operand))
        return node

    def visit_BinopNode(self, node):
        self.visitchildren(node)
        self.resolve_type(node)

        if node.operator in comparison_ops:
            dst_type = self.context.promote_types(node.lhs.type, node.rhs.type)
        else:
            dst_type = node.type

        if dst_type.is_pointer:
            return node

        return self.handle_binop(dst_type, node)

    def visit_VectorStoreNode(self, node):
        self.visitchildren(node)
        return node

    def handle_binop(self, dst_type, node):
        node.lhs = self.promote(dst_type, node.lhs)
        node.rhs = self.promote(dst_type, node.rhs)
        return node

    def visit_AssignmentExpr(self, node):
        self.visitchildren(node)
        self.resolve_type(node)
        return self.handle_binop(node.lhs.type, node)

    def visit_ResolvedVariable(self, node):
        return self.visit(node.element)
