#     Copyright 2013, Kay Hayen, mailto:kay.hayen@gmail.com
#
#     Part of "Nuitka", an optimizing Python compiler that is compatible and
#     integrates with CPython, but also works on its own.
#
#     Licensed under the Apache License, Version 2.0 (the "License");
#     you may not use this file except in compliance with the License.
#     You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS,
#     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#     See the License for the specific language governing permissions and
#     limitations under the License.
#
""" Nodes for comparisons.

"""

from .NodeBases import ExpressionChildrenHavingBase

from nuitka import PythonOperators

# Delayed import into multiple branches is not an issue, pylint: disable=W0404

class ExpressionComparison( ExpressionChildrenHavingBase ):
    kind = "EXPRESSION_COMPARISON"

    named_children = ( "left", "right" )

    def __init__( self, left, right, comparator, source_ref ):
        assert left.isExpression()
        assert right.isExpression()
        assert type( comparator ) is str, comparator

        assert comparator in PythonOperators.all_comparison_functions

        ExpressionChildrenHavingBase.__init__(
            self,
            values     = {
                "left"  : left,
                "right" : right
            },
            source_ref = source_ref
        )

        self.comparator = comparator

        if comparator in ( "Is", "IsNot" ):
            assert self.__class__ is not ExpressionComparison

    def getOperands( self ):
        return (
            self.getLeft(),
            self.getRight()
        )

    getLeft = ExpressionChildrenHavingBase.childGetter( "left" )
    getRight = ExpressionChildrenHavingBase.childGetter( "right" )

    def getComparator( self ):
        return self.comparator

    def getDetails( self ):
        return { "comparator" : self.comparator }

    def getSimulator( self ):
        return PythonOperators.all_comparison_functions[ self.comparator ]

    def computeExpression( self, constraint_collection ):
        # Left and right is all we need, pylint: disable=W0613

        left, right = self.getOperands()

        if left.isCompileTimeConstant() and right.isCompileTimeConstant():
            left_value = left.getCompileTimeConstant()
            right_value = right.getCompileTimeConstant()

            from .NodeMakingHelpers import getComputationResult

            return getComputationResult(
                node        = self,
                computation = lambda : self.getSimulator()(
                    left_value,
                    right_value
                ),
                description = "Comparison with constant arguments"
            )

        return self, None, None

    def computeExpressionOperationNot( self, not_node, constraint_collection ):
        if self.comparator in PythonOperators.comparison_inversions:
            left, right = self.getOperands()

            from .NodeMakingHelpers import makeComparisonNode

            result = makeComparisonNode(
                left       = left,
                right      = right,
                comparator = PythonOperators.comparison_inversions[ self.comparator ],
                source_ref = self.source_ref
            )

            return result, "new_expression", "Replaced negated comparison with inverse comparision."

        return not_node, None, None

    def mayProvideReference( self ):
        # Dedicated code returns "True" or "False" only, which requires no reference,
        # except for rich comparisons, which do.
        return self.comparator in PythonOperators.rich_comparison_functions


class ExpressionComparisonIsIsNotBase( ExpressionComparison ):
    def __init__( self, left, right, comparator, source_ref ):
        ExpressionComparison.__init__(
            self,
            left       = left,
            right      = right,
            comparator = comparator,
            source_ref = source_ref
        )

        assert comparator in ( "Is", "IsNot" )

        self.match_value = comparator == "Is"

    def isExpressionComparison( self ):
        # Virtual method, pylint: disable=R0201
        return True

    def computeExpression( self, constraint_collection ):
        left, right = self.getOperands()

        if constraint_collection.mustAlias( left, right ):
            from .NodeMakingHelpers import (
                makeConstantReplacementNode,
                wrapExpressionWithSideEffects
            )

            result = makeConstantReplacementNode(
                constant = self.match_value,
                node     = self
            )

            if left.mayHaveSideEffects() or right.mayHaveSideEffects():
                result = wrapExpressionWithSideEffects(
                    side_effects = self.extractSideEffects(),
                    old_node     = self,
                    new_node     = result
                )

            return result, "new_constant", """\
Determined values to alias and therefore result of %s comparison.""" % self.comparator

        if constraint_collection.mustNotAlias( left, right ):
            from .NodeMakingHelpers import makeConstantReplacementNode, wrapExpressionWithSideEffects

            result = makeConstantReplacementNode(
                constant = not self.match_value,
                node     = self
            )

            if left.mayHaveSideEffects() or right.mayHaveSideEffects():
                result = wrapExpressionWithSideEffects(
                    side_effects = self.extractSideEffects(),
                    old_node     = self,
                    new_node     = result
                )

            return result, "new_constant", """\
Determined values to not alias and therefore result of %s comparison.""" % self.comparator

        return ExpressionComparison.computeExpression( self, constraint_collection )

    def extractSideEffects( self ):
        left, right = self.getOperands()

        return left.extractSideEffects() + right.extractSideEffects()


class ExpressionComparisonIs( ExpressionComparisonIsIsNotBase ):
    kind = "EXPRESSION_COMPARISON_IS"

    def __init__( self, left, right, source_ref ):
        ExpressionComparisonIsIsNotBase.__init__(
            self,
            left       = left,
            right      = right,
            comparator = "Is",
            source_ref = source_ref
    )


class ExpressionComparisonIsNOT( ExpressionComparisonIsIsNotBase ):
    kind = "EXPRESSION_COMPARISON_IS_NOT"

    def __init__( self, left, right, source_ref ):
        ExpressionComparisonIsIsNotBase.__init__(
            self,
            left       = left,
            right      = right,
            comparator = "IsNot",
            source_ref = source_ref
    )
