/*
 *  Licensed to the Apache Software Foundation (ASF) under one
 *  or more contributor license agreements.  See the NOTICE file
 *  distributed with this work for additional information
 *  regarding copyright ownership.  The ASF licenses this file
 *  to you 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.
 */
package org.codehaus.groovy.classgen.asm

import org.junit.jupiter.api.Test

import static org.codehaus.groovy.control.CompilerConfiguration.DEFAULT as config
import static org.junit.jupiter.api.Assumptions.assumeFalse

final class BinaryOperationsTest extends AbstractBytecodeTestCase {

    @Test
    void testIntPlus() {
        assumeFalse(config.indyEnabled)
        assert compile("""\
            int i = 1
            int j = 2
            int k = i + j
        """).hasSequence([
                "ILOAD",
                "ILOAD",
                "IADD"
        ])
    }

    @Test
    void testIntCompareLessThan() {
        assumeFalse(config.indyEnabled)
        assert compile("""\
            int i = 0
            if (i < 100) println "true"
        """).hasSequence([
                "ILOAD",
                "BIPUSH 100",
                "IF_ICMPGE"
        ])
    }

    // GROOVY-4741
    @Test
    void testCompareLessThanInClosure() {
        assumeFalse(config.indyEnabled)
        assert """
            int a = 0
            [].each {
                if (a < 0) {}
            }
            true
        """
    }

    @Test
    void testLongLeftShift() {
        assumeFalse(config.indyEnabled)
        assert compile("""\
            long a = 1
            long b = a << 32
        """).hasStrictSequence([
                "BIPUSH 32",
                "LSHL"
        ])
    }

    @Test
    void testIntConstants() {
        assumeFalse(config.indyEnabled)
        (0..5).each {
            assert compile("""\
                int a = $it
            """).hasStrictSequence([
                    "ICONST_$it",
            ])
        }
        assert compile("""\
            int a = -1
        """).hasStrictSequence([
                "ICONST_M1",
        ])
        [6,Byte.MIN_VALUE,Byte.MAX_VALUE].each {
            assert compile("""\
                    int a = $it
                """).hasStrictSequence([
                    "BIPUSH",
            ])
        }
        [Byte.MIN_VALUE-1,Byte.MAX_VALUE+1,Short.MIN_VALUE,Short.MAX_VALUE].each {
            assert compile("""\
                    int a = $it
                """).hasStrictSequence([
                    "SIPUSH",
            ])
        }
        [Short.MAX_VALUE+1,Integer.MAX_VALUE].each {
            assert compile("""\
                    int a = $it
                """).hasStrictSequence([
                    "LDC",
            ])
        }
    }

    @Test
    void testPrimitiveZeroConstants() {
        assumeFalse(config.indyEnabled)
        assert compile("""\
            long a = 0L
        """).hasStrictSequence([
                'LCONST_0',
        ])
        assert compile("""\
            float a = 0f
        """).hasStrictSequence([
                'FCONST_0',
        ])
        assert compile("""\
            double a = 0d
        """).hasStrictSequence([
                'DCONST_0',
        ])
    }

    @Test
    void testCharXor() {
        assumeFalse(config.indyEnabled)
        assert compile('''
            int i = ('a' as char) ^ ('b' as char)
        ''').hasStrictSequence ([
            'IXOR'
        ])
    }

    // GROOVY-10657
    @Test
    void testPutAtValue() {
        def sequence = compile '''
            class C {
                void putAt(String key, Object value) {
                }
            }

            def obj = new C()
            obj['key'] = 'xx'
        '''
    }

    @Test
    void testPrimitiveOrAssign() {
        ['byte','int','short','long'].each { type ->
            assertScript """
                $type[] array = new $type[1]
                array[0] = 16
                array[0] |= 2
                assert array[0] == 18 : "Failure for type $type"
            """
        }
    }

    @Test
    void testPrimitiveAndAssign() {
        ['byte','int','short','long'].each { type ->
            assertScript """
                $type[] array = new $type[1]
                array[0] = 18
                array[0] &= 2
                assert array[0] == 2 : "Failure for type $type"
            """
        }
    }
}
